戻る

ネイティブ実行ファイルからのヒープダンプの作成

実行中の実行ファイルの実行状況を監視するために、ヒープダンプを作成できます。他のJavaヒープダンプと同様に、VisualVMツールで開くことができます。

ヒープダンプのサポートを有効にするには、ネイティブ実行ファイルを--enable-monitoring=heapdumpオプションを付けてビルドする必要があります。ヒープダンプは、以下の方法で作成できます。

  1. VisualVMでヒープダンプを作成します。
  2. コマンドラインオプション-XX:+HeapDumpOnOutOfMemoryErrorを使用すると、ネイティブ実行ファイルがJavaヒープメモリを使い果たしたときにヒープダンプを作成できます。
  3. -XX:+DumpHeapAndExitコマンドラインオプションを使用して、ネイティブ実行ファイルの初期ヒープをダンプします。
  4. 実行時にアプリケーションにSIGUSR1シグナルを送信して、ヒープダンプを作成します。
  5. org.graalvm.nativeimage.VMRuntime#dumpHeap APIを使用して、プログラムでヒープダンプを作成します。

すべてのアプローチについて、以下で説明します。

注:デフォルトでは、ヒープダンプは現在の作業ディレクトリに作成されます。-XX:HeapDumpPathオプションを使用して、代替のファイル名またはディレクトリを指定できます。例えば
./helloworld -XX:HeapDumpPath=$HOME/helloworld.hprof

注:Microsoft Windowsプラットフォームでは、ヒープダンプを作成できません。

VisualVMでヒープダンプを作成する

ヒープダンプを作成する便利な方法は、VisualVMを使用することです。このためには、--enable-monitoringオプションにjvmstatを追加する必要があります(例:--enable-monitoring=heapdump,jvmstat)。これにより、VisualVMは実行中のネイティブイメージプロセスを検出してリストできます。その後、アプリケーションがJVMで実行されている場合と同じ方法でヒープダンプをリクエストできます(例:プロセスを右クリックし、[ヒープダンプ]を選択します)。

OutOfMemoryErrorでヒープダンプを作成する

ネイティブ実行ファイルがJavaヒープメモリを使い果たしたためにOutOfMemoryErrorをスローした場合にヒープダンプを取得するには、-XX:+HeapDumpOnOutOfMemoryErrorオプションを指定してアプリケーションを起動します。ヒープダンプは、svm-heapdump-<PID>-OOME.hprofという名前のファイルに作成されます。例えば

