Есть ли способ получить полный обзор всей памяти, выделенной процессом? - PullRequest
0 голосов
/ 28 мая 2011

Сначала немного предыстории. У меня есть базовое представление, которое расширяет три основных вида моего приложения. Дочерние виды являются пустыми, аналоговыми и цифровыми. Я помещаю эти дочерние виды в вид сетки (2x3), а вид сетки в скользящий рисунок. Этот выдвижной ящик - суть моего приложения. Это абсолютно необходимо. Выдвижной ящик должен быть в каждом действии, поэтому при изменении действия я просто сохраняю состояние в приложении и извлекаю его при загрузке нового действия.

Когда приложение открывается, gridview создает шесть пустых представлений и добавляет их в свой адаптер. Теперь, когда все представления пусты, приложение работает безупречно. Я могу побродить по мероприятиям и выполнить все остальные функции, которые есть в приложении. И пока я занимаюсь одним и тем же делом, я могу создавать аналоговые и цифровые представления для своего сердца. Они перемещают, удаляют и выполняют все свои функции должным образом. Но как только я перехожу к другому виду деятельности И , у меня появляется даже одно аналоговое или цифровое представление в виде сетки, приложение вылетает через OutOfMemoryError: bitmap size exceeds VM Budget.

Как аналоговое, так и цифровое представление создают для себя два растровых изображения. Один - это фон для представления, а другой - уникальный вид представления, который меняется так редко, что он лучше подходит в качестве растрового изображения. Оба растровых изображения довольно маленькие (221x221 px в моем тестовом Evo). Это заставило меня думать, что я не перерабатывал их должным образом при изменениях активности. Поэтому я вернулся и проверил, что все очищается, и создал метод, который полностью уничтожил каждое представление. Каждая переменная имеет значение null, и все растровые изображения перезаписываются, когда когда-либо активность приостанавливается. (Примечание: используя регистратор, я убедился, что onPause действительно вызывается так же, как и мой метод destroy.)

Сейчас, спустя несколько дней, я все еще не могу понять, почему выдается эта ошибка памяти. Я провел бесчисленное количество времени, просматривая DDMS и Memory Tracker, и это, пожалуй, самая бесполезная вещь за всю историю. Я полностью сыт по горло DDMS, я не могу заставить глупость сказать мне что-нибудь полезное.

Так что теперь вопрос. Есть ли способ (метод / системный вызов или что-то), что я могу получить полный список распределений процесса (мои приложения) и распечатать / отобразить / сохранить в файл / etc ... it?

Редактировать 1 : это ответ Фальмарри. Возможно, я публикую немного, и я прошу прощения за это. Если вы хотите взглянуть на что-то более конкретное, я с радостью помогу, у вас нет причин для того, чтобы вырывать мой код.

Клип из BaseView:

public abstract class GaugeBase extends View implements BroadcastListener {
    protected static final String TAG = "GaugeBase";
    // =======================================
    // --- Declarations 
    /** Animation dynamics. */
    protected float mTarget = 0, position = 0, velocity = 0.0f, acceleration = 0.0f;
    protected long lastMoveTime = -1L;

    /** Background objects. */
    protected Bitmap mBackground;
    protected Bitmap mFaceTexture;
    protected float borderSize = 0.02f;

    /** Face objects. */
    protected Paint mRimShadowPaint, mTitlePaint, mFacePaint, mRimPaint, mRimBorderPaint;
    protected Path mTitlePath;

    /** Bounding rects. */
    protected static RectF mRimRect, mFaceRect;

    /** Text tools. */
    protected static Typeface mTypeface;

    /** The preferred size of the widget. */
    private static final int mPreferredSize = 300;

    /** The Broadcaster the gauge is registered to. */
    protected SensorBroadcaster mCaster;

    /** Is the view instantiated? */
    private boolean isDestroyed = true;
    // ---
    // =======================================

    public GaugeBase(Context context) {
        super(context);
        mCaster = ((AppionApplication)getContext().getApplicationContext())
            .getSensorBroadcaster(AppionApplication.TEST_SENSOR);
        lastMoveTime = System.currentTimeMillis();
        setTarget(mCaster.getReading());
    }

