#android #dump #out-of-memory #hprof
#Android #дамп #нехватка памяти #hprof
Вопрос:
Sun JVM поддерживает -XX: HeapDumpOnOutOfMemoryError
опцию сброса кучи всякий раз, когда у Java-процесса заканчивается куча.
Есть ли аналогичная опция на Android, которая приведет к созданию кучи дампа приложения Android при OutOfMemoryException? При использовании DDMS вручную может быть сложно правильно рассчитать время.
Ответ №1:
Чтобы расширить ответ CommonsWare:
Я понятия не имею, работает ли это, но вы могли бы попробовать добавить обработчик исключений верхнего уровня и там запросить дамп кучи, если это
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());
}
}
После создания дампа вам нужно скопировать его со своего телефона на свой компьютер: нажмите «Включить USB-накопитель» на телефоне, найдите файл и скопируйте его на свой жесткий диск.
Затем, если вы хотите использовать анализатор памяти Eclipse (MAT) для анализа файла, вам нужно будет скрыть файл: hprof-conv.exe dump.hprof dump-conv.hprof
(hprof-conv находится под android-sdk/tools
)
Наконец, откройте dump-conv.hprof
файл с помощью MAT
Комментарии:
1. Я заметил, что это часто работает, но иногда не приводит к дампу. Не уверен, почему.
2. Я также делал то же самое, и иногда файл дампа поврежден (или, по крайней мере, hprof-conv, похоже, так думает). Я бы только добавил предложение выполнить System.gc() перед сбросом.
3. Вероятно, приложение было завершено до того, как файл был записан, или (что еще хуже) до его завершения.
4. Возможно, это проблема с куриным яйцом, у вас закончилась память, поэтому он не может правильно создать дамп. Однако, возможно, это не удалось из-за неудачного выделения большого объема, и хотя он не смог выделить запрошенный объем, у него есть память для создания дампа.
Ответ №2:
Я понятия не имею, работает ли это, но вы могли бы попробовать добавить обработчик исключений верхнего уровня и там запросить дамп кучи, если это OutOfMemoryError
.
Ответ №3:
Вот улучшенная версия. В дополнение к исходной реализации эта реализация также поддерживает:
- Обнаружение ошибок нехватки памяти во всех потоках (не только в основном потоке)
- Идентификация ошибок нехватки памяти, даже если они скрыты внутри другой ошибки. В некоторых случаях ошибка нехватки памяти инкапсулируется в ошибку времени выполнения.
- Также вызывая исходный обработчик неперехваченных исключений по умолчанию.
- Работает только в отладочных сборках.
Использование: Вызовите статический initialize
метод в вашем классе приложения в методе onCreate.
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 amp;amp; 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;
}
}