Generating Native Heap Dumps

As described in chapter Creating Native Images in GraalVM it is possible to create highly performing applications. It is also possible to monitor these apps using Agent and generate Java heap dumps from within the native image application. This functionality is available with GraalVM Enterprise - OTN download. It is not available in GraalVM open source version available on GitHub.

GraalVM native images do not implement JVMTI agent and it is not possible to trigger heap dump creation using tools like Visual VM or jmap. But it is possible to build native image for your application to handle signals and then get a heap dump when the app receives SIGUSR1 signal. Another possibility is to add a special method to your application which will generate the heap dump at certain point in the lifetime of your application. E.g. when certain conditions are met when running native image application then your application code triggers heap dump creation. We will show both possibilities in this chapter.

Handling SIGUSR1 Signal

Following Java example is a simple threaded application which runs for 60 seconds. There is enough time to get its PID and send SIGUSR1 signal which generates a heap dump into application working directory. Save following code as SVMHeapDump.java file on your disk:

import java.text.DateFormat;
import java.util.Date;

public class SVMHeapDump extends Thread {
    static int i = 0;
    static int runs = 60;
    static int sleepTime = 1000;
    @Override
    public void run() {
        System.out.println(DateFormat.getDateTimeInstance().format(new Date()) + ": Thread started, it will run for " + runs + " seconds");
        while (i < runs){
            System.out.println("Sleeping for " + (runs-i) + " seconds." );
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException ie){
                System.out.println("Sleep interrupted.");
            }
            i++;
        }
    }
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws InterruptedException {
        // Do some manipulations so we have something to dump
        StringBuffer sb1 = new StringBuffer(100);
        sb1.append(DateFormat.getDateTimeInstance().format(new Date()));
        sb1.append(": Hello GraalVM native image developer! \nGet PID of this process: ");
        sb1.append("'ps -C svmheapdump -o pid= '\n");
        sb1.append("then send it signal: ");
        sb1.append("'kill -SIGUSR1 <pid_printed_above>' \n");
        sb1.append("to get heap dump generated into working directory.\n");
        sb1.append("Starting thread!");
        System.out.println(sb1);
        SVMHeapDump t = new SVMHeapDump();
        t.start();
        while (t.isAlive()) {
            t.join(0);
        }
        sb1 = new StringBuffer(100);
        sb1.append(DateFormat.getDateTimeInstance().format(new Date()));
        sb1.append(": Thread finished after: ");
        sb1.append(i);
        sb1.append(" iterations.");
        System.out.println(sb1);
    }
}

Building an Example

Compile SVMHeapDump.java as following

$ javac SVMHeapDump.java

If you run it on java, you will see it runs for 60 seconds then finishes. We will now look how to tell native-image to generate binary which will accept SIGUSR1 signal to produce a heap dump.

Building Native Image

When using GraalVM Enterprise Edition, downloaded from the Oracle Technology Network, you need to specify -H:+AllowVMInspection option for GraalVM native-image tool. See following example:

$ $GRAALVM_HOME/bin/native-image SVMHeapDump -H:+AllowVMInspection
Build on Server(pid: 31386, port: 26682)
   classlist:     172.62 ms
       (cap):     865.71 ms
       setup:   1,103.32 ms
  (typeflow):   2,003.86 ms
   (objects):     664.40 ms
  (features):      26.82 ms
    analysis:   2,779.99 ms
    universe:     152.09 ms
     (parse):     311.20 ms
    (inline):     471.03 ms
   (compile):   2,405.97 ms
     compile:   3,458.48 ms
       image:     540.78 ms
       write:     464.04 ms
     [total]:   8,714.06 ms

The native-image tool analyzes existing SVMHeapDump.class and creates from it an executable file. When the command completes, svmheapdump is created in current directory.

Run the Application and Get Heap Dump

1. Run the App

$ ./svmheapdump
Feb 22, 2018 2:56:30 PM: Hello GraalVM native image developer!
Get PID of this process: 'ps -C svmheapdump -o pid= '
then send it signal: 'kill -SIGUSR1 <pid_printed_above>'
to get heap dump generated into working directory.
Starting thread!
Feb 22, 2018 2:56:30 PM: Thread started, it will run for 60 seconds
Sleeping for 60 seconds.