    @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); }
    @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); }
    @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { regenerate(); } 
    @Override public void onBroadcastReceived() { setTarget(mCaster.getReading()); }

    @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int chosenWidth = chooseDimension(widthMode, widthSize);
        int chosenHeight = chooseDimension(heightMode, heightSize);
        int chosenDimension = Math.min(chosenWidth, chosenHeight);
        setMeasuredDimension(chosenDimension, chosenDimension);
    }
    @Override protected void onDraw(Canvas canvas) {
        if (isDestroyed) return;
        if (mBackground == null) regenerate(); 
        canvas.drawBitmap(mBackground, 0, 0, null);
        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        canvas.scale((float)getWidth(), (float)getWidth());
        drawForeground(canvas); canvas.restore(); animate();
    }

    public HashMap<String, Object> onSavePersistentState() {
        HashMap<String, Object> mState = new HashMap<String, Object>();
        mState.put("sensor_broadcaster", mCaster.getBroadcasterName());
        mState.put("type", this.getClass().getSimpleName());
        return mState;
    }

    public void onRestorePersistentState(HashMap<String, Object> state) {
        mCaster = ((AppionApplication)getContext().getApplicationContext())
            .getSensorBroadcaster((String)state.get("sensor_broadcaster"));
    }

    private final void setTarget(float target) { mTarget = target; animate(); }

    private static final int chooseDimension(int mode, int size) {
        if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) return size;
        else return mPreferredSize;
    }

    private final void animate() {
        if (! (Math.abs(position - mTarget) > 0.01f)) return;
        if (lastMoveTime != -1L) {
            long currentTime = System.currentTimeMillis();
            float delta = (currentTime - lastMoveTime) / 1000.0f;
            float direction = Math.signum(velocity);
            if (Math.abs(velocity) < 90.0f) acceleration = 10.0f * (mTarget - position);
            else acceleration = 0.0f;
            position += velocity * delta;
            velocity += acceleration * delta;
            if ((mTarget - position) * direction < 0.01f * direction) {
                position = mTarget;
                velocity = 0.0f;
                acceleration = 0.0f;
                lastMoveTime = -1L;
            } else lastMoveTime = System.currentTimeMillis();               
            invalidate();
        } else {
            lastMoveTime = System.currentTimeMillis();
            animate();
        }
    }

    public void preInit() {
        mTypeface = Typeface.createFromAsset(getContext().getAssets(),
                "fonts/SFDigitalReadout-Heavy.ttf");
        mFaceTexture = BitmapFactory.decodeResource(getContext().getResources(),
                R.drawable.gauge_face);
        BitmapShader shader = new BitmapShader(mFaceTexture, 
                Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);

        Matrix matrix = new Matrix();
        mRimRect = new RectF(0.05f, 0.05f, 0.95f, 0.95f);
        mFaceRect = new RectF(mRimRect.left + borderSize, mRimRect.top + borderSize,
                             mRimRect.right - borderSize, mRimRect.bottom - borderSize);


        mFacePaint = new Paint();
        mFacePaint.setFilterBitmap(true);
        matrix.setScale(1.0f / mFaceTexture.getWidth(), 1.0f / mFaceTexture.getHeight());
        shader.setLocalMatrix(matrix);
        mFacePaint.setStyle(Paint.Style.FILL);
        mFacePaint.setShader(shader);

        mRimShadowPaint = new Paint();
        mRimShadowPaint.setShader(new RadialGradient(0.5f, 0.5f, mFaceRect.width() / 2.0f,
                new int[] { 0x00000000, 0x00000500, 0x50000500 },
                new float[] { 0.96f, 0.96f, 0.99f },
                Shader.TileMode.MIRROR));
        mRimShadowPaint.setStyle(Paint.Style.FILL);

        mRimPaint = new Paint();
        mRimPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mRimPaint.setShader(new LinearGradient(0.4f, 0.6f, 0.6f, 1.0f,
                Color.rgb(0xff0, 0xf5, 0xf0), Color.rgb(0x30, 0x31, 0x30),
                Shader.TileMode.CLAMP));

        mRimBorderPaint = new Paint();
        mRimBorderPaint.setAntiAlias(true);
        mRimBorderPaint.setStyle(Paint.Style.STROKE);
        mRimBorderPaint.setColor(Color.argb(0x4f, 0x33, 0x36, 0x33));
        mRimBorderPaint.setStrokeWidth(0.005f);

        mTitlePaint = new Paint();
        mTitlePaint.setColor(0xff000000);
        mTitlePaint.setAntiAlias(true);
        mTitlePaint.setTypeface(mTypeface);
        mTitlePaint.setTextAlign(Paint.Align.CENTER);
        mTitlePaint.setTextSize(0.2f);
        mTitlePaint.setTextScaleX(0.8f);        

        // Now we prepare the gauge
        init();
        isDestroyed = false;
    }

    /** Update the gauge independent static buffer cache for the background. */
    private void regenerate() {
        if (isDestroyed) return;
        if(mBackground != null) { mBackground.recycle(); mBackground = null; } 
        // Our new drawing area
        mBackground = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas backCanvas = new Canvas(mBackground);
        float scale = (float)getWidth();
        backCanvas.scale(scale, scale);
        drawRim(backCanvas);
        drawFace(backCanvas);
        drawTitle(backCanvas);
        if (!(this instanceof EmptySpace)) { mCaster.getGroup().draw(backCanvas); }
        regenerateBackground(backCanvas);
    }

    /** Prepare the view to be cleaned up. This is called to prevent memory leaks. */
    public void destroy() { 
        isDestroyed = true;
        if (mFaceTexture != null) { mFaceTexture.recycle(); mBackground = null; }
        if (mBackground != null) { mBackground.recycle(); mBackground = null; }
        mRimShadowPaint = null;
        mRimShadowPaint = null;
        mFacePaint = null;
        mRimPaint = null;
        mRimBorderPaint = null;
        mTitlePath = null;
        mRimRect = null; mFaceRect = null;
        mTypeface = null;
        destroyDrawingCache();
    }

    /**
     * Create a bitmap of the gauge. The bitmap is to scale. 
     * @return The bitmap of the gauge.
     */
    int tobitmap = 0;
    public Bitmap toBitmap() {
        Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas();
        canvas.setBitmap(b);
        draw(canvas);
        return b;
    }

    /** Update the gauge dependent static buffer cache for the background. */
    protected abstract void regenerateBackground(Canvas canvas);
    /** Initializes all of the objects the gauge widget will need before use. */
    protected abstract void init();
    /** This is called when drawing the background. Draws the bordered edge of the gauge. */
    protected abstract void drawRim(Canvas canvas);
    /** This is called when drawing the background. Draws the face of the gauge. */
    protected abstract void drawFace(Canvas canvas);
    /** This is called when drawing the background. Draws the title to the gauge. */
    protected abstract void drawTitle(Canvas canvas);
    /**
     *  This is called when drawing the foreground. The foreground includes items like the 
     *  scale of an analog gauge, or the text of a digital gauge. Also any other necessary
     *  items that need drawing go here. Note: drawForeground is called quickly, repeatedly, 
     *  make it run fast and clean.
     */
    protected abstract void drawForeground(Canvas canvas);
}

