Cara mendapatkan thread dan heap dump dari proses Java di Windows yang tidak berjalan di konsol

232

Saya memiliki aplikasi Java yang saya jalankan dari konsol yang pada gilirannya mengeksekusi proses Java lain. Saya ingin mendapatkan dump thread / heap dari proses anak itu.

Di Unix, saya bisa melakukan kill -3 <pid> tetapi pada Windows AFAIK satu-satunya cara untuk mendapatkan thread dump adalah Ctrl-Break di konsol. Tapi itu hanya memberi saya dump dari proses orang tua, bukan anak.

Apakah ada cara lain untuk mendapatkan heap dump?

Kasun Siyambalapitiya
sumber

Jawaban:

376

Anda dapat menggunakan jmapuntuk mendapatkan dump dari setiap proses yang berjalan, dengan asumsi Anda tahu pid.

Gunakan Task Manager atau Resource Monitor untuk mendapatkan pid. Kemudian

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

untuk mendapatkan tumpukan untuk proses itu.

rkaganda
sumber
jmap tidak tersedia untuk JDK5 di windows. Apakah ada cara untuk mengambil dump dengan JDK5 di windows?
Santron Manibharathi
173
Utas ini menjadi sangat populer sehingga saya baru saja mendengar seseorang menyebut tumpukan sampah sebagai "cheap.bin"
mjaggard
7
Nama file yang lebih mudah: "heap.hprof", seperti dalam format HPROF.
MGM
1
Pastikan untuk menggunakan pengguna yang benar yang memulai proses java. Dalam kasus saya itu tomcat8 ps -C java -o pid sudo -u tomcat8 jmap -dump: format = b, file = <filename> <pid>
bitsabhi
115

Anda membingungkan dua dump java yang berbeda. kill -3menghasilkan dump thread, bukan heap dump.

Thread dump = tumpukan jejak untuk setiap utas dalam output JVM ke stdout sebagai teks.

Heap dump = konten memori untuk output proses JVM ke file biner.

Untuk mengambil thread dump di Windows, CTRL+ BREAKjika JVM Anda adalah proses latar depan adalah cara paling sederhana. Jika Anda memiliki shell seperti unix di Windows seperti Cygwin atau MobaXterm, Anda dapat menggunakannyakill -3 {pid} seperti yang Anda bisa di Unix.

Untuk mengambil thread dump di Unix, CTRL+ Cjika JVM Anda adalah proses foreground ataukill -3 {pid} akan berfungsi selama Anda mendapatkan PID yang tepat untuk JVM.

Dengan platform mana pun, Java dilengkapi dengan beberapa utilitas yang dapat membantu. Untuk kesedihan thread, jstack {pid}adalah taruhan terbaik Anda. http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html

Hanya untuk menyelesaikan pertanyaan dump: Heap dumps tidak umum digunakan karena mereka sulit diinterpretasikan. Tetapi, mereka memiliki banyak informasi berguna di dalamnya jika Anda tahu di mana / bagaimana melihatnya. Penggunaan paling umum adalah menemukan kebocoran memori. Ini adalah praktik yang baik untuk mengatur -Dpada baris perintah java sehingga heap dump dihasilkan secara otomatis pada OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError Tapi, Anda juga dapat secara manual memicu heap dump, juga. Cara paling umum adalah dengan menggunakan utilitas java jmap.

CATATAN: utilitas ini tidak tersedia di semua platform. Pada JDK 1.6, jmaptersedia di Windows.

Contoh baris perintah akan terlihat seperti

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

Output "myheap.bin" tidak dapat dibaca oleh manusia (bagi kebanyakan dari kita), dan Anda akan memerlukan alat untuk menganalisisnya. Preferensi saya adalah MAT. http://www.eclipse.org/mat/

Derek
sumber
3
Pada linux saya Ctrl-C menyela (mengakhiri) itu, saya melakukan Ctrl- \
nafg
Pertimbangkan ini dan dampaknya secara umum pada "Untuk melakukan dump thread pada Windows, CTRL + BREAK". Ini sebenarnya tergantung pada keputusan rekayasa pabrik. FE, Lenova, IIRC, adalah cntrl + fn + p.
ChiefTwoPencils
30

