別のJavaプロセスを実行するコンソールから実行するJavaアプリケーションがあります。その子プロセスのスレッド/ヒープダンプを取得したいのですが。
Unixではできましたkill -3 <pid>
が、Windows AFAIKではスレッドダンプを取得する唯一の方法は、コンソールでCtrl-Breakを押すことです。しかし、それは私に親プロセスのダンプを与えるだけで、子は与えません。
そのヒープダンプを取得する別の方法はありますか?
別のJavaプロセスを実行するコンソールから実行するJavaアプリケーションがあります。その子プロセスのスレッド/ヒープダンプを取得したいのですが。
Unixではできましたkill -3 <pid>
が、Windows AFAIKではスレッドダンプを取得する唯一の方法は、コンソールでCtrl-Breakを押すことです。しかし、それは私に親プロセスのダンプを与えるだけで、子は与えません。
そのヒープダンプを取得する別の方法はありますか?
回答:
jmap
を知っていれば、実行中のプロセスのダンプを取得できますpid
。
タスクマネージャーまたはリソースモニターを使用してを取得しpid
ます。その後
jmap -dump:format=b,file=cheap.hprof <pid>
そのプロセスのヒープを取得します。
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以降、jmap
Windowsで使用できます。
コマンドラインの例は次のようになります
jmap -dump:file=myheap.bin {pid of the JVM}
出力 "myheap.bin"は人間が読める形式ではないため(ほとんどの場合)、それを分析するためのツールが必要になります。私の好みはマットです。 http://www.eclipse.org/mat/
Linuxプロセスで.hprofファイルを作成する最良の方法は、jmapコマンドを使用することだと思います。例えば:jmap -dump:format=b,file=filename.hprof {PID}
上記のjconsole / visualvmの使用に加えてjstack -l <vm-id>
、別のコマンドラインウィンドウでを使用して、その出力をキャプチャすることができます。
<vm-id>は、タスクマネージャー(WindowsおよびUNIXのプロセスID)またはを使用して見つけることができますjps
。
jstack
とjps
はどちらも、Sun JDKバージョン6以降に含まれています。
JDK(jvisualvm.exe)とともに配布されているJava VisualVMをお勧めします。動的に接続し、スレッドとヒープにアクセスできます。いくつかの問題のために非常に貴重であることがわかりました。
server-jre 8以降を使用している場合は、これを使用できます。
jcmd PID GC.heap_dump /tmp/dump
以下のオプションのいずれかを試してください。
32ビットJVMの場合:
jmap -dump:format=b,file=<heap_dump_filename> <pid>
64ビットJVMの場合(明示的に引用):
jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
VMパラメーターにG1GCアルゴリズムを使用する64ビットJVMの場合(ライブオブジェクトヒープのみがG1GCアルゴリズムで生成されます):
jmap -J-d64 -dump:live,format=b,file=<heap_dump_filename> <pid>
関連するSEの質問:jmapコマンドでのJavaヒープダンプエラー:早期EOF
メモリ不足でヒープダンプが必要な場合は、オプションでJavaを起動できます -XX:-HeapDumpOnOutOfMemoryError
実行してjconsole
(Java 6のSDKに含まれている)、Javaアプリケーションに接続できます。実行中のすべてのスレッドとそのスタックトレースが表示されます。
kill -3 <pid>
Cygwinからを送信できます。Cygwin ps
オプションを使用してWindowsプロセスを検索し、そのプロセスに信号を送信する必要があります。
2番目のJava実行可能ファイルからの出力をファイルにリダイレクトする必要があります。次に、SendSignalを使用して「-3」を2番目のプロセスに送信します。
JDK 1.6以降を使用している場合は、jmap
コマンドを使用してJavaプロセスのヒープダンプを取得できます。条件は、ProcessIDを知っている必要があります。
Windowsマシンを使用している場合は、タスクマネージャを使用してPIDを取得できます。Linuxマシンの場合、アプリケーションに応じて、ps -A | grep java
or netstat -tupln | grep java
やのようなtop | grep java
さまざまなコマンドを使用できます。
次にjmap -dump:format=b,file=sample_heap_dump.hprof 1234
、1234がPIDであるようなコマンドを使用できます。
品種があります利用可能なツール HPROFファイルを解釈するのは。使い方が簡単なOracleのvisualvmツールをお勧めします。
何らかの理由でコンソール/ターミナルを使用できない(または使用したくない)場合は、別の解決策があります。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());
次のスクリプトは、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
スクリプトを実行できる他のセッションの新しいコンソールに移動します。
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を表します。参照リンク
多分jcmd?
Jcmdユーティリティは、診断コマンド要求をJVMに送信するために使用されます。これらの要求は、Javaフライト記録の制御、トラブルシューティング、およびJVMとJavaアプリケーションの診断に役立ちます。
jcmdツールは、OracleのJava 7で導入され、JavaプロセスのID(jpsに類似)を識別し、ヒープダンプ(jmapに類似)を取得し、スレッドダンプ(jstackに類似)を取得することで、JVMアプリケーションの問題のトラブルシューティングに特に役立ちます。 )、システムプロパティやコマンドラインフラグ(jinfoと同様)などの仮想マシンの特性を表示し、ガベージコレクション統計(jstatと同様)を取得します。jcmdツールは、「JVMアプリケーションの問題を調査および解決するためのスイスアーミーナイフ」および「隠された宝石」と呼ばれています。
の呼び出しに使用する必要があるプロセスは次のjcmd
とおりです。
jcmd <pid> GC.heap_dump <file-path>
Javaヒープダンプの取得について詳しくは、こちらをご覧ください。
Visualvmのフォローアップ:
jvisualvmから実行中のJVMに「接続できない」場合、正しいJVM引数で起動しなかった(そしてリモートJVMで起動した)場合は、リモートサーバーで実行jstatd
し、直接接続していると仮定して、それをvisualvmの「リモートホスト」としてホスト名をダブルクリックすると、そのボックスの他のすべてのJVMがvisualvmに魔法のように表示されます。
そのボックスのポートへの「直接接続」がない場合は、プロキシを介してこれを行うこともできます。
必要なプロセスが表示されたら、jvisualvmでそのプロセスにドリルダウンし、[モニター]タブ-> [ヒープダンプ]ボタンを使用します。
以下の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;
}
}
Windowsで子Javaプロセスからスレッドダンプ/ヒープダンプを取得するには、最初のステップとして子プロセスIDを識別する必要があります。
コマンド:jpsを発行すると、Windowsマシンで実行されているすべてのJavaプロセスIDを取得できます。このリストから、子プロセスIDを選択する必要があります。子プロセスIDを取得したら、スレッドダンプとヒープダンプをキャプチャするさまざまなオプションがあります。
スレッドダンプのキャプチャ:
スレッドダンプをキャプチャするには、8つのオプションがあります。
各オプションの詳細については、この記事をご覧ください。スレッドダンプを取得したら、fastThreadやSamuraitoなどのツールを使用してスレッドダンプを分析できます。
ヒープダンプのキャプチャ:
ヒープダンプをキャプチャするには、7つのオプションがあります。
jmap
-XX:+ HeapDumpOnOutOfMemoryError
jcmd
JVisualVM
JMX
プログラムによるアプローチ
管理コンソール
各オプションの詳細については、この記事をご覧ください。あなたはヒープダンプをキャプチャした後、次のようなツールを使用することができますEclipseのメモリ分析ツール、HeapHeroをキャプチャし、ヒープ・ダンプを分析します。