Understanding Android App RAM Usage - android

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.

Related

Why is this a memory leak

I came across a library for memory leak detection in Android (Java) called LeakCanary but cannot understand the example where they leak the memory. Could anybody please explain how and why the code shown in their example is a memory leak.
class Cat {
}
class Box {
Cat hiddenCat;
}
class Docker {
static Box container;
}
// ...
Box box = new Box();
Cat schrodingerCat = new Cat();
box.hiddenCat = schrodingerCat;
Docker.container = box;
and then they watch the variable schrodingerCat for leaks which gives a leak shown as follows (which I dont know how to relate to the above code).
* GC ROOT static Docker.container
* references Box.hiddenCat
* leaks Cat instance
Any help with the explanation of the leak and how the detection relates to it would be very helpful. Also some good articles for beginners would be nice.
Thanks!
First, let's understand what is a Memory Leak:
Definition
Memory Leak is data allocated (bitmaps, objects, arrays, etc) in the RAM that the garbage collector (GC) is unable to free, although it is not needed anymore by the program.
Example
A user is opening a view that shows an Image. We load the bitmap to the memory. Now the user exit the view and the image is not needed anymore and there is no reference to it from the code. At that moment the GC comes into action and remove it from the memory. BUT, if we still had a reference to it, the GC will not know it is OK for removal and it would have stayed in the RAM taking unneeded space - aka Memory Leak.
Cat in A Box
Let's say we have a Cat object in our app, and we hold it in a Box object. If we hold the box (have a reference to the Box object) and the Box holds the Cat, the GC will not be able to clean the Cat object from the memory.
The Docker is a class that has a Static reference to our Box. This means that unless we nullify it, or re-assign the value, the Docker will keep referencing the Box. Preventing the Box (and the inner Cat) from ever being removed from the memory by the GC.
So, do we need the Cat? is it still relevant for the App?
This is up to the developer to decide how long we need the Cat for. LeakCanary and other diagnostic tools suggest of a possible Memory Leak. They THINK that the object (Cat) might not be needed anymore so they alert that it is a leak.
Recap
In the example, they give a common scenario of a Memory leak. When using a Static reference we prevent from the GC to clean an Object. You should read this:
* GC ROOT static Docker.container
* references Box.hiddenCat
* leaks Cat instance
as:
Object Cat might be not used anymore but was not removed from memory by the GC.
The reason the object Cat was not removed is since Box is having a reference to it.
The Reason the object Box was not removed is since the Docker has a static reference to it.
Static reference by Docker is the ROOT of the tree that causes the possible leak.
It looks like the RefWatcher instance used to "watch the variable schrodingerCat for leaks":
refWatcher.watch(schrodingerCat);
forces a set of GC passes and if the reference passed in isn't collected during those GC passes it's considered a leak.
Since the static Docker.container.hiddenCat is keeping a GC rooted reference to the object originally known as schrodingerCat, it can't be GC'ed so when you ask RefWatcher to check it. Therefore it lets you know that the object can't be collected.
I suggest you read this answer https://stackoverflow.com/a/11908685/1065810
It will probably help you understand the example above.
In brief, in your example, the class Docker keeps a reference to a Box. Even when the container box is not needed anymore, the class Docker still holds a reference to it thus creating a memory leak.
Let me know if that helps.

BitmapFont Memory Leak

I am using a BitmapFont as follows:
create(){
font = new BitmapFont(getFileResource("50.fnt"), getFileResource("50.png"),
false);
}
dispose(){
font.dispose();
}
I have several screens that load and unload fonts of different sizes. As time goes by, the memory fills up.
After spending a long time looking for a memory leak, I find this BitmapFont class is leaking. I think it is leaking on native memory because the leak is not seen using Memory Analyzer.
I am following the procedure to clean up the memory according to the current documentation. But this is not enough. What else should I do to ensure the BitmapFont lets go of its memory ?
This might be a bug. Here you can see your constructor. And here a very similar one. The difference is that the 2nd sets the ownsTexture flag. Only if this flag is set, the texture gets disposed.
I will create an issue/PR to fix this problem or at least make it behave the same way, or add a JavaDoc warning.
For now you can solve this problem by doing bitmapFont.setOwnsTexture(true) yourself.
The error was that the libgdx code is long running, so I could not wait for dispose to be called.
So I was calling dispose on the elements that were no longer needed on the screen from a thread other than the GLThread (on Android)
Libgdx ignores dispose() when not coming from the GLThread().
Adding the clean up code on render such that it runs when outdated components pile up fixed the problem.

