5

I'm trying to pinpoint this memory leak.

I have two SurfaceViews, A and B. I start A, then navigate to B, then press the back button to go back to A, and then I navigate to B again.

I can see my allocated memory rise each time I do this, and eventually I'll get an out of memory error.

Here is how I navigate to B, from inside the SurfaceView connected to A

        Context context =  this.getContext();
        Intent i =new Intent(context, StartCareer.class);
        i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        context.startActivity(i);

In both views, I have a lot of Bitmaps drawing. In B, I can't find any references to A, and the only reference outside the context that I can think of is a reference to a Global class that I have. I also have some analytics stuff going on in the background. It could be a million different things, I'd imagine

I have the DDMS view on Eclipse up, but I'm not sure what I'm looking at, or how to find the exact object that keeps getting repeated.

I'd accept either a crash-course/tutorial on the DDMS Allocation Tracker, or someone to point out what I'm doing wrong.


Additional information:

I have some bitmaps being drawn on a SurfaceView. Examples of such from B are:

////At class level
Bitmap rightB,leftB;
////In the constructor
rightB = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.right), 100,75, true);
////In doDraw
canvas.drawBitmap(rightB, rbX, rbY, null);

And my onDestroys

@Override
public void surfaceDestroyed(SurfaceHolder holder) {


    if (mThread.isAlive()){
        mThread.setMenuRunning(false);
    }
}

So I've run MAT and found one leak, at least. My Thread Keeps getting recreated. Here's what's doing it.

@Override
public void surfaceCreated(SurfaceHolder holder) {
    loading=false;
    if (!mThread.isAlive()){
        mThread = new ViewThread(this);
        mThread.setMenuRunning(true);
        mThread.start();
    }
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {

    if (mThread.isAlive()){ 
        mThread.setMenuRunning(false);
    }
}

Assuming that these methods get called every time the view loses or gains focus, this seems obviously wrong. How can I re-organize this so that it's not?

  • Do you have bitmaps on these activities ? – Siddharth Jan 31 '13 at 07:33
  • In the `SurfaceViews` associated with them, yes. –  Jan 31 '13 at 07:35
  • 1
    Paste that code, and your onDestroys – Siddharth Jan 31 '13 at 07:35
  • What does this Global class do? Does it hold any references to any Views, Drawables, Handlers, or anything like that? What about from static variables? There are a number of commonly used objects that keep an entire Activity context in memory. – mkuech Jan 31 '13 at 07:42
  • @Siddharth done @mkuech The Global class holds various ints, strings, oh, and a `Resources`, which is set from the constructor of `A` –  Jan 31 '13 at 07:45
  • See, as soon as you said bitmaps you got a bunch of answers. – Siddharth Jan 31 '13 at 07:49
  • Well, I did mention "bitmaps" right under my first block of code –  Jan 31 '13 at 07:50
  • I updated my question with a known memory leak. –  Feb 03 '13 at 21:58
  • Now I narrowed down my memory leak, and it is its own question: http://stackoverflow.com/questions/14677913/how-do-i-fix-this-android-memory-leak-involving-threads –  Feb 04 '13 at 20:07

4 Answers4

4

Call this method in onDestroy() and onstop() of your app.

private void unbindDrawables(View view) {
     Log.d(TAG,"in unbindDrawables");
        if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
        }
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
        ((ViewGroup) view).removeAllViews();
        view.setBackgroundResource(0);
        Log.d(TAG,"removed views");
        //finish();
        }
 }
seenukarthi
  • 8,241
  • 10
  • 47
  • 68
Payal
  • 903
  • 1
  • 9
  • 28
3

A few hints:

  • Recycle bitmaps when done with an activity (onDestroy for example)
  • Use the application context rather than the activity itself as context whenever possible
Vincent Mimoun-Prat
  • 28,208
  • 16
  • 81
  • 124
1

Try to recycle() your bitmaps in onDestroy() of your activities.

Michał Z.
  • 4,119
  • 1
  • 22
  • 32
  • I'll try this, but off the top of my head, I don't know how to access members of the view I created –  Feb 03 '13 at 20:16
  • You call recycle() on Bitmap objects you created like: Bitmap rightB,leftB; it would be like rightB.recycle(); – Michał Z. Feb 03 '13 at 20:19
  • Well, I tried recycling all three of the bitmaps I draw in `B`, but it doesn't seem to help. The memory leak still occurs. The problem must be elsewhere –  Feb 03 '13 at 20:32
  • 1
    So, I implemented this, and whenever I exit out of my app, and come back in, it crashes since the bitmaps that I'm trying to draw are recycled. Maybe because the bitmap is static. –  Feb 09 '13 at 23:21
  • Static non-trivial members are known to leak context. I would begin the investigation there – Makibo Jul 03 '13 at 23:19
0

What you need to do is explained in detail here. For your specific issue, you need to do this

// resize to desired dimensions
    int height = b.getHeight();
    int width = b.getWidth();
    Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
       height: " + height);

    double y = Math.sqrt(IMAGE_MAX_SIZE
            / (((double) width) / height));
    double x = (y / height) * width;

    Bitmap scaledBitmap = Bitmap.createScaledBitmap(b, (int) x, 
       (int) y, true);
    b.recycle();
Community
  • 1
  • 1
Siddharth
  • 9,349
  • 16
  • 86
  • 148
  • I'm having memory leaks. I'm not allocating too much memory toward bitmaps –  Feb 03 '13 at 20:46
  • Regardless of how much memory you are allocating, cleanup is essential. And Bitmaps are known to cause OOM very quickly with relatively small amounts of memory. – Siddharth Feb 04 '13 at 02:36