Saya pikir cara terbaik untuk membuat file .hprof dalam proses Linux adalah dengan perintah jmap . Sebagai contoh:jmap -dump:format=b,file=filename.hprof {PID}

Roberto Flores
sumber
19

Selain menggunakan jconsole / visualvm yang disebutkan, Anda dapat menggunakan jstack -l <vm-id> pada jendela baris perintah lain, dan menangkap output itu.

<vm-id> dapat ditemukan menggunakan task manager (ini adalah id proses di windows dan unix), atau menggunakan jps.

Keduanya jstackdan jpssudah termasuk dalam Sun JDK versi 6 dan lebih tinggi.

ankon
sumber
Alat-alat ini tidak didukung di Jawa 1.6. Java 1.6 hanya memiliki jconsole.
Vanchinathan Chandrasekaran
7
Anda mungkin mencampur JDK dan JRE, saya secara eksplisit menyebutkan JDK. Lihat dokumentasi untuk alat: download.oracle.com/javase/6/docs/technotes/tools/share/... dan download.oracle.com/javase/6/docs/technotes/tools/share/...
Ankon
17

Saya merekomendasikan Java VisualVM yang didistribusikan dengan JDK (jvisualvm.exe). Itu dapat terhubung secara dinamis dan mengakses utas dan tumpukan. Saya menemukan sangat berharga untuk beberapa masalah.

Lawrence Dol
sumber
2
Itu sebagian besar kali tidak layak karena memiliki overhead yang melekat padanya dan dump benang umumnya diambil dari mesin produksi.
Hammad Dar
pertanyaan awal adalah tentang proses 'tidak berjalan'. Kemungkinan jvisualvm tidak dapat terhubung.
Jaberino
3
@ Jaberino: Tidak, ini tentang proses Java yang sedang berjalan, di Windows, tanpa konsol yang terkait dengannya.
Lawrence Dol
Dalam rilis java terbaru Java VisualVM digantikan oleh JMC / JFR . Lihat juga Apa perbedaan antara JVisualVM dan Java Mission Control?
Vadzim
16

Jika Anda menggunakan server-jre 8 ke atas, Anda dapat menggunakan ini:

jcmd PID GC.heap_dump /tmp/dump
Atul Soman
sumber
1
Di sebagian besar sistem produksi, kami hanya memiliki jre dan bukan jdk. Jadi ini membantu.
Pragalathan M
15

Coba salah satu opsi di bawah ini.

  1. Untuk JVM 32 bit:

    jmap -dump:format=b,file=<heap_dump_filename> <pid>
  2. Untuk JVM 64 bit (mengutip secara eksplisit):

    jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
  3. Untuk JVM 64 bit dengan algoritma G1GC dalam parameter VM (Hanya tumpukan objek langsung yang dihasilkan dengan algoritma G1GC):

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

Pertanyaan SE terkait: Java heap dump error dengan perintah jmap: Premature EOF

Lihat berbagai opsi jmapdi artikel ini

Ravindra babu
sumber
13

Jika Anda ingin heapdump kehabisan memori, Anda dapat memulai Java dengan opsi -XX:-HeapDumpOnOutOfMemoryError

cf halaman referensi Opsi JVM

Daniel Winterstein
sumber
Terima kasih Daniel. Di mana file ini dibuat di mesin windows? Apakah ada jalur default?
lava
1
@lava Anda dapat mengatur path melalui -XX: HeapDumpPath, seperti yang dijelaskan di halaman Opsi VM Oracle .
kamczak
Luar biasa. Saya ingin menjalankan tes semalam dengan harapan menunjukkan kebocoran memori tetapi khawatir tentang OOM dan crashign sementara saya tidak hadir. Ini sempurna.
Basil
7

