I'm writing my first app and trying to understand how to use MAT to find potential memory leaks. To keep things simple, I compiled the default Hello World app provided when starting a new project in Android Studio, i.e.,
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Nice and simple. Now immediately after launching this app on my tablet I take a heap dump (heap-conv-1.hprof) using DDMS and the hprof-conv conversion tool. I then rotate the tablet 20 times so the activity goes through a number of life cycles, after which I take another heap dump (heap-conv-2.hprof).
I load both heap dump files into MAT, compare them and do a regex for .*MainActivity.*. The result is:
Now there are 7 instances of the MainActivity after the rotations. Since I'm not doing anything with this app, am I right in thinking that this is purely because those instances haven't been GC'd yet? In which case , am I right in thinking that Android doesn't GC after an orientation change, and only when it needs more memory?
For Your code its not take that much of memory and doesn't leads to leaks..Application takes lot memory when you are loading large number of bitmaps into memory or holding large number of objects.and Using Activity context to long lived objects..
Here is the excellent video on memory leaks by google developer.Memory leaks
you will get to know all the things after watching this..
Related
I have a memory leak of bitmaps causing out of memory. I ran the tests on Android 5.0 (Samsung S5). I have investigated the issue using Android Studio (1.5.1 + 2.0.0 Preview 7). The HPROF memory dump show that there are multiple byte[] which correspond exactly to a particular huge bitmap I use temporarily. If I make sure I keep references to the bitmap then Android Studio shows me a Bitmap with 11MB dominating size and a byte[] with 11MB Shallow size. If I don't keep references to the bitmaps then some of the bitmaps become garbage collected and some end up as byte[] without incoming references (ie. no parents) as shown in image.
I have tested my app enough to know with reasonable confidence that this 11MB byte[] is a roughly 2891x1000x4 bitmap I have had in memory. Some smaller bitmaps also get leaked and show up with no incoming references.
The bitmaps above are allocated in a subactivity. If I return to the parent activity (in same process and thus dalvikVM) and force 2x GC then memory is released. Multiple manual GC's does not release the memory before quitting the sub-activity.
It seems to be independent on whether I run bitmap.recycle() or not. It happens very rarely if I just stand in the same place in the app and run the code generating the huge bitmap out of the same view. If I move around the app and generate the bitmap from different views its a lot more frequent, like from 50% leaks to 10% leaks.
Is it an Android bug?
Do I use the bitmaps wrong in some way and Android studio just fails to show me a correct view of the memory?
Is my understanding correct that if there are no parents in the reference tree below (see image), then the memory should be released by garbage collector (hence its an Android Bug or studio bug or hprof dump-bug?)
I found a solution to the leak, though Android Studios reporting of the unreferenced byte[] is still a mystery.
It appears that the ImageView in which i did
imageView.setImageBitmap(bitmap)
will prevent GC of the bitmaps underlying byte[]. bitmap.recycle() does not help, nor does unbinddrawables() as written about in numerous locations
if (imageView.getBackground() != null) {
imageView.getBackground().setCallback(null);
}
setImageBackground(imageView, null);
imageView.setImageBitmap(null);
imageView.setImageDrawable(null);
When I removed the view from the viewhiearchy and removed all my own references to the view, then the byte[] was GC'ed and the leak was gone.
My app uses a service which I start in the main activity's onCreate() method. When I first launch the app on my tablet, and view what's running in Settings/Apps/Running, it shows my service running and consuming 11MB of RAM.
Now, if I cycle the activity's life cycle 20 times by rotating the device, and go back into Settings/Apps/Running, it shows that I'm now using 29MB of RAM.
At first I thought this must be due to a memory leaks, but after taking heap dumps before and after cycling the activity's life cycle, I don't appear to be leaking any objects. Below is a screenshot from MAT, where the column titled Objects #0 lists instances of my objects before cycling, and column titled Objects #1 lists instances of my objects after cycling.
and for all objects
There don't appear to be any obvious memory leaks, and yet I can't understand why the RAM usage in Settings/Apps/Running increases after each orientation/lifecycle. Am I missing something here? Why is my RAM usage apparently increasing, when I don't appear to have any memory leaks?
Update
The reason my app was consuming more RAM on each orientation change was the result of creating custom fonts from assets. I had created a custom TypefacedTextView (can be seen as an object in the screenshots above) that appears to have been reloading the fonts into memory each time the view was created. Removing the TypefacedTextViews has fixed the problem. The problem was made clearly apparent using the adb tool with command shell dumpsys meminfo my.package.com which listed my abundant font Asset Allocations.
There's a short blog post which, I believe, holds a lot of information nonetheless. You can find it here.
In it you find an intereseting explanation of what can/will happen if you manage your Activity's references wrongly:
private static Drawable sBackground;
#Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
This code is very fast and also very wrong; it leaks the first
activity created upon the first screen orientation change. When a
Drawable is attached to a view, the view is set as a callback on the
drawable. In the code snippet above, this means the drawable has a
reference to the TextView which itself has a reference to the activity
(the Context) which in turns has references to pretty much anything
(depending on your code.)
Since you're talking about memory leaks on orientation changes, you may very well be making the mistakes the author details in his post.
I experience some memory leaks in my android application. I've already used MAT to analyze the memory usage. But I have one question from the DDMS perspectiv in Eclipse, what does 1-byte array[byte[], boolean[]) mean?
Is this specific for my application? You can see that this is the big memory leak issue, this always increases in size, and the rest will increase and decrease randomly. My guess is that the GC doesn't catch this type. Can anybody explain why this happen, with this little information?
One byte array is the designation for any data structure that is organized as a single byte array. In you case and with that size, I would bet in a Bitmap or a Drawble.
Most common reasons for memory leaks are static object not properly managed and holding references to:
Context
View (which holds reference to context (and possibly also to bitmap)
Thread (which are not easly collected by GC)
Handler (which holds reference to context)
Most of them can be solved ensuring that you set the object to null when it's no long required.
Regards.
A byte and a boolean are each 1 byte. If you have an array of those you have a "1-byte array".
A ByteBuffer for example should internally hold one of those.
You have a total of 614 of them where the smallest one be a byte[24] (or boolean[24]), the largest one is 3MB. All of them together use 104MB.
The GC will get rid of them if they are no longer referenced.
For example when you put
private static byte myArray[] = new byte[3 * 1024 * 1024];
in one of your classes and never set it back to null (myArray = null) then this one can't be garbage collected because another Object has a reference to it. The object would be the class itself (as in String.class). The class object can't be garbage collected since classes are never unloaded. (they are referenced by their ClassLoader which could itself be referenced / loaded by another ClassLoader and you can't create Objects & use classes without them - they need to stay and they need to keep a reference to their classes)
It's usually not that simple but often start with static somewhere.
Within MAT (after you force GC) look at the reference chain for the objects that are no longer intended to stay alive and identify the one Object that holds the reference. Set that one to null in your code and your leak is gone. Explained in more detail here:
http://android-developers.blogspot.de/2011/03/memory-analysis-for-android.html
I ran to this problem tonight and almost I checked every bit of code but I couldn't find anything.
What I did was starting the app from intelij and then pressing home button and open the app again. Every time the app heap was doubled.
Finally I discover when I launch the app from ADB and press the home button and open the app again it doesn't bring back the old activity, it just start a new one. When I pressed finish it starts to cycle through all of them. It's like they are treated as two different intent. So I put android:launchMode="singleTop" on the main activity in manifest and it resolved the problem.
Although it's not the main cause of this problem but if you encountered this check this out before anything. It wasted three or four hours for me.
So, I'm using custom number tiles for images. The images are stored as resources. I've been tracing a memory leak, and have cause to believe the method I am storing these images for use is suspect. Currently, I'm doing this:
private void loadImageList()
{
Log.d(TAG,"Reloading List "+imageList);
if (imageList==null || imageList.size()<10)
{
imageList=new ArrayList<Bitmap>(10);
imageList.add(0,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number0));
imageList.add(1,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number1));
imageList.add(2,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number2));
imageList.add(3,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number3));
imageList.add(4,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number4));
imageList.add(5,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number5));
imageList.add(6,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number6));
imageList.add(7,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number7));
imageList.add(8,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number8));
imageList.add(9,BitmapFactory.decodeResource(getResources(), R.drawable.ant_number9));
}
}
public void onStart()
{
super.onStart();
loadImageList();
}
What will happen is if I open and close this application repeatedly, then the system won't have enough memory to add an image to the ImageList. I'm setting the image of the button like this (But with an object ImageButton, of course). I should add that the application is threaded, and this call resides in a runOnUiThread(Runnable)
ImageButton.setImageBitmap(imageList.get(current_var));
I've tried deleting the images in the onStop() command, but it will sometimes cause a crash when the thread tries to allocate the image stored in memory to the button, due to the threaded nature of the beast.
So, is there a better way that I can load these images that won't cause a memory leak?
I realized something after posting this. I could just make the imageList static. These images aren't going to change from one view to the next. The Bitmaps don't contain a view, so the memory won't lock up. I'm already checking to see if the values exist before I'm using them. And this function is used so frequently that holding on to static memory won't hurt other aspects of the program. Sometimes it just helps to put it into words..
I am working on an app having a lot of image and using a sensor listener that I register at the beginning of the activity with the sensor manager to detect shake events and change images based on shake direction.
I read the event values in onSensorChanged(SensorEvent event) in order to use it in my app:
public void onSensorChanged(SensorEvent event){
accX = event.values[0];
accY = event.values[1];
accY = event.values[2];
}
I unregister the sensor listener on pause of the activity and also call system.gc for garbage collection.
Everything works well till I am on a single activity.
When I finish current activity and open another activity (again having a a sensor listener), app starts giving out of memory exception after a while:
"OutOfMemoryException: bitmap size
exceeds VM budget"
Note that heap size of the app is never more than 4 MB.
One more observation is that the app never crashes if sensor listener is not used which makes me thinks that possibly there are memory leaks when sensor events are generated and when a new activity is loaded, due to lesser memory available, the app gives OutOfMemoryException when bitmaps are used in the activity.
So seemingly two possible reasons:
images are not recycled properly
http://code.google.com/p/android/issues/detail?id=8488
every time there is a SensorEvent, somewhere there are memory leaks which decreases the available memory for an app
Android: reading accelerometer without memory allocation?
I'm also using the solution described in comment 51 here:
http://code.google.com/p/android/issues/detail?id=8488
to recycle everything but it still gives memory issues.
If you can tell me the best approach to use the images in a large size app, how to recycle them and confirm if there are really any memory leaks while using the sensor listener, or any suggestions what I should look for in the app, it will be really helpful.
For the images to get recycled properly, if possible, try using Bitmap.recycle() method. This is becasue the image is actually kept as a reference (in pre-Honeycomb) and the actual data is allocated using native memory.
In Honeycomb the memory for images is allocated on the heap so it is easilly GCed. Additionally, you may set largeHeap="true" (again post-Honeycomb).
FYI, you may use the combination of SoftReference and HashMap to achieve better memory control on Bitmap. By using SoftReference, memory will be released by not referencing the object anymore when memory is not enough.
A Common Example:
HashMap<String, SoftReference<Bitmap>> cache;
...
Bitmap bitmapToRead = cache.get(key).get();
if (bitmapToRead == null){
cache.put(key, new SoftReference<Bitmap>(bitmap)); // bitmap from Storage for example
/* your operation */
}else{
/* your operation on bitmapToRead */
}
But to be honest, I have experienced OutOfMemoryError even used SoftReference<Bitmap>. What I have done to overcome this error is to recycle() the bitmap manually.