コンソールで実行されていないWindows上のJavaプロセスのスレッドとヒープダンプを取得する方法


232

別のJavaプロセスを実行するコンソールから実行するJavaアプリケーションがあります。その子プロセスのスレッド/ヒープダンプを取得したいのですが。

Unixではできましたkill -3 <pid>が、Windows AFAIKではスレッドダンプを取得する唯一の方法は、コンソールでCtrl-Breakを押すことです。しかし、それは私に親プロセスのダンプを与えるだけで、子は与えません。

そのヒープダンプを取得する別の方法はありますか?


回答:


376

jmapを知っていれば、実行中のプロセスのダンプを取得できますpid

タスクマネージャーまたはリソースモニターを使用してを取得しpidます。その後

jmap -dump:format=b,file=cheap.hprof <pid>

そのプロセスのヒープを取得します。


jmapは、WindowsのJDK5では使用できません。WindowsでJDK5を使用してダンプを取得する方法はありますか?
Santron Manibharathi 2013年

173
このスレッドは非常に人気が高まっているので、ヒープダンプを「cheap.bin」と呼ぶ人がいるのを耳にしたばかりです
mjaggard

7
よりわかりやすいファイル名: "heap.hprof"(HPROF形式)。
MGM

1
Javaプロセスを開始した正しいユーザーを使用してください。私の場合、それはtomcat8 ps -C java -o pid sudo -u tomcat8 jmap -dump:format = b、file = <filename> <pid>
bitsabhi

115

2つの異なるJavaダンプを混同しています。 kill -3ヒープダンプではなくスレッドダンプを生成します。

スレッドダンプ=テキストとしてstdoutに出力されるJVMの各スレッドのスタックトレース。

ヒープダンプ=バイナリファイルに出力されるJVMプロセスのメモリ内容。

CTRL+ Windowsでスレッドダンプを取得するには、+ BREAKJVMがフォアグラウンドプロセスの場合が最も簡単な方法です。CygwinやMobaXtermのようなWindowsにUNIXのようなシェルがある場合は、Unixでできるkill -3 {pid}ように使用できます。

CTRL+ Unixでスレッドダンプを取得するにCは、JVMがフォアグラウンドプロセスであるかkill -3 {pid}、JVMの正しいPIDを取得している限り機能します。

どちらのプラットフォームでも、Javaには役立ついくつかのユーティリティが付属しています。スレッドダンプの場合jstack {pid}は、最善の策です。http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html

ダンプの質問を終えるだけ:ヒープダンプは、解釈が難しいため、一般的には使用されません。しかし、それらをどこでどのように見るかを知っていれば、多くの有用な情報が含まれています。最も一般的な使用方法は、メモリリークを見つけることです。-DヒープダンプがOutOfMemoryErrorで自動的に生成されるようにjavaコマンドラインでを設定-XX:+HeapDumpOnOutOfMemoryError することをお勧めしますが、手動でヒープダンプをトリガーすることもできます。最も一般的な方法は、javaユーティリティを使用することですjmap

注:このユーティリティは、すべてのプラットフォームで使用できるわけではありません。JDK 1.6以降、jmapWindowsで使用できます。

コマンドラインの例は次のようになります

jmap -dump:file=myheap.bin {pid of the JVM}

出力 "myheap.bin"は人間が読める形式ではないため(ほとんどの場合)、それを分析するためのツールが必要になります。私の好みはマットです。 http://www.eclipse.org/mat/


3
LinuxのCtrl-Cで割り込み(終了)します
。Ctrl-

これと、「Windowsでスレッドダンプを取るには、CTRL + BREAK」に対する一般的な影響を考慮してください。それは実際にはメーカーの技術的な決定に依存しています。FE、Lenova、IIRCはcntrl + fn + pです。
ChiefTwoPencils 2015年

30

Linuxプロセスで.hprofファイルを作成する最良の方法は、jmapコマンドを使用することだと思います。例えば:jmap -dump:format=b,file=filename.hprof {PID}