2. Generate Heap Dump

Open the 2nd terminal to get PID of the running svmheapdump application using a command like ps -C svmheapdump -o pid= for Linux OS and pgrep svmheapdump for Mac OS. PID is printed, e.g. 100, which is then used in sending the signal to the running app:

kill -SIGUSR1 100

Heap dump is available at the working directory while application continues running.

Java API Example

The following Java example shows how Java heap dump can be generated from within a running application using Compiler.command() after some condition is met. The condition to generate a heap dump is to provide an option on command line. Save following Java code as SVMHeapDumpAPI.java. The application creates some data to have something to dump, checks command line if the heap dump has to be created and then in method createHeapDump() creates the actual heap dump performing checks for file existence.

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.util.Date;

public class SVMHeapDumpAPI {

    private static final String HEAP_DUMP_COMMAND = "HeapDump.dumpHeap(FileOutputStream, Boolean)Boolean";

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // Do some manipulations so we have something to dump
        StringBuffer sb1 = new StringBuffer(100);
        sb1.append(DateFormat.getDateTimeInstance().format(new Date()));
        sb1.append(": Hello GraalVM native image developer. \nYour command line options are: ");

        if (args.length > 0) {
            sb1.append(args[0]);
            System.out.println(sb1);
            if (args[0].equalsIgnoreCase("--heapdump")){
                createHeapDump();
            }
        } else {
                sb1.append("None");
                System.out.println(sb1);
        }
     }

    /**
     * Generate heap dump and save it into temp file.
     */
    private static void createHeapDump() {
        boolean heapDumpCreated = false;
        try {
            File file = File.createTempFile("SVMHeapDump-", ".hprof");
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            // Create heap dump
            final Object[] args = new Object[]{HEAP_DUMP_COMMAND, fileOutputStream, Boolean.TRUE};
            final Object resultObject = Compiler.command(args);
            // Following code checks if heap dump was created using return value
            if (resultObject instanceof Boolean) {
                heapDumpCreated = ((Boolean) resultObject).booleanValue();
            }
            fileOutputStream.close();

            if (heapDumpCreated){
                System.out.println("  Heap dump created " + file.getAbsolutePath() + ", size: " + file.length());
            } else {
                // Delete the file to not pollute disk with empty files.
                System.out.println("  Heap dump creation failed.");
                file.delete();
            }
        } catch (IOException ioe) {
            System.out.println("  Caught IOException.");
        }
    }
}

Building an Example

In the next step, compile SVMHeapDumpAPI.java. It is good to define an environment variable pointing to your GraalVM installation, e.g. export GRAALVM_HOME=/usr/graalvm. Then compile using following command:

$ javac SVMHeapDumpAPI.java

Build Native Image

Heap dump creation will work after the native image is created from a class file. Use the following command:

$ $GRAALVM_HOME/bin/native-image SVMHeapDumpAPI
   Build on Server(pid: 31386, port: 26682)*
   classlist:     804.50 ms
       (cap):   1,645.69 ms
       setup:   2,439.03 ms
  (typeflow):   3,636.09 ms
   (objects):   1,979.26 ms
  (features):      57.29 ms
    analysis:   5,775.18 ms
    universe:     241.79 ms
     (parse):     849.48 ms
    (inline):   1,042.78 ms
   (compile):   7,632.91 ms
     compile:   9,971.89 ms
       image:   1,441.42 ms
       write:     637.83 ms
     [total]:  21,388.04 ms

When the command completes, svmheapdumpapi is created in the current directory.

Run the Application and Check Heap Dump

Now you can run your native image application and generate heap dump from it with the output similar to one below:

$ ./svmheapdumpapi --heapdump
Oct 3, 2017 10:33:54 AM: Hello GraalVM native image developer.
Your command line options are: --heapdump
  Heap dump created /tmp/SVMHeapDump-7779460659275234903.hprof, size: 4436516

Resulting heap dump can be then opened with Graal VisualVM tool like any other Java heap dump.