Это из цифрового вида: (потому что он меньше и все еще вызывает ошибку)

public class DigitalGauge extends GaugeBase {
    // ================================
    // --- Drawing tools
    private RectF lcdRect;
    private Paint lcdPaint, detailPaint;
    private Path facePath, borderPath;
    // ---
    // ================================
    public DigitalGauge(Context context) {
        super(context);
    }

    @Override protected void regenerateBackground(Canvas canvas) { }
    @Override protected void init() {
        lcdPaint = new Paint();
        lcdPaint.setColor(0xff000000);
        lcdPaint.setAntiAlias(true);
        lcdPaint.setStrokeWidth(0.005f);
        lcdPaint.setTextSize(0.4f);
        lcdPaint.setTypeface(mTypeface);
        lcdPaint.setTextAlign(Paint.Align.CENTER);

        detailPaint = new Paint();
        detailPaint.setColor(0xff000000);
        detailPaint.setTextSize(0.2f);
        detailPaint.setStrokeWidth(0.005f);
        detailPaint.setAntiAlias(true);
        detailPaint.setTypeface(mTypeface);
        detailPaint.setTextScaleX(0.8f);
        detailPaint.setTextAlign(Paint.Align.CENTER);

        facePath = new Path();
        facePath.moveTo(0.12f, 0.0f);
        facePath.lineTo(0.88f, 0.0f);
        facePath.arcTo(new RectF(), 0, 90);
        // TODO Make the trapazoidal look of the digital gauge


        lcdRect = new RectF(mFaceRect.left + borderSize, mFaceRect.top + borderSize,
                mFaceRect.right - borderSize, mFaceRect.top - borderSize - lcdPaint.getTextSize());
    }

