Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to have an Android process produce a heap dump on an OutOfMemoryError?

The sun JVM supports a -XX:+HeapDumpOnOutOfMemoryError option to dump heap whenever a java process runs out of heap.

Is there a similar option on Android that will make an android app dump heap on an OutOfMemoryException? It can be difficult to try to time it properly when using DDMS manually.

like image 954
emmby Avatar asked May 25 '11 22:05

emmby


People also ask

How do you get heap dump on OutOfMemoryError?

In order to capture a heap dump automatically, we need to add the HeapDumpOnOutOfMemoryError command-line option that generates a heap dump when a java. lang. OutOfMemoryError is thrown.

How do I make an automatic heap dump?

The -XX:+HeapDumpOnOutOfMemoryError command-line option tells the HotSpot VM to generate a heap dump when an allocation from the Java heap or the permanent generation cannot be satisfied.

What is heap dump in Android?

A heap dump is used to find which object in our app is using memory at the time of capturing the heap dump. A heap dump can help us in identifying memory leaks by showing objects that are still in memory and that is not going to be used by the program.

How do you take a heap dump?

Heap dump = memory contents for the JVM process output to a binary file. To take a thread dump on Windows, CTRL + BREAK if your JVM is the foreground process is the simplest way. If you have a unix-like shell on Windows like Cygwin or MobaXterm, you can use kill -3 {pid} like you can in Unix.


3 Answers

To expand upon CommonsWare's answer:

I have no idea if this works, but you might try adding a top-level exception handler, and in there asking for a heap dump if it is an OutOfMemoryError.

I followed his suggestion successfully in my own Android app with the following code:

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());
    }
}

After the dump is created, you need to copy it from your phone to your PC: Click "Turn on USB storage" on the phone, find the file and copy it to your hard drive.

Then, if you want to use the Eclipse Memory Analyzer (MAT) to analyze the file, you will need to covert the file: hprof-conv.exe dump.hprof dump-conv.hprof (hprof-conv is located under android-sdk/tools)

Finally, open the dump-conv.hprof file with MAT

like image 77
jfritz42 Avatar answered Oct 19 '22 18:10

jfritz42


I have no idea if this works, but you might try adding a top-level exception handler, and in there asking for a heap dump if it is an OutOfMemoryError.

like image 33
CommonsWare Avatar answered Oct 19 '22 18:10

CommonsWare


Here is an improved version. On top of the original implementation this implementation also supports:

  • Catching Out of Memory errors on all threads (not only on the main thread)
  • Identifying Out of Memory errors even when it's hidden inside a different error. On some instances the Out of Memory error is encapsulated inside a Runtime error.
  • Invoking the original default uncaught exception handler too.
  • Only works in DEBUG builds.

Usage: Call the static initialize method in your Application class in the onCreate method.

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;
    }
}
like image 45
Guy B. Avatar answered Oct 19 '22 17:10

Guy B.