Android Context Memory Leak ListView due to AudioManager - android

I have a ListView and I would expect it to be cleared from memory when the activity finishes. However, it appears that it is leaking. When I check the Memory Dump, and get the pathToGC for the ListView I get the following,
Class Name | Shallow Heap | Retained Heap
android.widget.ExpandableListView # 0x4063e560 | 768 | 39,904
|- list, mList com.hitpost.TeamChooser # 0x405f92e8 | 176 | 1,648
| '- mOuterContext android.app.ContextImpl # 0x40657368 | 160 | 304
| '- mContext android.media.AudioManager # 0x40662600 | 40 | 168
| '- this$0 android.media.AudioManager$1 # 0x406626b0 Unknown| 24 | 24
I see this same context leaking on a lot of of my ListView's. The trick is that, I am not using AudioManager anywhere in my app at all, no sound coming from the app at all. Please help, it's driving me crazy. Obviously trying to figure out why this is happening and what could be the root issue?

Not related to OP's leak, but for people who come in here because of AudioManager causing leak:
If you see this leak because you are using VideoView, probably is because of this bug: https://code.google.com/p/android/issues/detail?id=152173
VideoView never release AudioManager if video being loaded.
the fix is, as mentioned in the link, create VideoView manually using ApplicationContext.
Edit: this work around will work, until... if the video decoder says the video has an encoding problem. VideoView tries to pop up a AlertDialog using application context. Than a crash happens.
The only work around I can think is to keep creating video view using activity context, and in activity.onDestroy, set AudioManager's mContext to application context using reflection.
Note: have to get the AudioManager using activity.getSystemService(Context.AUDIO_SERVICE) rather than activity.getApplicationContext.getSystemService(Context.AUDIO_SERVICE), since AudioManager is an member variable of Context (you will get wrong instance of AudioManager if you get it from application context).
At last, you may wonder why a member variable (AudioManager) is preventing the class (Activity) to being garbage collected. From the memory analyzer, it shows AudioManager is owned by native stack. So AudioManager somehow did not clean itself properly.

There are several references to AudioManager in your code that you don't create actively. E.g. each clickable View might have one to play onClick sounds [source]. I guess that is the link.
The code looks like it would not create references to AudioManager if you disable the click sounds in your Settings. You could try that and check if there is still a leak.
The reason for your leak might be that you are holding onto some View object in your ListView (Adapter?) code. If you keep them around then you might have a View that has an AudioManager reference and that keeps a Context reference)

I had this same issue but it went away after following the below advice.
Mr Guy recommends not doing heap dumps in the debugger and causing a few GCs before getting the dump.
https://groups.google.com/forum/?fromgroups=#!topic/android-developers/ew6lfZUH0z8

You can use application context to avoid leaks in this case. I don't know why but when I started to use application context the problem was gone.

The most common reason that I found in my application was due to initializing some components via the XML file. When you do that, the Activity Context gets injected but sometimes all you need is an ApplicationContext. With respect to the Web View in Android, this technique greatly helped me a lot.

I would like to share my experience regarding the same issue, I was keeping some Activity in stack by default and not finishing them.
For those activity, I was getting same as mentioned above in hprof report.
Once I finished no longer used Activities, above references did not come. Just finish your activity when it is no longer required, then this issue will be resolved.

This is a very basic activity that replicates the issue (on Android 4.0.3 at least)
public class MainActivity extends Activity {
int image[];
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
image = new int[1000 * 1500 * 4];
}
}
As you can see there are no Views or Layots associated with activity, also I set "Silent" profile in system Sound Settings and turned off "Vibrate on touch".
Now, after few (5-7 depending on your heapSize) restarts this activity generates the java.lang.OutOfMemoryError on trying to create new array.
07-27 19:54:10.160 22542-22542/? D/dalvikvm﹕ GC_FOR_ALLOC freed 6K, 1% free 56040K/56391K, paused 25ms
07-27 19:54:10.190 22542-22542/? D/dalvikvm﹕ GC_BEFORE_OOM freed 23449K, 43% free 32591K/56391K, paused 30ms
07-27 19:54:10.260 22542-22543/? D/dalvikvm﹕ GC_CONCURRENT freed 0K, 1% free 56029K/56391K, paused 3ms+3ms
07-27 19:54:11.850 22542-22542/? D/dalvikvm﹕ GC_FOR_ALLOC freed 6K, 1% free 56040K/56391K, paused 20ms
07-27 19:54:11.880 22542-22542/? D/dalvikvm﹕ GC_BEFORE_OOM freed <1K, 1% free 56040K/56391K, paused 29ms
... Out of memory on a 24000016-byte allocation.
Dumping .hprof I also saw 2 activities, one of them is being held by AudioManager.
Calling the "Update Heap" and then Collect Garbage in Android Device Monitor really does remove the activity from memory, that is what the logcat states on this procedure
07-27 19:44:23.150 85-85/? I/DEBUG﹕ #06 pc 000382cc /system/lib/libdvm.so (dvmCollectGarbageInternal(GcSpec const*)+1204)
I have also tried to build the release version of the apk and it behaves the same. So it is not the debugger holding the reference.
This seems to me as a bug in Android. The workaround would be to explicitly call the image = null in OnStop() or onFinish() of the activity. This of course is not convenient.