Using MAT to Understand Android Memory Leaks

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..

DDMS Heap - 1-byte array(byte[], boolean[])

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.

Memory Error whilst setting image following screen rotation on Mono for Android

Things were going well until I switched off the screen lock on my device, then things started going wrong intermittently.
I've managed to track the issue down and have some workarounds in mind BUT I would like to know if there is 'best practice' for avoiding or removing the issue.
The issue:
I have an application which changes images based on the application status.
The images are not huge but quite large (231k~) and are stored as resources.
After several screen rotations (I counted 27 with a project using a single ImageView), loading the images fails with Exception of type 'Java.Lang.OutOfMemoryError'
Stripped down to the barest project, the following demonstrates the problem:
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
//get a reference to the ImageView
var imageView = FindViewById<ImageView>(Resource.Id.imageView1);
imageView.SetImageBitmap( Android.Graphics.BitmapFactory.DecodeResource( this.Resources, Resource.Drawable.Ready) );
}
The above code is the only method I used to reproduce the issue.
Whilst attempting to resolve, I extended the example so that imageView was released in OnDestry:
protected override void OnDestroy ()
{
base.OnDestroy ();
imageView.SetImageBitmap( null );
imageView.DestroyDrawingCache();
imageView.Dispose();
}
This made no difference unless I added GC.Collect() which I don't want to do.
The best workaround I've currently thought of so far would be to modify the code as follows:
static Bitmap _ready = null;
private Bitmap GetReadyImage {
get {
if (_ready == null) {
_ready = Android.Graphics.BitmapFactory.DecodeResource (this.Resources, Resource.Drawable.Ready);
}
return _ready;
}
}
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
//get a reference to the ImageView
imageView = FindViewById<ImageView>(Resource.Id.imageView1);
imageView.SetImageBitmap( GetReadyImage );
}
This relies upon a static reference to each Bitmap and a property accessor for each.
I could even write a method which stores the images in a static List to save writing property accessors for each different property/variable.
I could perhaps add the flags ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize |ConfigChanges.KeyboardHidden) but this would break the normal Activity lifecycle which I've read isn't best practice?
I find it strange that having scoured the web, I've not yest encountered similar issues or examples. I'm left wondering how most others deal with this?
Any thoughts or comments are much appreciated.
I can only approach this problem from a truly native perspective, as I have not worked directly with the Mono framework.
The described symptoms are 100% indicative of a memory leak in the Activity, but the code shows no real evidence. If you can truly produce the issue with a project containing only one Activity and those four lines of code, it sounds to me like it is perhaps a framework bug that ought to be filed with Xamarin. Have you attempted to create the same simple project in pure Java to see how the results fare on the same device/emulator you are using? It would also be interesting to know if the issue is localized to a specific version of Android. I have never seen this particular behavior before in a native application project.
The awkward part is your statement that forcing a garbage collection makes the problem go away. You're right, you shouldn't have to do that, but a true memory leak (i.e. an unreleased reference) would still persist even if you hit the GC several times. The Android paradigm of destroying and re-creating the Activity on each rotation is such that even if the old Activity lived for awhile, when memory was tight it (and all its references) would quickly be collected to make room for a new instance. If this is not happening, and each Activity is living on past even the system triggered GC passes, perhaps there is a stuck reference in the native code generated by Mono.
Interestingly enough, technically your workaround actually does introduce a true leak, by attaching the Bitmap to a static field that is never cleared. However, I agree that in comparison it seems like a more efficient move. A simpler workaround might also be to code your Activity to manually handle configuration changes (I don't know if Mono is different, but this is accomplished by adding android:configChanges="orientation" to the manifest file). This will keep your Activity from being recreated on each rotation, but it may also require you to reload your view hierarchy if you have different layouts for landscape and portrait. However, even if you have to do this the Acitivity instance will be the same you can safely save the Bitmap without resorting to a static field.
However, if you cannot reproduce the problem with the same project in a native Java project, I would report a Mono bug.
Hard to see without the entire code but it obviously sounds like you have a memory leak. Screen rotation (due to the destroying/creation of the activity) is known to cause these. You might want to have a read at this article by Romain Guy as well as this talk from last year's IO.

Categories

Resources