2

I have a program that loads a bitmap from gallery (by dispatching intent for result) and then displays the image. Then when leaving the activity that displays the image, I call bm.recycle(). But that does not seem to be recycling the bitmap? I know this because of the problem described in the post here: android bitmap out-of-memory error after getting same image twice in a row.

This question is specifically as stated in the present title: Why is onDestroy not recycling the bitmap? (I only provide the link for some context not as a distraction)

Here is my onDestroy:

@Override
protected void onDestroy() {
    super.onDestroy();
    unbindDrawables(findViewById(android.R.id.content).getRootView());
    System.gc();
    myImage.recycle();
    myImage = null;
}
Community
  • 1
  • 1
learner
  • 11,490
  • 26
  • 97
  • 169
  • Maybe you recycle only last bitmap you have created. You should recycle bitmap when you are done with it. – ACengiz Apr 26 '13 at 19:40
  • By done with it do you mean right after displaying it in a view as either setImageBitmap or setDrawable? – learner Apr 26 '13 at 19:43
  • Not exactly if you done it, you may get exception. I wanted to say that do not wait until your activity to enter onDestroy(), if a bitmap is not useful for you anymore, you can recycle it. – ACengiz Apr 26 '13 at 19:51

2 Answers2

3

You have probably somewhere a reference which you forgot to free. See also this answer about How to find Memory Leak Class/Activity in Android. I'm sure it will help you to find fast where you leak your memory.

Also remember that you have to free the memory by yourself. The callback onDestroy() does not do that for you.


First you need to install the MAT plugin. After a restart click on Dump HPROF file:

Device controls

After some time about 30 seconds. You'll get a dialog where you can click on finish. Than you get this view:

Dump result

There you left click on the biggest part and filter for with outgoing references. Then you get this view here:

Here you can see that I missed to delete items in my ContentManager where several items were stored in my pending HashMap.

Community
  • 1
  • 1
rekire
  • 47,260
  • 30
  • 167
  • 264
2

Look at the Android Activity Lifecycle which shows which methods should be used to create and release resources. It shows that the corresponding function to onCreate() is not necessarily onDestroy() because the developer has no idea when the Android OS will call onDestroy().

The Answer:

Therefore, in your particular instance, you could free or recycle() your Bitmap in onStop(), not in onDestroy().

onPause()/onResume():

Additionally, it is better practice to free resources in onPause() and then recreate them in the corresponding method onResume().

inSampleSize and OutOfMemoryError:

You should also make sure to open your Bitmap images as small as possible with BitmapFactory.Options.inSampleSize in order to reduce the possibility of receiving an OutOfMemoryError. There are many examples on this site demonstrating the use of inSampleSize. Here is just one of them: Resize picture into according to textivew's size

Android Activity Lifecycle:

Android Activity Lifecycle

Community
  • 1
  • 1
David Manpearl
  • 12,362
  • 8
  • 55
  • 72
  • ah, I have been reading some blogs that pair onCreate/onDestroy – learner Apr 26 '13 at 19:47
  • Incorrect. The opposite of onCreate() is indeed onDestroy() - these methods concern lifetime. The opposite of onStart() is onStop(), a pair of methods which concern (at least partial) screen visibility. Your confusion appears to come from the atypical path of the app being killed without onDestroy() ever being called. – Chris Stratton Apr 26 '13 at 19:57
  • In the case where onDestroy() is not called, it is because the process has been killed, at which point there's neither need - or opportunity - for developer-written code to free any resources. Remote resources in other processes linked to those in the dead process should be freed if Binder IPC has been properly used to notify of remote object death. Similarly, OS level resources should be getting freed by process death itself. – Chris Stratton Apr 26 '13 at 20:05
  • When I change onDestroy to onPause, I get a nullPointerException at `unbindDrawables(findViewById(android.R.id.content).getRootView())`. The exception says `E/AndroidRuntime(3430): java.lang.RuntimeException: Unable to pause activity ...` I am guessing it's referring to getRootView. – learner Apr 26 '13 at 20:18
  • Are you calling `onPause()` [bad]? Does `onPause()` now call `super.onPause()` [good] or `super.onDestroy()` [bad]? Also, once you `recycle()` a `Bitmap`, you must be sure not to use it again unless you recreate it. Use `isRecycled()` to determine if you are trying to access a `Bitmap` that has been recycled. – David Manpearl Apr 26 '13 at 20:20