19

上記のjconsole / visualvmの使用に加えてjstack -l <vm-id>、別のコマンドラインウィンドウでを使用して、その出力をキャプチャすることができます。

<vm-id>は、タスクマネージャー(WindowsおよびUNIXのプロセスID)またはを使用して見つけることができますjps

jstackjpsはどちらも、Sun JDKバージョン6以降に含まれています。


これらのツールは、Java 1.6ではサポートされていません。Java 1.6にはjconsoleしかありません。
Vanchinathan Chandrasekaran、2011

7
JDKとJREを混同している可能性があります。ツールのマニュアルを参照してください:download.oracle.com/javase/6/docs/technotes/tools/share/...download.oracle.com/javase/6/docs/technotes/tools/share/...
ankon

17

JDK(jvisualvm.exe)とともに配布されているJava VisualVMをお勧めします。動的に接続し、スレッドとヒープにアクセスできます。いくつかの問題のために非常に貴重であることがわかりました。


2
オーバーヘッドが付加されており、スレッドダンプは通常、運用マシンから取得されるため、ほとんどの場合、これは現実的ではありません。
Hammad Dar

元の質問は「非実行」プロセスについてです。jvisualvmが接続できない可能性があります。
Jaberino 2016

3
@Jaberino:いいえ、それはWindowsで現在実行中のJavaプロセスに関するもので、コンソールは関連付けられていません。
Lawrence Dol、2016

最新のJavaリリースでは、Java VisualVMはJMC / JFRに置き換えられました。参照JVisualVMとJava Mission Controlの違いは何ですか?
Vadzim

16

server-jre 8以降を使用している場合は、これを使用できます。

jcmd PID GC.heap_dump /tmp/dump

1
ほとんどのプロダクションシステムでは、jreのみがあり、jdkはありません。これは役立ちます。
Pragalathan M

15

以下のオプションのいずれかを試してください。

  1. 32ビットJVMの場合:

    jmap -dump:format=b,file=<heap_dump_filename> <pid>
  2. 64ビットJVMの場合(明示的に引用):

    jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
  3. VMパラメーターにG1GCアルゴリズムを使用する64ビットJVMの場合(ライブオブジェクトヒープのみがG1GCアルゴリズムで生成されます):

    jmap -J-d64 -dump:live,format=b,file=<heap_dump_filename> <pid>

関連するSEの質問:jmapコマンドでのJavaヒープダンプエラー:早期EOF

jmapこの記事のさまざまなオプションをご覧ください


13

メモリ不足でヒープダンプが必要な場合は、オプションでJavaを起動できます -XX:-HeapDumpOnOutOfMemoryError

cf JVMオプションのリファレンスページ


ダニエルに感謝します。このファイルはWindowsマシンのどこに作成されますか?デフォルトのパスはありますか?
溶岩

1
@lava OracleのVMオプションページで説明されているように、-XX:HeapDumpPathを介してパスを設定できます。
kamczak 2013年

驚くばかり。私はメモリリークを示すことを期待して一晩テストを実行したかったが、私がいない間にOOMとcrashignが心配でした。これは完璧です。
バジル

7

実行してjconsole(Java 6のSDKに含まれている)、Javaアプリケーションに接続できます。実行中のすべてのスレッドとそのスタックトレースが表示されます。


断然ベストアンサー!これまでこれを知らなかったし、それは本当に実用的です!
Xerus 2017年

7

kill -3 <pid>Cygwinからを送信できます。Cygwin psオプションを使用してWindowsプロセスを検索し、そのプロセスに信号を送信する必要があります。



3

JDK 1.6以降を使用している場合は、jmapコマンドを使用してJavaプロセスのヒープダンプを取得できます。条件は、ProcessIDを知っている必要があります。