Anda dapat menjalankan jconsole(disertakan dengan Java 6's SDK) kemudian terhubung ke aplikasi Java Anda. Ini akan menunjukkan kepada Anda setiap Thread yang berjalan dan jejak tumpukannya.

Steve Kuo
sumber
jawaban terbaik sejauh ini! Tidak tahu ini sampai sekarang dan ini sangat praktis!
Xerus
7

Anda dapat mengirim kill -3 <pid>dari Cygwin. Anda harus menggunakan psopsi Cygwin untuk menemukan proses windows kemudian hanya mengirim sinyal ke proses itu.

krosenvold
sumber
3

Jika Anda menggunakan JDK 1.6 atau lebih tinggi, Anda dapat menggunakan jmapperintah untuk mengambil tumpukan Dump dari proses Java, syaratnya Anda harus tahu ProcessID.

Jika Anda menggunakan Windows Machine, Anda dapat menggunakan Task Manager untuk mendapatkan PID. Untuk mesin Linux Anda dapat menggunakan varietas perintah seperti ps -A | grep javaatau netstat -tupln | grep javaatau top | grep java, tergantung pada aplikasi Anda.

Kemudian Anda dapat menggunakan perintah seperti di jmap -dump:format=b,file=sample_heap_dump.hprof 1234mana 1234 adalah PID.

Ada variasi alat yang tersedia untuk menafsirkan file hprof. Saya akan merekomendasikan alat visualvm Oracle, yang mudah digunakan.

Badal
sumber
3

Jika Anda tidak dapat (atau tidak ingin) menggunakan konsol / terminal karena alasan tertentu, ada solusi alternatif. Anda dapat membuat aplikasi Java mencetak tempat utas untuk Anda. Kode yang mengumpulkan Jejak Tumpukan adalah wajar sederhana dan dapat dilampirkan ke tombol atau antarmuka 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();
}

Metode ini akan mengembalikan string yang terlihat seperti ini:

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)

Bagi mereka yang tertarik dengan versi Java 8 dengan stream, kodenya bahkan lebih ringkas:

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

Anda dapat dengan mudah menguji kode ini dengan:

System.out.print(getThreadDump());
HugoTeixeira
sumber
3

Script berikut menggunakan PsExec untuk terhubung ke Sesi Windows lain sehingga berfungsi bahkan ketika terhubung melalui Remote Desktop Service.

Saya menulis skrip batch kecil untuk Java 8 (menggunakan PsExecdan jcmd) bernama jvmdump.bat, yang membuang thread, heap, properti sistem, dan args JVM.

:: 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%

Itu harus dijalankan dalam sesi Windows yang sama dari pengguna yang memulai JVM, jadi jika Anda terhubung melalui Remote Desktop Anda mungkin perlu meluncurkan command prompt Session 0dan menjalankannya dari sana. misalnya

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

Ini akan meminta Anda (klik ikon bilah tugas di bagian bawah) ke View the messagedalam sesi interaktif, yang akan membawa Anda ke konsol baru di sesi lain dari mana Anda dapat menjalankan jvmdump.batskrip.

isapir
sumber
2

Bagaimana cara mendapatkan id proses aplikasi java?

Jalankan perintah 'jcmd' untuk mendapatkan proses id aplikasi java.

Bagaimana cara mendapatkan Thread dump?

jcmd PID Thread.print> thread.dump

Tautan referensi

Anda bahkan dapat menggunakan jstack untuk mendapatkan dump thread (jstack PID> thread.dump). Tautan referensi

Bagaimana cara mendapatkan heap dump?

Gunakan alat jmap untuk mendapatkan heap dump. jmap -F -dump: live, format = b, file = heap.bin PID

PID adalah singkatan dari id proses aplikasi. Tautan referensi

Hari Krishna
sumber
1

Mungkin jcmd ?

Utilitas Jcmd digunakan untuk mengirim permintaan perintah diagnostik ke JVM, di mana permintaan ini berguna untuk mengendalikan Rekaman Penerbangan Java, memecahkan masalah, dan mendiagnosis JVM dan Aplikasi Java.

