5

SETUP :

I have this app which has 4 activities in a linear path, really simple navigation : A -> B -> C -> D

All the activities share the same background image and all have a couple of regular buttons, a textview or an edittext. All of which are defined in separate views xml files.

The background is a gradient and is kind of heavy. Around 3 megs as an uncompressed bitmap.

The app does nothing yet, the only logic in it, is for starting activities and closing them on button clicks

I tried to use MAT to find a memory leak but couldn't find anything. The biggest retained size in my app is 656(ko?) the total retained size for the app is 1520(ko?) and I can't find any object that would be duplicated. Which by the way completely contradicts dumpsys which shows 27300(ko?) allocated

PROBLEMS :

  1. When I navigate UP, I see an increase of memory usage equivalent to the background size.
  2. When I navigate DOWN, closing the activities with the back button or a finish command, the memory usage of the app does not decrease.
  3. If I go from A to D then back to B and rotate the screen, the app force closes with an OutOfMemory Exception.

QUESTIONS :

Update : I guess the real question is why do I have a huge memory leak(5 megs at a time) with a 27megs app size at freeze time and I can not see it in the MAT ?

  1. Why would Android decompress multiple times the same background, once per activity ? Seems inefficient.
  2. Is it possible to overcome this problem by using themes or will I see the same "allocate 1 background by activity" weirdness ?
  3. Why are the activities not reclaimed when closed ?
  4. Why are MAT and dumpsys presenting different numbers ?

CLUES

At exactly the same time I have : dumpsys meminfo :

Applications Memory Usage (kB):
Uptime: 74006853 Realtime: 110962243

** MEMINFO in pid 22683 [com.kayenko.sosadresse] **
                    native   dalvik    other    total
            size:    20820     5767      N/A    26587
       allocated:    18751     2901      N/A    21652
            free:      312     2866      N/A     3178
           (Pss):     1357      201    16782    18340
  (shared dirty):     2264     1804     5456     9524
    (priv dirty):     1280      116    16032    17428

 Objects
           Views:        0        ViewRoots:        0
     AppContexts:        0       Activities:        0
          Assets:        2    AssetManagers:        2
   Local Binders:       18    Proxy Binders:       16
Death Recipients:        1
 OpenSSL Sockets:        0

 SQL
               heap:        0         MEMORY_USED:        0
 PAGECACHE_OVERFLOW:        0         MALLOC_SIZE:        0

And this dominator tree :

MAT Dominator tree

Thansk to anyone with a clue on what I'm suppose to look for.

Yahel
  • 8,522
  • 2
  • 24
  • 32

3 Answers3

7

Memory is a very tricky subject in Android.

Every app gets a heap memory limit depending on the device. This heap memory is the dalvik memory plus the native memory, and you can see it as the total column in the dumpsys meminfo results. The dalvik memory deals with everything except with the bitmaps, that are allocated in the native memory (this is true for Android versions before Honeycomb).

Having said that I can only answer to some of your questions:

  1. As far as I know, Android will always allocate memory for Bitmaps, even if they are the same. Therefore, in your case, every activity allocates memory for your background.

  2. I don't know if its better to work with themes, you'll have to try that.

  3. On one hand, activities are not reclaimed while the device has enough memory to deal with the next activity. Every activity is pushed to a pile from where it is recovered when you pressed the back button. In case Android needs more memory it removes one activity from the pile deallocating its memory (going back to question number one, maybe this is the reason for not sharing memory). On the other hand, you can set the activities launchMode to change this behaviour (have a look here).

  4. I think MAT doesn't show native memory data. Use dumpsys meminfo's native column to see how much allocated memory for Bitmaps you have.

I have had hard times dealing with OutOfMemory problems myself. Now I have a much more clear idea of how it works and I am able to work with large files without running out of memory. I would highly recommend these two resources that helped me a lot:

Good luck!

Community
  • 1
  • 1
Xavi Gil
  • 11,460
  • 4
  • 56
  • 71
  • Thanks Xavi, this was very informative. I had already viewed the links you gave me but nothing helped unfortunately. I didn't know about launchmode and must admit I understood nothing of what it's supposed to do. I guess I'll have to test it because the documentation is well as usual 'lacking' :) – Yahel Jan 27 '12 at 12:04
  • 1
    I'll mark your answer as the answer though because you actually gave me 3 answers out of 4 :D – Yahel Jan 27 '12 at 12:06
4

So after hours of investigation and the help of Xavi here are the results :

Q. Why would Android decompress multiple times the same background, once per activity ? Seems inefficient.

A. Even though it would seem logical to have some sort of way to ask a bitmap to be shared across activities since we are on mobile devices with little memory, this does not seem to exists in Android. Every time a bitmap is used in different activities it is uncompressed to native memory.

Q. Is it possible to overcome this problem by using themes or will I see the same "allocate one bitmap by activity" weirdness ?

After experimentation, the memory consumed using themes does not differ at all from the amount of memory used by explicitely setting the bitmap in the xml of the layouts. This is weird to me since styling is about grouping attributes to a same place.

Q. Why are the activities not reclaimed when closed ?

A. Well i'm not sure but what I found is that this gave me OOM errors almost only when debugging. When launching the app from the device, it almost never happened. A glitch in the debugging process ? Try it before you loose 5 hours testing a zillion thing.

Q. Why are MAT and dumpsys presenting different numbers ?

A. The answer by Xavi is correct, dumpsys meminfo shows all the memory allocated (native + dalvik) while the MAT shows only the Dalvik one. Since bitmaps pixels are allocated in the native memory, MAT won't see it. This is only true prior to Android 3.0 where they changed the allocation scheme and made the pixel data of bitmap fit into Dalvik.

Q. How did I solve my problem

A. First this might not have been a problem when not debugging. Second, to be on the safe side I replaced the gradient png with a shape with a radial gradient and used the

getWindow().setFormat(PixelFormat.RGBA_8888);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DITHER);

in the oncreate of my activities to try to avoid banding. I will still have banding on some devices but I'd rather have banding than FCs

Yahel
  • 8,522
  • 2
  • 24
  • 32
0

You need to explicitly recycle Bitmap used as background when you destroying your activity. Code will be like this:

@Override
protected void onDestroy () {
    Drawable drawable = getView().getBackground();
    if (drawable instanceof BitmapDrawable) {
        ((BitmapDrawable)drawable).getBitmap().recycle();
    }
    drawable.setCallback(null);
    getView().setBackgroundDrawable(null);
    super.onDestroy();
}

Maybe you need to free resources recursively for nested views, but it depends on your layout structure. This is general case

OleGG
  • 8,589
  • 1
  • 28
  • 34
  • Thanks for the input, unfortunately it did not help at all. None of my widget have bitmapdrawables according to your code and I also tried the code from this page : http://www.alonsoruibal.com/bitmap-size-exceeds-vm-budget/ and then a mix of the two to be as agressive as possible but to no avail. – Yahel Jan 26 '12 at 19:02
  • I disagree. Dalvik will recycle bitmaps automatically once there are no more references to it. It is not necessary to do this by hand, the recycle() method exists so you can hurry this process and reclaim memory in advance. – Kevin Read Jan 04 '13 at 09:45