After using the NDK for almost a year I've come to realize that the Process lifecycle in Android is different than the Activity lifecycle.
More specifically, my native library is being loaded in OnCreate but in onDestroy it is not unloaded (I don't even know if you can unload a native library here), but at the next OnCreate, it is still the same process, so all global variables from my native library are still alive with their state preserved. This is the same for static variables in Java as well.
This was pretty fine with Android 2.3 but I recently got onto Android 4.2.2 and I noticed something really weird. It is possible now to have the process killed and the activity to just call onRestart() (or that is what I assume from reading the logs) which is kind of weird to be honest because I assume onCreate would be called right after the process is instantiated, but he keeps a different tab because he thinks in "activities" and not processes. So basically if I pause one of my apps for a really long time (say 1+ hours) and I return to it, it usually results in a crash.
Excerpt from the device log :
04-14 04:41:34.886 2376 2376 I am_on_paused_called: [0,com.re3.benchmark.AboutActivity]
04-14 05:06:26.128 386 594 I am_proc_died: [0,2376,com.re3.benchmark]
04-14 13:19:44.256 386 538 I am_proc_start:[0,4761,10062,com.re3.benchmark,activity,com.re3.benchmark/.AboutActivity]
04-14 13:19:44.295 386 595 I am_proc_bound: [0,4761,com.re3.benchmark]
04-14 13:19:44.397 386 595 I configuration_changed: 5248
04-14 13:19:44.459 386 595 I am_restart_activity: [0,1115347592,8,com.re3.benchmark/.AboutActivity]
04-14 13:19:44.701 4761 4761 I am_on_resume_called: [0,com.re3.benchmark.AboutActivity]
04-14 13:19:44.881 386 401 I am_activity_launch_time: [0,1115347592,com.re3.benchmark/.AboutActivity,631,631]
04-14 13:19:52.725 386 595 I am_crash: [4761,0,com.re3.benchmark,8961606,java.lang.NullPointerException,NULL,REFramework.java,330]
04-14 13:19:52.725 386 595 I am_finish_activity: [0,1115347592,8,com.re3.benchmark/.AboutActivity,crashed]
04-14 13:19:52.772 386 595 I am_pause_activity: [0,1115347592,com.re3.benchmark/.AboutActivity]
PS:I do realize the crash originates in my code, but my code doesn't really handle onRestart being the first function to be called in the entire process (as in, it bypasses my native library initializations since I expect onCreate to be called first ).
You probably should be loading native lib in Application.onCreate and not Activity.
If you have more than 1 Activity, only the currently visible is created and if you were loading lib in the first Activity, you won't get it loaded at this point.
Related
I have a NativeContentAdView in a ListView item. When I call setNativeAd(NativeAd) on it, a noticeable lag happens. All AdMob class and method names are mangled (zzdt, zzalp, zzow etc) so I omit them.
Things AdMob performs in View#onAttachedToWindow callback:
registers several BroadcastReceivers (each requires a Binder transaction) (sum ~10 ms)
Things AdMob performs in OnScrollChangedListener#onScrollChanged:
dozens of DP-to-PX conversions, calling Display#getMetrics and triggering a Binder transaction for each conversion (sum ~7 ms)
generates some JSON (~8 ms)
evaluates some JS (~7 ms)
creates a WebView instance (~5.5 ms), sets it up (~9 ms), asks it to loadUrl (~7.5 ms)
Inside Handler#handleCallbacks (i. e. Handler#post):
Creates a WebView, sets it up, adds to Window and evaluates JS (~27 ms)
Gets packageInfo (IPC), creates a WebView, sets it up, asks to loadUrl, checks permissions (IPC), parses JSON, gets packageInfo one more time, introspects resources via Resources#getIdentifier, gets activityInfo (IPC) (sum ~40 ms)
Obviously, all these heavyweight operations are leading to jerky scroll.
What should I do to avoid lags or what I'm possibly doing wrong? Why don't AdMob native AD is as slow in other applications I've seen?
Using AdMob/GMS v. 10 and/or 11, but 16 shows the same lags.
All measurements are taken with Nanoscope on their special emulator, but lag on a real not-so-old Nexus 5 is even worse.
We're seeing a number of TimeoutExceptions in GcWatcher.finalize, BinderProxy.finalize, and PlainSocketImpl.finalize. 90+% of them happen on Android 4.3. We're getting reports of this from Crittercism from users out in the field.
The error is a variation of: "com.android.internal.BinderInternal$GcWatcher.finalize() timed out after 10 seconds"
java.util.concurrent.TimeoutException: android.os.BinderProxy.finalize() timed out after 10 seconds
at android.os.BinderProxy.destroy(Native Method)
at android.os.BinderProxy.finalize(Binder.java:459)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:187)
at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:170)
at java.lang.Thread.run(Thread.java:841)
So far we haven't had any luck reproducing the problem in house or figuring out what might have caused it.
Any ideas what can cause this?
Any idea how to debug this and find out which part of the app causes this?
Anything that sheds light on the issue helps.
More Stacktraces:
1 android.os.BinderProxy.destroy
2 android.os.BinderProxy.finalize Binder.java, line 482
3 java.lang.Daemons$FinalizerDaemon.doFinalize Daemons.java, line 187
4 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
5 java.lang.Thread.run Thread.java, line 841
2
1 java.lang.Object.wait
2 java.lang.Object.wait Object.java, line 401
3 java.lang.ref.ReferenceQueue.remove ReferenceQueue.java, line 102
4 java.lang.ref.ReferenceQueue.remove ReferenceQueue.java, line 73
5 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
6 java.lang.Thread.run
3
1 java.util.HashMap.newKeyIterator HashMap.java, line 907
2 java.util.HashMap$KeySet.iterator HashMap.java, line 913
3 java.util.HashSet.iterator HashSet.java, line 161
4 java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers ThreadPoolExecutor.java, line 755
5 java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers ThreadPoolExecutor.java, line 778
6 java.util.concurrent.ThreadPoolExecutor.shutdown ThreadPoolExecutor.java, line 1357
7 java.util.concurrent.ThreadPoolExecutor.finalize ThreadPoolExecutor.java, line 1443
8 java.lang.Daemons$FinalizerDaemon.doFinalize Daemons.java, line 187
9 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
10 java.lang.Thread.run
4
1 com.android.internal.os.BinderInternal$GcWatcher.finalize BinderInternal.java, line 47
2 java.lang.Daemons$FinalizerDaemon.doFinalize Daemons.java, line 187
3 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
4 java.lang.Thread.run
Full disclosure - I'm the author of the previously mentioned talk in TLV DroidCon.
I had a chance to examine this issue across many Android applications, and discuss it with other developers who encountered it - and we all got to the same point: this issue cannot be avoided, only minimized.
I took a closer look at the default implementation of the Android Garbage collector code, to understand better why this exception is thrown and on what could be the possible causes. I even found a possible root cause during experimentation.
The root of the problem is at the point a device "Goes to Sleep" for a while - this means that the OS has decided to lower the battery consumption by stopping most User Land processes for a while, and turning Screen off, reducing CPU cycles, etc. The way this is done - is on a Linux system level where the processes are Paused mid run. This can happen at any time during normal Application execution, but it will stop at a Native system call, as the context switching is done on the kernel level. So - this is where the Dalvik GC joins the story.
The Dalvik GC code (as implemented in the Dalvik project in the AOSP site) is not a complicated piece of code. The basic way it work is covered in my DroidCon slides. What I did not cover is the basic GC loop - at the point where the collector has a list of Objects to finalize (and destroy). The loop logic at the base can be simplified like this:
take starting_timestamp,
remove object for list of objects to release,
release object - finalize() and call native destroy() if required,
take end_timestamp,
calculate (end_timestamp - starting_timestamp) and compare against a hard coded timeout value of 10 seconds,
if timeout has reached - throw the java.util.concurrent.TimeoutException and kill the process.
Now consider the following scenario:
Application runs along doing its thing.
This is not a user facing application, it runs in the background.
During this background operation, objects are created, used and need to be collected to release memory.
Application does not bother with a WakeLock - as this will affect the battery adversely, and seems unnecessary.
This means the Application will invoke the GC from time to time.
Normally the GC runs is completed without a hitch.
Sometimes (very rarely) the system will decide to sleep in the middle of the GC run.
This will happen if you run your application long enough, and monitor the Dalvik memory logs closely.
Now - consider the timestamp logic of the basic GC loop - it is possible for the device to start the run, take a start_stamp, and go to sleep at the destroy() native call on a system object.
When it wakes up and resumes the run, the destroy() will finish, and the next end_stamp will be the time it took the destroy() call + the sleep time.
If the sleep time was long (more than 10 seconds), the java.util.concurrent.TimeoutException will be thrown.
I have seen this in the graphs generated from the analysis python script - for Android System Applications, not just my own monitored apps.
Collect enough logs and you will eventually see it.
Bottom line:
The issue cannot be avoided - you will encounter it if your app runs in the background.
You can mitigate by taking a WakeLock, and prevent the device from sleeping, but that is a different story altogether, and a new headache, and maybe another talk in another con.
You can minimize the problem by reducing GC calls - making the scenario less likely (tips are in the slides).
I have not yet had the chance to go over the Dalvik 2 (a.k.a ART) GC code - which boasts a new Generational Compacting feature, or performed any experiments on an Android Lollipop.
Added 7/5/2015:
After reviewing the Crash reports aggregation for this crash type, it looks like these crashes from version 5.0+ of Android OS (Lollipop with ART) only account for 0.5% of this crash type. This means that the ART GC changes has reduced the frequency of these crashes.
Added 6/1/2016:
Looks like the Android project has added a lot of info on how the GC works in Dalvik 2.0 (a.k.a ART).
You can read about it here - Debugging ART Garbage Collection.
It also discusses some tools to get information on the GC behavior for your app.
Sending a SIGQUIT to your app process will essentially cause an ANR, and dump the application state to a log file for analysis.
We see this constantly, all over our app, using Crashlytics. The crash usually happens way down in platform code. A small sampling:
android.database.CursorWindow.finalize() timed out after 10 seconds
java.util.regex.Matcher.finalize() timed out after 10 seconds
android.graphics.Bitmap$BitmapFinalizer.finalize() timed out after 10 seconds
org.apache.http.impl.conn.SingleClientConnManager.finalize() timed out after 10 seconds
java.util.concurrent.ThreadPoolExecutor.finalize() timed out after 10 seconds
android.os.BinderProxy.finalize() timed out after 10 seconds
android.graphics.Path.finalize() timed out after 10 seconds
The devices on which this happens are overwhelmingly (but not exclusively) devices manufactured by Samsung. That could just mean that most of our users are using Samsung devices; alternately it could indicate a problem with Samsung devices. I'm not really sure.
I suppose this doesn't really answer your questions, but I just wanted to reinforce that this seems quite common, and is not specific to your application.
I found some slides about this issue.
http://de.slideshare.net/DroidConTLV/android-crash-analysis-and-the-dalvik-garbage-collector-tools-and-tips
In this slides the author tells that it seems to be a problem with GC, if there are a lot of objects or huge objects in heap. The slide also include a reference to a sample app and a python script to analyze this issue.
https://github.com/oba2cat3/GCTest
https://github.com/oba2cat3/logcat2memorygraph
Furthermore I found a hint in comment #3 on this side: https://code.google.com/p/android/issues/detail?id=53418#c3
Here is an effective solution from didi to solve this problem, Since this bug is very common and difficult to find the cause, It looks more like a system problem, Why can't we ignore it directly?Of course we can ignore it, Here is the sample code:
final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler =
Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread t, Throwable e) {
if (t.getName().equals("FinalizerWatchdogDaemon") && e instanceof TimeoutException) {
} else {
defaultUncaughtExceptionHandler.uncaughtException(t, e);
}
}
});
By setting a special default uncaught exception handler, application can change the way in which uncaught exceptions are handled for those threads that would already accept whatever default behavior the system provided. When an uncaught TimeoutException is thrown from a thread named FinalizerWatchdogDaemon, this special handler will block the handler chain, the system handler will not be called, so crash will be avoided.
Through practice, no other bad effects were found. The GC system is still working, timeouts are alleviated as CPU usage decreases.
For more details see: https://mp.weixin.qq.com/s/uFcFYO2GtWWiblotem2bGg
We solved the problem by stopping the FinalizerWatchdogDaemon.
public static void fix() {
try {
Class clazz = Class.forName("java.lang.Daemons$FinalizerWatchdogDaemon");
Method method = clazz.getSuperclass().getDeclaredMethod("stop");
method.setAccessible(true);
Field field = clazz.getDeclaredField("INSTANCE");
field.setAccessible(true);
method.invoke(field.get(null));
}
catch (Throwable e) {
e.printStackTrace();
}
}
You can call the method in Application's lifecycle, like attachBaseContext().
For the same reason, you also can specific the phone's manufacture to fix the problem, it's up to you.
Broadcast Receivers timeout after 10 seconds. Possibly your doing an asynchronous call (wrong) from a broadcast receiver and 4.3 actually detects it.
One thing which is invariably true is that at this time, the device would be suffocating for some memory (which is usually the reason for GC to most likely get triggered).
As mentioned by almost all authors earlier, this issue surfaces when Android tries to run GC while the app is in background. In most of the cases where we observed it, user paused the app by locking their screen.
This might also indicate memory leak somewhere in the application, or the device being too loaded already.
So the only legitimate way to minimize it is:
to ensure there are no memory leaks, and
to reduce the memory footprint of the app in general.
try {
Class<?> c = Class.forName("java.lang.Daemons");
Field maxField = c.getDeclaredField("MAX_FINALIZE_NANOS");
maxField.setAccessible(true);
maxField.set(null, Long.MAX_VALUE);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
The finalizeQueue may be too long
i think that java may require GC.SuppressFinalize() & GC.ReRegisterForFinalize() to let user reduce the finalizedQueue length explicitly
if the JVM' source code is available, may implement these method ourself, such as android ROM maker
It seems like a Android Runtime bug. There seems to be finalizer that runs in its separate thread and calls finalize() method on objects if they are not in the current frame of the stacktrace.
For example following code(created to verify this issue) ended with the crash.
Let's have some cursor that do something in finalize method(e.g. SqlCipher ones, do close() which locks to the database that is currently in use)
private static class MyCur extends MatrixCursor {
public MyCur(String[] columnNames) {
super(columnNames);
}
#Override
protected void finalize() {
super.finalize();
try {
for (int i = 0; i < 1000; i++)
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
And we do some long running stuff having opened cursor:
for (int i = 0; i < 7; i++) {
new Thread(new Runnable() {
#Override
public void run() {
MyCur cur = null;
try {
cur = new MyCur(new String[]{});
longRun();
} finally {
cur.close();
}
}
private void longRun() {
try {
for (int i = 0; i < 1000; i++)
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
This causes following error:
FATAL EXCEPTION: FinalizerWatchdogDaemon
Process: la.la.land, PID: 29206
java.util.concurrent.TimeoutException: MyCur.finalize() timed out after 10 seconds
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:371)
at java.lang.Thread.sleep(Thread.java:313)
at MyCur.finalize(MessageList.java:1791)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:222)
at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:209)
at java.lang.Thread.run(Thread.java:762)
The production variant with SqlCipher is very similiar:
12-21 15:40:31.668: E/EH(32131): android.content.ContentResolver$CursorWrapperInner.finalize() timed out after 10 seconds
12-21 15:40:31.668: E/EH(32131): java.util.concurrent.TimeoutException: android.content.ContentResolver$CursorWrapperInner.finalize() timed out after 10 seconds
12-21 15:40:31.668: E/EH(32131): at java.lang.Object.wait(Native Method)
12-21 15:40:31.668: E/EH(32131): at java.lang.Thread.parkFor$(Thread.java:2128)
12-21 15:40:31.668: E/EH(32131): at sun.misc.Unsafe.park(Unsafe.java:325)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.LockSupport.park(LockSupport.java:161)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:840)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:873)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1197)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:200)
12-21 15:40:31.668: E/EH(32131): at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
12-21 15:40:31.668: E/EH(32131): at net.sqlcipher.database.SQLiteDatabase.lock(SourceFile:518)
12-21 15:40:31.668: E/EH(32131): at net.sqlcipher.database.SQLiteProgram.close(SourceFile:294)
12-21 15:40:31.668: E/EH(32131): at net.sqlcipher.database.SQLiteQuery.close(SourceFile:136)
12-21 15:40:31.668: E/EH(32131): at net.sqlcipher.database.SQLiteCursor.close(SourceFile:510)
12-21 15:40:31.668: E/EH(32131): at android.database.CursorWrapper.close(CursorWrapper.java:50)
12-21 15:40:31.668: E/EH(32131): at android.database.CursorWrapper.close(CursorWrapper.java:50)
12-21 15:40:31.668: E/EH(32131): at android.content.ContentResolver$CursorWrapperInner.close(ContentResolver.java:2746)
12-21 15:40:31.668: E/EH(32131): at android.content.ContentResolver$CursorWrapperInner.finalize(ContentResolver.java:2757)
12-21 15:40:31.668: E/EH(32131): at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:222)
12-21 15:40:31.668: E/EH(32131): at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:209)
12-21 15:40:31.668: E/EH(32131): at java.lang.Thread.run(Thread.java:762)
Resume: Close cursors ASAP. At least on Samsung S8 with Android 7 where the issue have been seen.
For classes that you create (ie. are not part of the Android) its possible to avoid the crash completely.
Any class that implements finalize() has some unavoidable probability of crashing as explained by #oba. So instead of using finalizers to perform cleanup, use a PhantomReferenceQueue.
For an example check out the implementation in React Native: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/jni/DestructorThread.java
In my app there are two activities viz A and B.
Activity A:
It has a list which displays information about an item briefly. When an item on A is clicked, it loads Activity B.
Activity B:
It has two web views and several listviews. Top of the activity is occupied by header web view. Lower part of activity has tab host, this hosts the second web view in first tab and remaining tab each hosts a list view.
Problem:
When user navigates from A to B, the heap size increases significantly. Even after I navigate back from B to A, heap size continues to be the same. There is not even a byte decrease, in fact it increases sometimes. Yes its because of these web views. I have read about web view memory leaks on SO and other sites. I followed approach mentioned here
Added web view dynamically inside a container[RelativeLayout]. Removed webview on activities onDestroy
Sub classed web view as suggested in the SO article
Cleared web view cache every time etc
No matter what heap size does not come back to what it was before navigating to B.
Really appreciate if someone can guide me to a possible fix
Note:
I have already read this and followed this issue on SO.
Memory leak in WebView
Edit:
I have tried without web views in B and the increase in heap size is very very less around 0.5 MB but with web views it creases by 4-5 MB
Heap size logs ( got by following suggestion mentioned here )
onCreate B
debug.heap native: allocated 4.11MB of 4.17MB (0.01MB free) in []
debug.memory: allocated: 12.00MB of 96.00MB (1.00MB free)
onDestroy B
debug.heap native: allocated 8.66MB of 10.08MB (0.48MB free) in []
debug.memory: allocated: 12.00MB of 96.00MB (1.00MB free)
on Resume A
debug.heap native: allocated 7.94MB of 10.08MB (1.32MB free) in []
debug.memory: allocated: 12.00MB of 96.00MB (0.00MB free)
I cross checked the numbers by taking heap dump before and after B is started and destoryed, heap size are quite close to the one I get it in logs
I followed series of steps and got the memory footprint reduced. This is what I did,
instead of creating webview statically via xml I now create webview programmatically and add it to a container. Once webview is no longer needed I remove the webview from the container then I remove all views from webview and call destroy on it. Eventually memory allocated reduces.
private void releaseWebView() {
webViewContainerRelView.removeAllViews();
if(mWebView != null){
mWebView.setTag(null);
mWebView.clearHistory();
mWebView.removeAllViews();
mWebView.clearView();
mWebView.destroy();
mWebView = null;
}
}
I call releaseWebView() from onDetachedFromWindow method of Activity as below.
#Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
releaseWebViews();
}
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/
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".