./mem-leak-example -XX:+HeapDumpOnOutOfMemoryError
Dumping heap to svm-heapdump-67799-OOME.hprof ...
Heap dump file created [10046752 bytes in 0.49 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Garbage-collected heap size exceeded.

ネイティブ実行ファイルの初期ヒープをダンプする

-XX:+DumpHeapAndExitコマンドラインオプションを使用して、ネイティブ実行ファイルの初期ヒープをダンプします。これは、ネイティブイメージのビルドプロセスが実行ファイルのヒープに割り当てたオブジェクトを特定するのに役立ちます。HelloWorldの例では、次のようにオプションを使用します。

$JAVA_HOME/bin/native-image HelloWorld --enable-monitoring=heapdump
./helloworld -XX:+DumpHeapAndExit
Heap dump created at '/path/to/helloworld.hprof'.

SIGUSR1でヒープダンプを作成する(Linux / macOSのみ)

注:これには、共有ライブラリのビルド時を除いてデフォルトで有効になっているSignal APIが必要です。

次の例は、60秒間実行される単純なマルチスレッドJavaアプリケーションです。これにより、SIGUSR1シグナルを送信するのに十分な時間が与えられます。アプリケーションはシグナルを処理し、アプリケーションの作業ディレクトリにヒープダンプを作成します。ヒープダンプには、静的変数CROWDによって参照されるPersonCollectionが含まれます。

SIGUSR1シグナルを受信したときにヒープダンプを生成するネイティブ実行ファイルをビルドするには、次の手順に従います。

前提条件

GraalVM JDKがインストールされていることを確認してください。開始する最も簡単な方法は、SDKMAN!を使用することです。その他のインストールオプションについては、ダウンロードセクションをご覧ください。

  1. 次のコードを*SVMHeapDump.java*という名前のファイルに保存します。
    import java.nio.charset.Charset;
    import java.text.DateFormat;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Date;
    import java.util.Random;
    import org.graalvm.nativeimage.ProcessProperties;
    
    public class SVMHeapDump extends Thread {
        static Collection<Person> CROWD = new ArrayList<>();
        static DateFormat DATE_FORMATTER = DateFormat.getDateTimeInstance();
        static int i = 0;
        static int runs = 60;
        static int sleepTime = 1000;
        @Override
        public void run() {
            System.out.println(DATE_FORMATTER.format(new Date()) + ": Thread started, it will run for " + runs + " seconds");
            while (i < runs) {
                // Add a new person to the collection
                CROWD.add(new Person());
                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 {
            // Add objects to the heap
            for (int i = 0; i < 1000; i++) {
                CROWD.add(new Person());
            }
    
            long pid = ProcessProperties.getProcessID();
            StringBuffer sb1 = new StringBuffer(100);
            sb1.append(DATE_FORMATTER.format(new Date()));
            sb1.append(": Hello GraalVM native image developer! \n");
            sb1.append("The PID of this process is: " + pid + "\n");
            sb1.append("Send it a signal: ");
            sb1.append("'kill -SIGUSR1 " + pid + "' \n");
            sb1.append("to dump the heap into the 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(DATE_FORMATTER.format(new Date()));
            sb1.append(": Thread finished after: ");
            sb1.append(i);
            sb1.append(" iterations.");
            System.out.println(sb1);
        }
    }
    
    class Person {
        private static Random R = new Random();
        private String name;
        private int age;
                
        public Person() {
            byte[] array = new byte[7];
            R.nextBytes(array);
            name = new String(array, Charset.forName("UTF-8"));
            age = R.nextInt(100);
        }
    }
    
  2. ネイティブ実行ファイルをビルドする

    *SVMHeapDump.java*を次のようにコンパイルします。

     javac SVMHeapDump.java
    

    --enable-monitoring=heapdumpコマンドラインオプションを使用して、ネイティブ実行ファイルをビルドします。(これにより、結果のネイティブ実行ファイルは、SIGUSR1シグナルを受信したときにヒープダンプを生成します。)

     native-image SVMHeapDump --enable-monitoring=heapdump
    

    native-imageビルダーは、*SVMHeapDump.class*ファイルからネイティブ実行ファイルを作成します。コマンドが完了すると、ネイティブ実行ファイル*svmheapdump*が現在のディレクトリに作成されます。)

  3. アプリケーションを実行し、シグナルを送信して、ヒープダンプを確認します

    アプリケーションを実行する

     ./svmheapdump
     17 May 2022, 16:38:13: Hello GraalVM native image developer! 
     The PID of this process is: 57509
     Send it a signal: 'kill -SIGUSR1 57509' 
     to dump the heap into the working directory.
     Starting thread!
     17 May 2022, 16:38:13: Thread started, it will run for 60 seconds
    

    PIDをメモして、2番目のターミナルを開きます。PIDを使用して、アプリケーションにシグナルを送信します。たとえば、PIDが57509の場合

     kill -SIGUSR1 57509
    

    アプリケーションの実行中に、作業ディレクトリにヒープダンプが作成されます。ヒープダンプは、以下に示すように、VisualVMツールで開くことができます。

    Native Image Heap Dump View in VisualVM

ネイティブ実行ファイル内からヒープダンプを作成する

次の例は、何らかの条件が満たされた場合に、VMRuntime.dumpHeap()を使用して、実行中のネイティブ実行ファイルからヒープダンプを作成する方法を示しています。ヒープダンプを作成するための条件は、コマンドラインでオプションとして指定されます。

  1. 以下のコードを*SVMHeapDumpAPI.java*という名前のファイルに保存します。

     import java.io.File;
     import java.io.FileOutputStream;
     import java.io.IOException;
     import java.nio.charset.Charset;
     import java.text.DateFormat;
     import java.util.ArrayList;
     import java.util.Collection;
     import java.util.Date;
     import java.util.Random;
     import org.graalvm.nativeimage.VMRuntime;
    
     public class SVMHeapDumpAPI {
         static Collection<Person> CROWD = new ArrayList<>();
    
         /**
         * @param args the command line arguments
         */
         public static void main(String[] args) {   	
             // Populate the crowd
             for (int i = 0; i < 1000; i++) {
                 CROWD.add(new Person());
             }
             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);
             }
         }
    
         /**
         * Create a heap dump and save it into temp file
         */
         private static void createHeapDump() {
             try {
                 File file = File.createTempFile("SVMHeapDumpAPI-", ".hprof");
                 VMRuntime.dumpHeap(file.getAbsolutePath(), false);
                 System.out.println("  Heap dump created " + file.getAbsolutePath() + ", size: " + file.length());
             } catch (UnsupportedOperationException unsupported) {
                 System.err.println("Heap dump creation failed: " + unsupported.getMessage());
             } catch (IOException ioe) {
                 System.err.println("IO went wrong: " + ioe.getMessage());
             }
         }
    
     }
    
     class Person {
             private static Random R = new Random();
             private String name;
             private int age;
                
             public Person() {
                 byte[] array = new byte[7];
                 R.nextBytes(array);
                 name = new String(array, Charset.forName("UTF-8"));
                 age = R.nextInt(100);
             }
         }
    

    前の例と同様に、アプリケーションは静的変数CROWDによって参照されるPersonCollectionを作成します。次に、コマンドラインをチェックして、ヒープダンプを作成する必要があるかどうかを確認し、メソッドcreateHeapDump()でヒープダンプを作成します。

  2. ネイティブ実行ファイルをビルドします。

    *SVMHeapDumpAPI.java*をコンパイルし、ネイティブ実行ファイルをビルドします

     javac SVMHeapDumpAPI.java
    
     native-image SVMHeapDumpAPI
    

    コマンドが完了すると、*svmheapdumpapi*ネイティブ実行ファイルが現在のディレクトリに作成されます。

  3. アプリケーションを実行して、ヒープダンプを確認します

    これで、ネイティブ実行ファイルを実行し、そこからヒープダンプを作成できます。出力は次のようになります。

     ./svmheapdumpapi --heapdump
     Sep 15, 2020, 4:06:36 PM: Hello GraalVM native image developer.
     Your command line options are: --heapdump
       Heap dump created /var/folders/hw/s9d78jts67gdc8cfyq5fjcdm0000gp/T/SVMHeapDump-6437252222863577987.hprof, size: 8051959
    

    結果のヒープダンプは、以下に示すように、他のJavaヒープダンプと同様にVisualVMツールで開くことができます。

    Native Image Heap Dump View in VisualVM

お問い合わせ