Windowsマシンを使用している場合は、タスクマネージャを使用してPIDを取得できます。Linuxマシンの場合、アプリケーションに応じて、ps -A | grep javaor netstat -tupln | grep javaやのようなtop | grep javaさまざまなコマンドを使用できます。

次にjmap -dump:format=b,file=sample_heap_dump.hprof 1234、1234がPIDであるようなコマンドを使用できます。

品種があります利用可能なツール HPROFファイルを解釈するのは。使い方が簡単なOracleのvisualvmツールをお勧めします。


3

何らかの理由でコンソール/ターミナルを使用できない(または使用したくない)場合は、別の解決策があります。Javaアプリケーションにスレッドダンプを出力させることができます。スタックトレースを収集するコードはかなり単純で、ボタンまたはWebインターフェイスにアタッチできます。

private static String getThreadDump() {
    Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();

    StringBuilder out = new StringBuilder();
    for (Map.Entry<Thread, StackTraceElement[]> entry : allStackTraces.entrySet()) {
        Thread thread = entry.getKey();
        StackTraceElement[] elements = entry.getValue();
        out.append(String.format("%s | prio=%d | %s", thread.getName(), thread.getPriority(), thread.getState()));
        out.append('\n');

        for (StackTraceElement element : elements) {
            out.append(element.toString()).append('\n');
        }
        out.append('\n');
    }
    return out.toString();
}

このメソッドは、次のような文字列を返します。

main | prio=5 | RUNNABLE
java.lang.Thread.dumpThreads(Native Method)
java.lang.Thread.getAllStackTraces(Thread.java:1607)
Main.getThreadDump(Main.java:8)
Main.main(Main.java:36)

Monitor Ctrl-Break | prio=5 | RUNNABLE
java.net.PlainSocketImpl.initProto(Native Method)
java.net.PlainSocketImpl.<clinit>(PlainSocketImpl.java:45)
java.net.Socket.setImpl(Socket.java:503)
java.net.Socket.<init>(Socket.java:424)
java.net.Socket.<init>(Socket.java:211)
com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:59)

Finalizer | prio=8 | WAITING
java.lang.Object.wait(Native Method)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

Reference Handler | prio=10 | WAITING
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:502)
java.lang.ref.Reference.tryHandlePending(Reference.java:191)
java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

ストリーム付きのJava 8バージョンに関心がある場合は、コードがさらにコンパクトになります。

private static String getThreadDump() {
    Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
    StringBuilder out = new StringBuilder();
    allStackTraces.forEach((thread, elements) -> {
        out.append(String.format("%s | prio=%d | %s", thread.getName(), thread.getPriority(), thread.getState()));
        out.append('\n');

        Arrays.stream(elements).forEach(element -> out.append(element.toString()).append('\n'));
        out.append('\n');
    });
    return out.toString();
}

このコードは次の方法で簡単にテストできます。

System.out.print(getThreadDump());

3

次のスクリプトは、PsExecを使用して別のWindowsセッションに接続するため、リモートデスクトップサービス経由で接続されている場合でも機能します。

スレッド、ヒープ、システムプロパティ、JVM引数をダンプするJava 8(PsExecおよびを使用jcmd)用の小さなバッチスクリプトを作成しましたjvmdump.bat

:: set the paths for your environment
set PsExec=C:\Apps\SysInternals\PsExec.exe
set JAVA_HOME=C:\Apps\Java\jdk1.8.0_121
set DUMP_DIR=C:\temp

@echo off

set PID=%1

if "%PID%"=="" (
    echo usage: jvmdump.bat {pid}
    exit /b
)

for /f "tokens=2,3,4 delims=/ " %%f in ('date /t') do set timestamp_d=%%h%%g%%f
for /f "tokens=1,2 delims=: " %%f in ('time /t') do set timestamp_t=%%f%%g
set timestamp=%timestamp_d%%timestamp_t%
echo datetime is: %timestamp%