    @Override protected void drawRim(Canvas canvas) {
        canvas.drawRect(mRimRect, mRimPaint);
        canvas.drawRect(mRimRect, mRimBorderPaint);
    }

    @Override protected void drawFace(Canvas canvas) {
        canvas.drawRect(mFaceRect, mFacePaint);
        canvas.drawRect(mFaceRect, mRimBorderPaint);
    }

    @Override protected void drawTitle(Canvas canvas) {
        canvas.drawText(mCaster.getBroadcasterSerial(), mFaceRect.left - 0.1f,
                mFaceRect.top + 0.1f, mTitlePaint);
    }
    @Override protected void drawForeground(Canvas canvas) {
        String display = "000000" + String.valueOf(Math.ceil(position));
        String read = display.substring(display.length()-8, display.length() - 2);
        canvas.drawText(read, 0.5f, lcdRect.top + lcdPaint.getTextSize() / 2, lcdPaint);
        /**canvas.drawText(mContext.getResources().getStringArray(R.array.pressureTypes)[measurement],
            0.5f, lcdRect.top + lcdPaint.getTextSize() , detailPaint);*/
    }
}

Что касается состояния, проходящего через приложение, я поместил тип представления и строковое имя кастера, который представление представляет в хэш-карту. Я передаю эту хэш-карту в gridview, который затем помещает все шесть карт в массив, который будет представлять местоположения представлений в gridview. Этот массив затем сохраняется в приложении и извлекается по необходимости.

Это вид сетки. Чем больше я думаю об этом классе, так это о том, где могут возникнуть проблемы.

public class Workbench extends GridView {
    /** Our debugging tag */
    private static final String TAG = "Workbench";
    /** Name of the Workbench. */
    private String mId = "-1";
    /** The title of the Workbench. */
    private String mTitle = "Workbench";
    /** The list of Widgets that will be handled by the bench */
    private GaugeBase[] mContent = new GaugeBase[6];
    /** The current selection from the bench */
    private int mSelection = -1;

    /** When a GaugeBase is moves we want to remove from the adapter. Now we won't lose it.*/
    private GaugeBase mHeldGaugeBase = null;
    private Bitmap mHold = null;
    private boolean mIsHolding = false;
    private float x = -1000f, y = -1000f; // Where the held bitmap should be
    private Bitmap trash;
    private RectF trashBox;