Alat jcmd diperkenalkan dengan Oracle Java 7 dan sangat berguna dalam mengatasi masalah dengan aplikasi JVM dengan menggunakannya untuk mengidentifikasi ID proses Java (mirip dengan jps), mendapatkan heap dumps (mirip dengan jmap), memperoleh dumping benang (mirip dengan jstack ), melihat karakteristik mesin virtual seperti properti sistem dan flag baris perintah (mirip dengan jinfo), dan memperoleh statistik pengumpulan sampah (mirip dengan jstat). Alat jcmd telah disebut "pisau swiss-army untuk menyelidiki dan menyelesaikan masalah dengan aplikasi JVM Anda" dan "permata tersembunyi."

Inilah proses yang harus Anda gunakan dalam menjalankan jcmd:

  1. Pergi ke jcmd <pid> GC.heap_dump <file-path>
  2. Di mana
  3. pid: adalah Java Process Id, di mana dump heap akan ditangkap
  4. file-path: adalah path file tempat heap dump dicetak.

Lihat itu untuk informasi lebih lanjut tentang mengambil Java heap dump .

Johnny
sumber
0

Tindak lanjut visualvm:

Jika Anda "tidak dapat terhubung" ke JVM Anda yang berjalan dari jvisualvm karena Anda tidak memulainya dengan argumen JVM yang tepat (dan itu ada di kotak jauh), jalankan jstatddi kotak jauh, kemudian, dengan asumsi Anda memiliki koneksi langsung, tambahkan itu sebagai "remote host" di visualvm, klik dua kali nama host, dan semua JVM lain di kotak itu akan secara ajaib muncul di visualvm.

Jika Anda tidak memiliki "koneksi langsung" ke port pada kotak itu, Anda juga dapat melakukan ini melalui proxy .

Setelah Anda dapat melihat proses yang Anda inginkan, bor ke dalamnya dalam jvisualvm dan gunakan tab monitor -> tombol "heapdump".

rogerdpack
sumber
0

Kode java di bawah ini digunakan untuk mendapatkan Heap Dump dari Proses Java dengan menyediakan PID. Program menggunakan koneksi JMX jarak jauh untuk membuang tumpukan. Mungkin bermanfaat bagi seseorang.

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

}

Ramesh Subramanian
sumber
0

Untuk mengambil thread dump / heap dump dari proses java anak di windows, Anda perlu mengidentifikasi id proses anak sebagai langkah pertama.

Dengan mengeluarkan perintah: jps Anda akan bisa mendapatkan semua proses java Id yang berjalan di mesin windows Anda. Dari daftar ini Anda perlu memilih Id proses anak. Setelah Anda memiliki anak proses Id, ada berbagai opsi untuk menangkap dump thread dan heap dump.

Menangkap Dump Thread:

Ada 8 opsi untuk menangkap dump thread:

  1. jstack
  2. bunuh -3
  3. jvisualVM
  4. JMC
  5. Windows (Ctrl + Break)
  6. ThreadMXBean
  7. Alat APM
  8. jcmd

Rincian tentang setiap opsi dapat ditemukan di artikel ini . Setelah Anda menangkap dump thread, Anda dapat menggunakan alat seperti fastThread , Samuraito menganalisis dump thread.

Menangkap Tumpukan Tumpukan:

Ada 7 opsi untuk menangkap dump heap:

  1. jmap

  2. -XX: + HeapDumpOnOutOfMemoryError

  3. jcmd

  4. JVisualVM

  5. JMX

  6. Pendekatan Programatis

  7. Konsol administratif

Rincian tentang setiap opsi dapat ditemukan di artikel ini . Setelah menangkap heap dump, Anda dapat menggunakan alat seperti alat Eclipse Memory Analysis , HeapHero untuk menganalisis heap dump yang ditangkap.

Jim T
sumber
-1

Pada Oracle JDK, kami memiliki perintah yang disebut jmap (tersedia di folder bin Home Java). penggunaan perintah datang sebagai berikut

jmap (opsi) (pid)

Contoh: jmap -dump: live, format = b, file = heap.bin (pid)

Suresh Ram
sumber