If your application crashes for memory leak, then you can avoid this crash using try - catch(java.lang.outofmemory). The fact is that GC is called by JVM itself, so programer has no control on this. You can install your application in SD card, in this case SD card memory will be used. Memory leak will not occur.
Just go to your manifest file, there must be version no. version name, there also must be " Install Location" , make it "preferExternal".

Related

Possible bug in dalvik of Android 2.x during Bitmap allocation?

The phenomenon: First do allocation some big memory blocks in the Java side until we catche OutOfMemoryError, then free them all. Now, weird things happen: load even a small picture(e.g. width:200, height:200) by BitmapFactory.decodeXXX(decodeResource, decodeFile, ...) will throw an OutOfMemoryError! But its OK to alloc any pure Java big Object(e.g. new byte[2*1024*1024]) now!
Verifying: I wrote some simple codes to verify the problem that can download here, press "Alloc" button many times and you will got an OOF Error, then press "Free All", now the environment is set up. Now you can press "LoadBitmap" and you will see its not work on most of Android 2.x phone.(But in the emulator its just OK, odd)
Digging deeper: I try to dig into some dalvik code to find out why, and find a possible bug in function externalAllocPossible in HeapSource.c which called by dvmTrackExternalAllocation who print the "xxx-byte external allocation too large for this process" messages in LogCat.
In externalAllocPossible it simply wrote:
if (currentHeapSize + hs->externalBytesAllocated + n <=
heap->absoluteMaxSize)
{
return true;
}
return false;
Which means once if the native Bitmap allocation size plus the currentHeapSize(NOT the actually allocated size as shown below, in this case, it's keeping the max size of the heap we bumped up but then freed them all) exceeds the limits, native Bitmap allocation will always fail, but the currentHeapSize in Java seems NOT decrease even when 91.3% Java objects' memory have been freed(set to null and trigger GC)!
Is there anybody else met this problem too?
I think this is correct. Its forcing the entire app (Java+native) take no more than a certain amount of memory from the OS. To do this it has to use the current heap size, because that amount of memory is still allocated to the app (it is not returned to the OS when freed by GC, only returned to the application's memory pool).
At any rate, 2.x is long dead so they're not going to fix it there. They did change how bitmaps store their memory in 3.x and 4.x. Your best bet is to allocate all the bitmaps you use first, then allocate those large structures. Or better yet- throw those large structures into a fixed size LRUCache, and don't use the grow until out of memory idea, instead load new data only when needed.
The class Bitmap has the recycle() method, described as:
Free the native object associated with this bitmap...
The reason behind this method is that there are two heaps: the Java heap and the heap used by native code. The GC only sees the Java heap sizes; for GC, a bitmap may look as a small object because it's size on the Java heap is small, despite the fact that it references a large memory block in the native heap.

Potential Out of Memory error with multiple bitmaps

I had a user comment that after viewing a bunch of images in my app, it crashes (he believes that it is due to out of memory error). I have the following relevant code:
int themeID = mNav[mPos];
String icon = getThemeData(DbAdapter.KEY_ICON, themeID);
ImageView viewer = (ImageView)findViewById(R.id.viewer);
Bitmap bMap = null;
try {
bMap = getJPG(icon + ".jpg");
} catch (IOException e) {
e.printStackTrace();
}
viewer.setImageBitmap(bMap);
That gets reran as the user flips between images. From here I see that you should call recycle() on bitmaps. Do i need to call it on bMap after setting the image? Or is there some way to pull it from viwer prior to setting the next one?
According to the documentation for recycle (if I call it on bMap) it appears I don't need to use it: This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.
If you need to explicitly call recycle() it probably means that you have memory leak. Calling it is almost never a solution.
Did you try to check your app for potential mmory leak?
To check it you can for example rotate your device a few times and check how the Garbage Collector behaves. You should have something like GC_... freed 211K, 71% free 300K/1024K, external 0K/0K, paused 1ms+1ms in your LogCat nearly every time you rotate. Watch for changes in this part: 300K/1024K. If you don't have memory leaks, the first part should grow and then get smaller after a few GCs. If you have a memory leak, it will grow and grow, to the point of OOM error.
Check out my other answer about a memory leak.
If you're sure you don't have a leak and you're operating on Honeycomb you can increase the heap size accessible for your app like this: android:largeHeap="true" but it's only recommended when you deal with some huuuge bitmaps or videos, so don't overuse it.

Android Heap Memory size doesn't looks correct

i have an Android app that displays alot of images, it works, the images are gatherd from an url, added to a que and gathered by 4 threads,stored in a cache and then displayed in a listview view 4 images for row, there are abot six rows at each time on the screen. There is a total of usually 90 images.
The rows(and imageviews) are always recycled, so the amount of items is always the same and i'm not initializing anything.
This seems to work quite fine, i have always an average used heap size of 13MB.
The problem i have is that at the beginning mi max heap size is quite small and i get GC messages like:
01-20 16:48:39.191: D/dalvikvm(9743): GC_FOR_ALLOC freed <1K, 31% free 12048K/17351K, paused 25ms
but the more i scroll up down the view the heap size grows more and more untile i get things like
01-20 17:02:05.339: D/dalvikvm(11730): GC_FOR_ALLOC freed 544K, 72% free 13871K/49159K, paused 35ms
as you see even if the used is the same the maximum is increased even if i never got to that limit. and the true problem is that at this point i start to get outofmemory errors.
Can someone explain me what's wrong?
Thanks!
What version of Android are you using? If you're testing on pre 3.0 (ie 2.x), the byte arrays that store most of the information in Bitmaps are allocated and stored in native memory. This means that in heap dumps and in the GC notifications, you only see the small amount of memory used for pointers in Bitmaps, rather than the actual size.
For more information check out this google IO talk on memory management and detecting memory leaks: http://www.youtube.com/watch?v=_CruQY55HOk
Also I've worked on several apps doing similar things. My guess is that either your cache size is way too large, or (more likely) the images you're displaying and storing in the cache are much larger than the size you actually want. If you display a bitmap in an image view, the imageview will store the original bitmap in memory, even if it is significantly larger than what would actually fit in the view. Try resizing the images from disk to at least closer to the appropriate size before trying to display them: How do I scale a streaming bitmap in-place without reading the whole image first?
To cache my Images I use Map<String, Drawable> drawableMap. On a OutOfMemoryError I call this function:
private void cacheLeeren()
{
int size = drawableMap.size();
int del = (int) (size * 0.3);
Set<String> s = drawableMap.keySet();
for (String t : s)
{
if (del >= 0)
{
drawableMap.put(t, null);
del--;
}
}
}
I think it's not the best way...but it works ;-)
My guess is that your app reaches a very high peak of memory usage for a short time. It's true that on average you only use 13MB but if your heap grows to as much as 50MB, it means that momentarily you've consumed much more memory than you're thinking.
Let's try to figure out where this is happening. You've mentioned that you're using an LRU cache. This cache frees memory as soon as it fills up. My guess is that you're starting to free memory too late, and this memory isn't freed immediately - since it depends on the system GC. Whenever you're freeing some items from the cache, try to call System.gc() manually.
You've also mentioned that you're calling Bitmap.recycle(). To the best of my knowledge this is useless on Android 3+ because the native heap is no longer used for bitmaps. Since all bitmaps are on the dalvik heap, they will be freed by the GC.. You can't rush this like before unless you call System.GC() yourself.
Another idea for your source of problems is heap fragmentation. See my previous SO answer to a similar issue in this question.

Android Memory Usage and Objects in Memory not being released in 1.6 and 2.1

I'm checking my app for Memory Leaks/Usage and came across something weird that I've only seen so far in Android 1.6 and 2.1. After clicking around in the app a bit and I run "adb shell dumpsys meminfo" for my application, I see the following:
DUMP OF SERVICE meminfo:
Applications Memory Usage (kB):
Uptime: 34639912 Realtime: 153524709
** MEMINFO in pid 5778 [com.app.myapp] **
native dalvik other total
size: 14336 4679 N/A 19015
allocated: 13971 4139 N/A 18110
free: 280 540 N/A 820
(Pss): 2986 4181 13491 20658
(shared dirty): 972 3948 620 5540
(priv dirty): 2876 3224 10976 17076
Objects
Views: 545 ViewRoots: 4
AppContexts: 32 Activities: 31
Assets: 2 AssetManagers: 2
Local Binders: 43 Proxy Binders: 79
Death Recipients: 2
OpenSSL Sockets: 1
SQL
heap: 91 dbFiles: 0
numPagers: 4 inactivePageKB: 0
activePageKB: 0
Asset Allocations
zip:/data/app/com.app.myapp.apk:/resources.arsc: 119K
As you can see, nothing is getting deallocated/GC'd, the Activities are piling up, the AppContexts, etc. until the app just crashes with an OutOfMemoryError. This doesn't happen on 2.2+.
Can anybody give me some insight into why this is happening? I have a feeling it's either something simple, or it's just something weird with my app, but I'm at a loss as to why this is happening.
FYI, I've reproduced this in a 1.6 and 2.1 emulator, as well as my G1 running 1.6. A recent crash report from a user also shows this, which they were running 2.1 on a Droid Eris. Let me know if any more details/code is needed to help with this.
##UPDATE##
Thanks to the info from momo, I was able to track down some memory leak issues, which drastically cut down on the amount of Activities/AppContexts that would show in the Objects list of meminfo.
The number is now down to around the number of actual activities that are in my application, so it seems that on older versions of Android, it will show the total amount of objects your app is consuming. On newer versions it won't, though that could just be only the case on my test devices.
To get a clear picture on why Activities are held up, I normally use MAT and then look at Path to GC root from the Activity that get stuck.
I've a created a simple project which load simple TestActivity in order to illustrate the process. Below is the code for it:
package com.so;
import android.app.Activity;
import android.os.Bundle;
public class TestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
Here are the steps:
Dump the hprof on the running process via DDMS "Dump HPROF File" function
Assuming you have MAT installed, this should bring up the MAT screen
Now filter based on your activity package, for the sample above, it is com.so. Screenshot for this process is below:
Now you want to see if this has a clear path to GC. You do that by right clicking the Activity and show all references as shown below:
You should see that your Activity is held by com.android.internal.policy.impl.PhoneWindow$DecorView and no one else. If this is the a case, you are ok and this Activity will be eventually reclaimed by GC.
Now I will do change my class to include a static variable that will hold its own instance:
package com.so;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
public class TestActivity extends Activity {
static ArrayList memoryLeakList;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// create a deliberate static list to cause the leak
TestActivity.memoryLeakList = new ArrayList();
TestActivity.memoryLeakList.add(this);
}
}
And if I run the code hprof with the same steps, I now get the reference of the Activity is held by the ArrayList and not the com.android.internal.policy.impl.PhoneWindow$DecorView signifying that there is possibility of a leak if I don't clean up the array
Now, you don't have to do that for every Activity, what I would do just briefly run the app and then dump the HPROF. You would then again filter by package to get the snapshot of your application. In the initial Histogram, you should be suspicious for any Activity that has number of instances more than one after hitting GC button in DDMS and start investigating from there.
One more note, on my 2.1 phones, I couldn't get the HPROF via DDMS, so I did it through the emulator following these steps:
Go to ./adb shell
Type ps to get the pid of your app process
Type kill -10 , you should see in your logcat that it is dumping the memory to /data/misc
If you get permission denied, make sure you read/write on that folder by doing chmod 777 data/misc
Pull the hprof generated by either using DDMS File Explorer in Eclipse or pull command
Since the hprof is dalvik based, in order to use it with memory profiling tools you need to convert it first via hprof-conv available in the tools directory of your Android SDK installation
Run ./hprof-conv [source dump] [target dump]
Use Memory Analyzer (MAT) to view your process Heap.
http://www.eclipse.org/mat/

Android - Memory Allocation Tracker not tracking my test allocation

I'm currently playing around with the memory allocation tracker of the DDMS tool.
In my project i insert the following line in a button's onClickListener:
memTrackerTest = new byte[1024*1024];
memTrackerTest is a private variable of the Activity.
I would expect to see the allocation in the tracker but unfortunately it doesn't appear.
Other parts of my code, like the creation of a ProgressDialog show up fine.
Any idea why my big allocation is not displayed in the allocation tracker?
I'm not sure what the exact problem was. I tried the same code in a different class and it worked fine there.

Categories

Resources