    // The touch listener we will use if we need to move a widget around
    private OnTouchListener mWidgetExchanger = new OnTouchListener() {
        @Override public boolean onTouch(View v, MotionEvent e) {
            int w = getWidth(); int h = getHeight(); 
            float xx = e.getX(); float yy = e.getY();
            switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN: // Fall through
            case MotionEvent.ACTION_MOVE: 
                if (mIsHolding) {
                    x = e.getX() - mHold.getWidth()/2; y = e.getY() - mHold.getHeight()/2;
                    postInvalidate(); break;
                }
            case MotionEvent.ACTION_UP: 
                if (mIsHolding) {
                    if (trashBox.contains(xx, yy)) removeGaugeBase(mSelection);
                    else {
                        if ((xx < w / 2) && (yy < h /3)) makeSwitch(0);
                        else if ((xx > w / 2) && (yy < h /3)) makeSwitch(1);
                        else if ((xx < w / 2) && (yy > h /3) && (yy < h * .666)) makeSwitch(2);
                        else if ((xx > w / 2) && (yy > h /3) && (yy < h * .666)) makeSwitch(3);
                        else if ((xx < w / 2) && (yy > h *.666)) makeSwitch(4);
                        else if ((xx > w / 2) && (yy > h *.666)) makeSwitch(5);
                    }
                    mSelection = -1;
                    //mHeldGaugeBase.destroy(); mHeldGaugeBase = null;
                    mHold.recycle(); mHold = null;
                    trash.recycle(); trash = null;
                    mIsHolding = false;
                    setOnTouchListener(null);
                    x = -1000f; y = -1000f;
                    ((AppionApplication)getContext().getApplicationContext()).vibrate(200); update();
                }
                break;
            }
            return true;
        }
    };

    public Workbench(Context context) { this(context, null); }
    public Workbench(Context context, AttributeSet attrs) { this(context, attrs, 0); }
    public Workbench(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        for (int i = 0; i < mContent.length; i++) {
            mContent[i] = new EmptySpace(getContext());
        }
        setAdapter(new BenchAdapter());
        this.setOnItemClickListener(new OnItemClickListener() {
            @Override public void onItemClick(AdapterView<?> arg0, View view, final int pos, long arg3) {
                if (mContent[pos] instanceof EmptySpace) {
                    CharSequence[] items = {"Analog", "Digital"};
                    AlertDialog.Builder adb = new AlertDialog.Builder(getContext());
                        adb.setTitle("Add a widget?")
                            .setItems(items, new DialogInterface.OnClickListener () {
                                @Override public void onClick(DialogInterface arg0, int position) {
                                    mContent[pos].destroy();
                                    mContent[pos] = null;
                                    SensorBroadcaster s = ((AppionApplication)getContext().getApplicationContext()).
                                        getSensorBroadcaster(AppionApplication.TEST_SENSOR);
                                    switch (position) {
                                    case 0: // Add an Analog GaugeBase to the Workbench
                                        mContent[pos] = new AnalogGauge(getContext());
                                        // TODO: Option to link to a manager
                                        break;
                                    case 1: // Add a digital GaugeBase to the Workbench
                                        mContent[pos] = new DigitalGauge(getContext());
                                        // TODO: Option to link to a manager
                                        break;
                                    } mContent[pos].preInit();
                                    update();
                                }
                            });
                        adb.show();
                } //else new GaugeBaseDialog(getContext(), Workbench.this, (GaugeBase)view, pos).show();
            }
        });
        setOnItemLongClickListener(new OnItemLongClickListener() {
            @Override public boolean onItemLongClick(AdapterView<?> arg0, View arg1,
                    int pos, long arg3) {
                mSelection = pos;
                mHold = mContent[pos].toBitmap();
                mHeldGaugeBase = mContent[pos];
                mHeldGaugeBase.destroy();
                trash = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getContext().getResources(),
                        R.drawable.trash), getWidth() / 10, getHeight() / 10, true);
                trashBox = new RectF(getWidth() / 2 - trash.getWidth()/2, getHeight() - trash.getHeight(),
                        getWidth() /2 + trash.getWidth() /2, getHeight());
                mContent[pos] = new EmptySpace(getContext());
                update();
                mIsHolding = true;
                setOnTouchListener(mWidgetExchanger);
                ((AppionApplication)getContext().getApplicationContext()).vibrate(300);
                return false;
            }
        });
    }

    /**
     * Perform a switch in within the bench. Exchange on slot with another.
     * @param slot The slot of the widgets list that we are switching to.
     */
    public void makeSwitch(int slot) {
        if (mSelection == -1) return;
        Log.i(TAG, "Performing a Widget switch");
        mContent[mSelection].destroy();
        mContent[mSelection] = mContent[slot];
        mContent[slot] = mHeldGaugeBase;
        mContent[slot].preInit();
        mContent[slot].invalidate();
        Log.d(TAG, " mSelection = " + mContent[mSelection] + " slot = " +mContent[slot]);
        update();                                                               
    }

Ответы [ 3 ]

1 голос
/ 28 мая 2011

Я предлагаю вам взглянуть на это видео .Это помогло мне во многих отношениях, и я также испытываю эту проблему с размерами растровых изображений и бюджетами памяти vm.

И по своему опыту: я разработал дурную привычку вызывать System.gc () везде после .recycle().Я знаю, что это нехорошо, но это помогло мне предотвратить принудительное закрытие после нескольких часов отладки, почему мои растровые изображения не были переработаны должным образом.

1 голос
/ 28 мая 2011

Это своего рода ответ на @mah, но он слишком длинный для комментария.

он всегда выполняется кратно размеру системной страницы

Это не обязательно правда.Особенно с приложением для Android.Существует много разных распределителей памяти с различной семантикой.Однако, если вы говорите о JAVA , в отличие от NDK (C ++), прозрачность еще меньше.Виртуальная машина Java (или, скорее, dalvik) почти наверняка излишне выделит память при запуске машины, а затем, когда приложение запрашивает небольшие биты памяти, оно отдаст ее вам из этого пула.