echo ### Version >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.version >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Uptime >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.uptime >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Command >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.command_line >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Flags >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.flags >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Properties >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.system_properties >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% Thread.print -l >"%DUMP_DIR%\%PID%-%timestamp%-threads.log"

%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% GC.heap_dump "%DUMP_DIR%\%PID%-%timestamp%-heap.hprof"

echo Dumped to %DUMP_DIR%

JVMを起動したユーザーと同じWindowsセッションで実行する必要があるため、リモートデスクトップ経由で接続する場合は、コマンドプロンプトを起動Session 0してそこから実行する必要がある場合があります。例えば

%PsExec% -s -h -d -i 0 cmd.exe

これView the messageにより、インタラクティブセッションで(下部のタスクバーアイコンをクリックして)プロンプトが表示され、jvmdump.batスクリプトを実行できる他のセッションの新しいコンソールに移動します。


2

JavaアプリケーションのプロセスIDを取得する方法

コマンド「jcmd」を実行して、JavaアプリケーションのプロセスIDを取得します。

スレッドダンプを取得する方法?

jcmd PID Thread.print> thread.dump

参照リンク

jstackを使用してスレッドダンプを取得することもできます(jstack PID> thread.dump)。参照リンク

ヒープダンプを取得する方法は?

jmapツールを使用してヒープダ​​ンプを取得します。jmap -F -dump:live、format = b、file = heap.bin PID

PIDは、アプリケーションのプロセスIDを表します。参照リンク


1

多分jcmd

Jcmdユーティリティは、診断コマンド要求をJVMに送信するために使用されます。これらの要求は、Javaフライト記録の制御、トラブルシューティング、およびJVMとJavaアプリケーションの診断に役立ちます。

jcmdツールは、OracleのJava 7で導入され、JavaプロセスのID(jpsに類似)を識別し、ヒープダンプ(jmapに類似)を取得し、スレッドダンプ(jstackに類似)を取得することで、JVMアプリケーションの問題のトラブルシューティングに特に役立ちます。 )、システムプロパティやコマンドラインフラグ(jinfoと同様)などの仮想マシンの特性を表示し、ガベージコレクション統計(jstatと同様)を取得します。jcmdツールは、「JVMアプリケーションの問題を調査および解決するためのスイスアーミーナイフ」および「隠された宝石」と呼ばれています。

の呼び出しに使用する必要があるプロセスは次のjcmdとおりです。

  1. に行く jcmd <pid> GC.heap_dump <file-path>
  2. その中で
  3. pid:は、ヒープダンプがキャプチャされるJavaプロセスIDです。また、
  4. file-path:ヒープダンプが出力されるファイルパスです。

Javaヒープダンプの取得について詳しくは、こちらをご覧ください。


0

Visualvmのフォローアップ:

jvisualvmから実行中のJVMに「接続できない」場合、正しいJVM引数で起動しなかった(そしてリモートJVMで起動した)場合は、リモートサーバーで実行jstatdし、直接接続していると仮定して、それをvisualvmの「リモートホスト」としてホスト名をダブルクリックすると、そのボックスの他のすべてのJVMがvisualvmに魔法のように表示されます。

そのボックスのポートへの「直接接続」がない場合は、プロキシを介してこれを行うこともできます。

必要なプロセスが表示されたら、jvisualvmでそのプロセスにドリルダウンし、[モニター]タブ-> [ヒープダンプ]ボタンを使用します。


0

以下のJavaコードは、PIDを提供することによってJavaプロセスのヒープダンプを取得するために使用されます。プログラムは、リモートJMX接続を使用してヒープをダンプします。一部の人に役立つかもしれません。

import java.lang.management.ManagementFactory;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.reflect.Method;

