當Java進程用完堆時,Sun JVM支持-XX:+HeapDumpOnOutOfMemoryError
選項來轉儲堆。有沒有辦法讓Android進程在OutOfMemoryError上產生堆轉儲?
Android上有類似的選項,會使OutOfMemoryException上的android應用程序轉儲堆?手動使用DDMS時,嘗試正確計時可能很困難。
當Java進程用完堆時,Sun JVM支持-XX:+HeapDumpOnOutOfMemoryError
選項來轉儲堆。有沒有辦法讓Android進程在OutOfMemoryError上產生堆轉儲?
Android上有類似的選項,會使OutOfMemoryException上的android應用程序轉儲堆?手動使用DDMS時,嘗試正確計時可能很困難。
要在CommonsWare的回答擴大:
我不知道這是否正常工作,但你可以嘗試adding a top-level exception handler,並在那裏asking for a heap dump如果它是一個
OutOfMemoryError
。
我跟着他的建議成功地在自己的Android應用程序用下面的代碼:
public class MyActivity extends Activity {
public static class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.e("UncaughtException", "Got an uncaught exception: "+ex.toString());
if(ex.getClass().equals(OutOfMemoryError.class))
{
try {
android.os.Debug.dumpHprofData("/sdcard/dump.hprof");
} catch (IOException e) {
e.printStackTrace();
}
}
ex.printStackTrace();
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.currentThread().setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
}
}
創建轉儲後,您需要將其從手機複製到PC:點擊「打開USB存儲器「,找到該文件並將其複製到您的硬盤驅動器。
然後,如果你想使用Eclipse的內存分析器(MAT)來分析文件,你需要隱蔽的文件:hprof-conv.exe dump.hprof dump-conv.hprof
(HPROF-CONV位於android-sdk/tools
下)
最後,打開dump-conv.hprof
文件與MAT
我不知道這是否可行,但您可以嘗試adding a top-level exception handler,並在asking for a heap dump如果它是OutOfMemoryError
。
這是一個改進版本。在原有執行此之上實現還支持:
用法:在onCreate方法的Application類中調用靜態initialize
方法。
package test;
import java.io.File;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import android.os.Environment;
import android.util.Log;
import com.example.test1.BuildConfig;
public class OutOfMemoryDumper implements Thread.UncaughtExceptionHandler {
private static final String TAG = "OutOfMemoryDumper";
private static final String FILE_PREFIX = "OOM-";
private static final OutOfMemoryDumper instance = new OutOfMemoryDumper();
private UncaughtExceptionHandler oldHandler;
/**
* Call this method to initialize the OutOfMemoryDumper when your
* application is first launched.
*/
public static void initialize() {
// Only works in DEBUG builds
if (BuildConfig.DEBUG) {
instance.setup();
}
}
/**
* Keep the constructor private to ensure we only have one instance
*/
private OutOfMemoryDumper() {
}
private void setup() {
// Checking if the dumper isn't already the default handler
if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof OutOfMemoryDumper)) {
// Keep the old default handler as we are going to use it later
oldHandler = Thread.getDefaultUncaughtExceptionHandler();
// Redirect uncaught exceptions to this class
Thread.setDefaultUncaughtExceptionHandler(this);
}
Log.v(TAG, "OutOfMemoryDumper is ready");
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.e(TAG, "Uncaught exception: " + ex);
Log.e(TAG, "Caused by: " + ex.getCause());
// Checking if the exception or the original cause for the exception is
// an out of memory error
if (ex.getClass().equals(OutOfMemoryError.class)
|| (ex.getCause() != null && ex.getCause().getClass()
.equals(OutOfMemoryError.class))) {
// Checking if the external storage is mounted and available
if (isExternalStorageWritable()) {
try {
// Building the path to the new file
File f = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
long time = System.currentTimeMillis();
String dumpPath = f.getAbsolutePath() + "/" + FILE_PREFIX
+ time + ".hprof";
Log.i(TAG, "Dumping hprof data to: " + dumpPath);
android.os.Debug.dumpHprofData(dumpPath);
} catch (IOException ioException) {
Log.e(TAG,"Failed to dump hprof data. " + ioException.toString());
ioException.printStackTrace();
}
}
}
// Invoking the original default exception handler (if exists)
if (oldHandler != null) {
Log.v(TAG, "Invoking the original uncaught exception handler");
oldHandler.uncaughtException(thread, ex);
}
}
/**
* Checks if external storage is available for read and write
*
* @return true if the external storage is available
*/
private boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
Log.w(TAG,"The external storage isn't available. hprof data won't be dumped! (state=" + state + ")");
return false;
}
}
我注意到,這經常起作用,但有時不會導致轉儲。不知道爲什麼。 – emmby 2013-02-01 00:11:01
我也做了同樣的事情,有時轉儲文件已損壞(或者至少hprof-conv似乎認爲是這樣)。我只會在轉儲之前添加一個建議來執行System.gc()。 – snowdragon 2013-08-08 20:13:18
可能是在寫入文件之前終止了應用程序,或者在完成之前終止了應用程序(更糟糕)。 – 2014-03-21 03:47:23