Если она запуститсяНедостаточно памяти из своего пула, он выделит другой блок из ОС и добавит его в свой пул и вернет оттуда блок.

Однако, если вы запрашиваете БОЛЬШИЕ блоки памяти, такие как вы хотите для растрового изображениянапример, JVM (или машина dalvik), скорее всего, будет использовать системный метод mmap, который отображает часть памяти.В зависимости от конкретных обстоятельств он может создать личную анонимную карту, а затем предоставить вам доступ к ее разделам.Или, он может отобразить файл в память, которая в основном представляет собой представление о том, что находится на диске.Вероятно, именно так dalvic обрабатывает большие выделения растровых изображений.

и создал метод, который полностью уничтожил каждое представление

Во-первых, вы действительно не имеете прямого контроля над этим в Java.Установка объекта на ноль не удаляет его.Даже если вы предполагаете, что у вас есть только одна ссылка на этот объект, вам придется подождать, пока сборщик мусора не очистит данные объекта.


Невозможно сказать вам, в чем заключается ваша проблема, илидействительно даже намек.Все, что я могу сказать, это то, что вы, вероятно, выделяете место для своих растровых изображений в большем количестве мест, чем вы.Или вы храните ссылки на них где-то, что они не очищаются.

Я просто сохраняю состояние в приложении и извлекаю его при загрузке нового действия.

Это довольно расплывчато, но я бы сначала взглянул на то, что вы делаете в этом.Если, скажем, вы передаете растровые изображения как анализируемые объекты, вы, вероятно, выделяете в 3-4 раза больше места, чем необходимо.Отправка больших пользовательских объектов и растровых изображений через анализируемые интерфейсы ОЧЕНЬ дорогая.

Я бы предложил одну из нескольких вещей.Либо вы можете лениво загружать свои растровые изображения.То есть нигде не храните их.Поднимайте их только тогда, когда они вам нужны.Это МОЖЕТ быть решением, потому что это может быть случай, когда вы думаете, что перехитрите компилятор.Но я гарантирую, что компилятор умнее вас при эффективном использовании памяти.

-

Или вы можете попробовать противоположное и ТОЛЬКО загрузить битовую карту при загрузке приложения и убедиться, что каждыйКогда вы отображаете его, вы не создаете новый растровый рисунок или что-то еще.Таким образом, он запоминается только один раз, и если он действительно слишком велик, вы рано падаете в известном месте.

-

Наконец, если вы действительно не можете найтирешение, и вы действительно думаете, что ваш Java-процесс действительно не хватает памяти, вы могли бы переписать часть, которая обрабатывает растровые представления в NDK.Таким образом, они не будут собираться и создаваться заново без вашего явного участия.

-

Так что теперь вопрос.Есть ли способ (метод / системный вызов или что-то), что я могу получить полный список распределений процесса (мои приложения) и распечатать / отобразить / сохранить в файл / etc ... it?

Я уверен, что есть.Но, и это большое, но это не будет ЛЮБЫМ способом легко.Вероятно, вам придется заменить glibc вашей системы (то есть, функции malloc) на версии, которые отслеживают, кто запрашивает память.Однако даже это будет скрыто виртуальной машиной Java.


Короче говоря, опубликуйте часть своего кода, особенно те части, где вы манипулируете и сохраняете свои представления и растровые изображения.

Обновление :

Просто взглянув на ваш код, я бы проверил, как часто вызывается regenerate(), в частности из-за этого:

mBackground = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas backCanvas = new Canvas(mBackground);

Я не эксперт по управлению памятью растровых изображений Java, но я думаю, что это будет дорого.

0 голосов
/ 28 мая 2011

Вы можете посмотреть / proc / self / maps (внутри процесса) или / proc / [идентификатор процесса] / maps, но вряд ли скажет вам, чего вы хотите, и никакой системный вызов не даст.Когда вы выделяете память в своем процессе, она всегда выполняется в кратных размерах системной страницы (4 КБ, возможно, больше), даже если вы выделяете только 1 байт, но тогда она становится внутренне управляемой.Распределения в будущем поступают из этого блока до того, как из системы будет получено больше памяти.

...