public class HeapDumper {

public static final String HOST = "192.168.11.177";
public static final String PORT = "1600";
public static final String FILE_NAME = "heapDump.hprof";
public static final String FOLDER_PATH = "C:/";
private static final String HOTSPOT_BEAN_NAME ="com.sun.management:type=HotSpotDiagnostic";

public static void main(String[] args) {
    if(args.length == 0) {
        System.out.println("Enter PID of the Java Process !!!");
        return;
    }

    String pidString = args[0];
    int pid = -1;
    if(pidString!=null && pidString.length() > 0) {
        try {
            pid = Integer.parseInt(pidString);
        }
        catch(Exception e) {
            System.out.println("PID is not Valid !!!");
            return;
        }
    }
    boolean isHeapDumpSuccess = false;
    boolean live = true;
    if(pid > 0) {
        MBeanServerConnection beanServerConn = getJMXConnection();

        if(beanServerConn!=null) {
            Class clazz = null;
            String dumpFile = FOLDER_PATH+"/"+FILE_NAME;
            try{
                clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
                Object hotspotMBean = ManagementFactory.newPlatformMXBeanProxy(beanServerConn, HOTSPOT_BEAN_NAME, clazz);
                Method method = clazz.getMethod("dumpHeap", new Class[]{String.class , boolean.class});
                method.setAccessible(true);
                method.invoke(hotspotMBean , new Object[] {dumpFile, new Boolean(live)});
                isHeapDumpSuccess = true;
            }
            catch(Exception e){
                e.printStackTrace();
                isHeapDumpSuccess = false;
            }
            finally{
                clazz = null;
            }
        }
    }

    if(isHeapDumpSuccess){
        System.out.println("HeapDump is Success !!!");
    }
    else{
        System.out.println("HeapDump is not Success !!!");
    }
}

private static MBeanServerConnection getJMXConnection() {
    MBeanServerConnection mbeanServerConnection = null;
    String urlString = "service:jmx:rmi:///jndi/rmi://" + HOST + ":" + PORT + "/jmxrmi";
    try {
        JMXServiceURL url = new JMXServiceURL(urlString);
        JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
        mbeanServerConnection = jmxConnector.getMBeanServerConnection();
        System.out.println("JMX Connection is Success for the URL :"+urlString);
    }
    catch(Exception e) {
        System.out.println("JMX Connection Failed !!!");
    }
    return mbeanServerConnection;
}

}


0

Windowsで子Javaプロセスからスレッドダンプ/ヒープダンプを取得するには、最初のステップとして子プロセスIDを識別する必要があります。

コマンド:jpsを発行すると、Windowsマシンで実行されているすべてのJavaプロセスIDを取得できます。このリストから、子プロセスIDを選択する必要があります。子プロセスIDを取得したら、スレッドダンプとヒープダンプをキャプチャするさまざまなオプションがあります。

スレッドダンプのキャプチャ:

スレッドダンプをキャプチャするには、8つのオプションがあります。

  1. jstack
  2. 殺す-3
  3. jvisualVM
  4. JMC
  5. ウィンドウズ(Ctrl + Break)
  6. ThreadMXBean
  7. APMツール
  8. jcmd

各オプションの詳細については、この記事をご覧ください。スレッドダンプを取得したら、fastThreadSamuraitoなどのツールを使用してスレッドダンプを分析できます。

ヒープダンプのキャプチャ:

ヒープダンプをキャプチャするには、7つのオプションがあります。

  1. jmap

  2. -XX:+ HeapDumpOnOutOfMemoryError

  3. jcmd

  4. JVisualVM

  5. JMX

  6. プログラムによるアプローチ

  7. 管理コンソール

各オプションの詳細については、この記事をご覧ください。あなたはヒープダンプをキャプチャした後、次のようなツールを使用することができますEclipseのメモリ分析ツールHeapHeroをキャプチャし、ヒープ・ダンプを分析します。


-1

Oracle JDKには、jmapというコマンドがあります(Javaホームのbinフォルダーにあります)。コマンドの使い方は次のとおりです

jmap(オプション)(pid)

例:jmap -dump:live、format = b、file = heap.bin(pid)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.