Есть ли способ заставить процесс Android создать дамп кучи при OutOfMemoryError?

#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;
    }
}