Làm cách nào để lấy một luồng và kết xuất một tiến trình Java trên Windows mà không chạy trong bảng điều khiển


232

Tôi có một ứng dụng Java mà tôi chạy từ bàn điều khiển, lần lượt thực thi một quy trình Java khác. Tôi muốn có được một chủ đề / đống heap của quá trình con đó.

Trên Unix, tôi có thể thực hiện kill -3 <pid>nhưng trên Windows AFAIK, cách duy nhất để có được kết xuất luồng là Ctrl-Break trong bảng điều khiển. Nhưng điều đó chỉ mang lại cho tôi sự đổ vỡ của quá trình cha mẹ chứ không phải đứa trẻ.

Có cách nào khác để có được đống rác đó không?


Câu trả lời:


376

Bạn có thể sử dụng jmapđể có được một kết xuất của bất kỳ quá trình nào đang chạy, giả sử bạn biết pid.

Sử dụng Trình quản lý tác vụ hoặc Trình giám sát tài nguyên để nhận pid. Sau đó

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

để có được đống cho quá trình đó.


jmap không có sẵn cho JDK5 trong windows. Có cách nào để kết xuất với JDK5 trên windows không?
Santron Manibharathi

173
Chủ đề này đã trở nên phổ biến đến nỗi tôi vừa nghe ai đó nói về một đống heap là "cheap.bin"
mjaggard 6/03/2015

7
Một tên tệp đơn giản hơn: "heap.hprof", vì nó ở định dạng HPROF.
MGM

1
Hãy chắc chắn để sử dụng đúng người dùng đã bắt đầu quá trình java. Trong trường hợp của tôi, đó là tomcat8 ps -C java -o pid sudo -u tomcat8 jmap -dump: format = b, file = <filename> <pid>
bitsabhi

115

Bạn đang nhầm lẫn hai bãi java khác nhau. kill -3tạo ra một bãi chứa luồng, không phải là một đống heap.

Chủ đề kết xuất = dấu vết ngăn xếp cho mỗi luồng trong đầu ra JVM thành thiết bị xuất chuẩn dưới dạng văn bản.

Heap dump = nội dung bộ nhớ cho đầu ra quá trình JVM thành tệp nhị phân.

Để xử lý kết xuất luồng trên Windows, CTRL+ BREAKnếu JVM của bạn là quá trình tiền cảnh là cách đơn giản nhất. Nếu bạn có một vỏ giống như unix trên Windows như Cygwin hoặc MobaXterm, bạn có thể sử dụng kill -3 {pid}như bạn có thể trong Unix.

Để xử lý kết xuất luồng trong Unix, CTRL+ Cnếu JVM của bạn là quá trình tiền cảnh hoặc kill -3 {pid}sẽ hoạt động miễn là bạn nhận được đúng PID cho JVM.

Với một trong hai nền tảng, Java đi kèm với một số tiện ích có thể giúp đỡ. Đối với bãi bỏ chủ đề, jstack {pid}là đặt cược tốt nhất của bạn. http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html

Chỉ cần kết thúc câu hỏi kết xuất: Heap heap không được sử dụng phổ biến vì chúng khó diễn giải. Nhưng, họ có rất nhiều thông tin hữu ích trong đó nếu bạn biết nơi / cách nhìn chúng. Cách sử dụng phổ biến nhất là xác định vị trí rò rỉ bộ nhớ. Đó là một cách thực hành tốt để đặt dòng -Dlệnh java để kết xuất vùng heap được tạo tự động khi OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError Nhưng, bạn cũng có thể tự kích hoạt kết xuất vùng heap. Cách phổ biến nhất là sử dụng tiện ích javajmap .

LƯU Ý: tiện ích này không có sẵn trên tất cả các nền tảng. Kể từ JDK 1.6, jmapcó sẵn trên Windows.

Một dòng lệnh ví dụ sẽ trông giống như

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

Đầu ra "myheap.bin" không thể đọc được (đối với hầu hết chúng ta) và bạn sẽ cần một công cụ để phân tích nó. Sở thích của tôi là MAT. http://www.eclipse.org/mat/


3
Trên linux của tôi, Ctrl-C bị gián đoạn (chấm dứt) nó, tôi thực hiện Ctrl- \
nafg

Hãy xem xét điều này và tác động chung của nó đối với "Để lấy kết xuất luồng trên Windows, CTRL + BREAK". Nó thực sự phụ thuộc vào quyết định kỹ thuật của nhà sản xuất. FE, Lenova, IIRC, là cntrl + fn + p.
ChiefTwoP Pencil

30

Tôi nghĩ cách tốt nhất để tạo tệp .hprof trong quy trình Linux là bằng lệnh jmap . Ví dụ:jmap -dump:format=b,file=filename.hprof {PID}


19

Ngoài việc sử dụng jconsole / visualvm đã đề cập, bạn có thể sử dụng jstack -l <vm-id>trên một cửa sổ dòng lệnh khác và chụp đầu ra đó.

Có thể tìm thấy <vm-id> bằng trình quản lý tác vụ (đó là id tiến trình trên windows và unix) hoặc sử dụng jps .

Cả hai jstackjpsđược bao gồm trong Sun JDK phiên bản 6 trở lên.


Các công cụ này không được hỗ trợ trong Java 1.6. Java 1.6 chỉ có jconsole.
Vanchinathan Chandrasekaran

7
Bạn có thể đang trộn lẫn JDK và JRE, tôi đã đề cập rõ ràng về JDK. Xem tài liệu về các công cụ: download.oracle.com/javase/6/docs/technotes/tools/share/ và và download.oracle.com/javase/6/docs/technotes/tools/share/
trộm

17

Tôi khuyên dùng Java VisualVM được phân phối với JDK (jvisualvm.exe). Nó có thể kết nối linh hoạt và truy cập các chủ đề và heap. Tôi đã tìm thấy vô giá cho một số vấn đề.


2
Điều đó hầu hết không khả thi vì nó có một chi phí gắn liền với nó và các bãi chứa sợi thường được lấy từ các máy sản xuất.
Hammad Dar

câu hỏi ban đầu là về một quá trình 'không chạy'. Có khả năng jvisualvm không thể kết nối.
Jaberino

3
@Jaberino: Không, đó là về một quy trình Java hiện đang chạy, trong Windows, không có giao diện điều khiển nào được liên kết với nó.
Lawrence Dol

Trong các bản phát hành java mới nhất, Java VisualVM đã được JMC / JFR hỗ trợ . Xem thêm Sự khác biệt giữa JVisualVM và Java Mission Control là gì?
Vadzim

16

Nếu bạn đang dùng máy chủ-jre 8 trở lên, bạn có thể sử dụng:

jcmd PID GC.heap_dump /tmp/dump

1
Trong hầu hết các hệ thống sản xuất, chúng tôi chỉ có jre và không có jdk. Vì vậy, điều này giúp.
Thực dụng M

15

Hãy thử một trong các tùy chọn dưới đây.

  1. Đối với JVM 32 bit:

    jmap -dump:format=b,file=<heap_dump_filename> <pid>
  2. Đối với JVM 64 bit (trích dẫn rõ ràng):

    jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
  3. Đối với JVM 64 bit với thuật toán G1GC trong các tham số VM (Chỉ có heap đối tượng trực tiếp được tạo bằng thuật toán G1GC):

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

Câu hỏi SE liên quan: Lỗi heap Java với lệnh jmap: EOF sớm

Hãy nhìn vào tùy chọn khác nhau của jmaplúc này bài viết


13

Nếu bạn muốn có một heapdump trên bộ nhớ ngoài, bạn có thể khởi động Java với tùy chọn -XX:-HeapDumpOnOutOfMemoryError

cf trang tham khảo JVM Tùy chọn


Cảm ơn Daniel. Tập tin này được tạo ở đâu trên máy windows? Có một đường dẫn mặc định?
dung nham

1
@lava Bạn có thể đặt đường dẫn qua -XX: HeapDumpPath, như được mô tả tại trang Tùy chọn VM của Oracle .
kamczak

Tuyệt vời. Tôi muốn chạy thử nghiệm qua đêm với hy vọng hiển thị rò rỉ bộ nhớ nhưng lo lắng về OOM và bị hỏng trong khi tôi không có mặt. Đây là hoàn hảo.
Basil

7

Bạn có thể chạy jconsole (bao gồm SDK của Java 6) sau đó kết nối với ứng dụng Java của bạn. Nó sẽ hiển thị cho bạn mọi Chủ đề đang chạy và theo dõi ngăn xếp của nó.


câu trả lời tốt nhất cho đến nay! Không biết điều này cho đến bây giờ và nó thực sự thiết thực!
Xerus

7

Bạn có thể gửi kill -3 <pid>từ Cygwin. Bạn phải sử dụng các pstùy chọn Cygwin để tìm các tiến trình windows sau đó chỉ cần gửi tín hiệu đến quá trình đó.



3

Nếu bạn đang sử dụng JDK 1.6 trở lên, Bạn có thể sử dụng jmaplệnh để lấy một đống Hump của một quy trình Java, điều kiện là bạn nên biết ProcessID.

Nếu bạn đang sử dụng Windows Machine, bạn có thể sử dụng Trình quản lý tác vụ để nhận PID. Đối với máy Linux bạn có thể sử dụng loại lệnh như ps -A | grep javahay netstat -tupln | grep javahay top | grep java, phụ thuộc vào ứng dụng của bạn.

Sau đó, bạn có thể sử dụng lệnh như jmap -dump:format=b,file=sample_heap_dump.hprof 1234trong đó 1234 là PID.

Có nhiều loại công cụ có sẵn để giải thích tệp hprof. Tôi sẽ giới thiệu công cụ visualvm của Oracle, sử dụng đơn giản.


3

Nếu bạn không thể (hoặc không muốn) sử dụng bàn điều khiển / thiết bị đầu cuối vì một số lý do, có một giải pháp thay thế. Bạn có thể làm cho ứng dụng Java in kết xuất luồng cho bạn. Mã thu thập Stack Trace là đơn giản hợp lý và có thể được gắn vào một nút hoặc giao diện 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();
}

Phương thức này sẽ trả về một chuỗi trông như thế này:

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)

Đối với những người quan tâm đến phiên bản Java 8 có luồng, mã thậm chí còn nhỏ gọn hơn:

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

Bạn có thể dễ dàng kiểm tra mã này với:

System.out.print(getThreadDump());

3

Tập lệnh sau sử dụng PsExec để kết nối với Phiên Windows khác để nó hoạt động ngay cả khi được kết nối qua Remote Desktop Service.

Tôi đã viết một tập lệnh bó nhỏ cho Java 8 (sử dụng PsExecjcmd) có tên jvmdump.bat, để loại bỏ các luồng, heap, thuộc tính hệ thống và 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%

Nó phải được chạy trong cùng một phiên Windows của người dùng đã khởi động JVM, vì vậy nếu bạn kết nối qua Remote Desktop, bạn có thể cần phải khởi chạy một dấu nhắc lệnh Session 0và chạy nó từ đó. ví dụ

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

Điều này sẽ nhắc bạn (nhấp vào biểu tượng thanh tác vụ ở dưới cùng) View the messagetrong phiên tương tác, sẽ đưa bạn đến bảng điều khiển mới trong phiên khác mà bạn có thể chạy jvmdump.battập lệnh.


2

Làm thế nào để có được id quá trình của ứng dụng java?

Thực hiện lệnh 'jcmd' để lấy id tiến trình của các ứng dụng java.

Làm thế nào để có được chủ đề?

jcmd PID Thread.print> thread.dump

Liên kết tham khảo

Bạn thậm chí có thể sử dụng jstack để nhận kết xuất luồng (jstack PID> thread.dump). Liên kết tham khảo

Làm thế nào để có được đống đổ?

Sử dụng công cụ jmap để có được đống heap. jmap -F -dump: live, format = b, file = heap.bin PID

PID là viết tắt của id quá trình của ứng dụng. Liên kết tham khảo


1

Có lẽ jcmd ?

Tiện ích Jcmd được sử dụng để gửi các yêu cầu lệnh chẩn đoán đến JVM, trong đó các yêu cầu này hữu ích để kiểm soát Bản ghi chuyến bay Java, khắc phục sự cố và chẩn đoán Ứng dụng JVM và Java.

Công cụ jcmd đã được giới thiệu với Java 7 của Oracle và đặc biệt hữu ích trong việc khắc phục sự cố với các ứng dụng JVM bằng cách sử dụng nó để xác định ID của các quy trình Java (gần giống với jps), có được các bãi chứa heap (gần giống với jmap) ), xem các đặc điểm của máy ảo như thuộc tính hệ thống và cờ dòng lệnh (gần giống với jinfo) và thu thập số liệu thống kê thu gom rác (gần giống với jstat). Công cụ jcmd đã được gọi là "con dao quân đội Thụy Sĩ để điều tra và giải quyết các vấn đề với ứng dụng JVM của bạn" và "viên ngọc ẩn".

Đây là quá trình bạn sẽ cần sử dụng để gọi jcmd:

  1. Đi đến jcmd <pid> GC.heap_dump <file-path>
  2. Trong đó
  3. pid: là Id quy trình Java, trong đó bãi chứa heap sẽ được ghi lại,
  4. đường dẫn tệp: là đường dẫn tệp trong đó kết xuất heap được in.

Kiểm tra nó để biết thêm thông tin về việc lấy đống heap Java .


0

Theo dõi trực quan:

Nếu bạn "không thể kết nối" với JVM đang chạy của mình từ jvisualvm vì bạn đã không khởi động nó với các đối số JVM phù hợp (và trên hộp từ xa), hãy chạy jstatdtrên hộp từ xa, sau đó, giả sử bạn có kết nối trực tiếp, hãy thêm nó như là một "máy chủ từ xa" trong visualvm, nhấp đúp vào tên máy chủ và tất cả các JVM khác trên hộp đó sẽ hiển thị một cách kỳ diệu trong visualvm.

Nếu bạn không có "kết nối trực tiếp" với các cổng trên hộp đó, bạn cũng có thể thực hiện việc này thông qua proxy .

Khi bạn có thể thấy quy trình bạn muốn, hãy đi sâu vào nó trong jvisualvm và sử dụng tab màn hình -> nút "heapdump".


0

Dưới đây mã java được sử dụng để lấy Heap Dump của một quy trình Java bằng cách cung cấp PID. Chương trình sử dụng kết nối Remote JMX để kết xuất đống. Nó có thể hữu ích cho một số người.

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

Đặt hàng để nhận kết xuất luồng / kết xuất heap từ quy trình java con trong windows, bạn cần xác định Id tiến trình con là bước đầu tiên.

Bằng cách ban hành lệnh: jps, bạn sẽ có thể nhận được tất cả các Id quy trình java đang chạy trên máy tính windows của mình. Từ danh sách này, bạn cần chọn Id tiến trình con. Khi bạn có Id tiến trình con, có nhiều tùy chọn khác nhau để chụp kết xuất luồng và kết xuất đống.

Chụp bãi chứa chủ đề:

Có 8 tùy chọn để chụp các bãi chứa luồng:

  1. jstack
  2. giết -3
  3. jvisualVM
  4. JMC
  5. Windows (Ctrl + Break)
  6. ThreadMXBean
  7. Công cụ APM
  8. jcmd

Chi tiết về từng lựa chọn có thể được tìm thấy trong bài viết này . Khi bạn đã bắt được các bãi chứa luồng, bạn có thể sử dụng các công cụ như fastThread , Samuraito phân tích các bãi chứa luồng.

Nắm bắt đống Heap:

Có 7 tùy chọn để chụp các đống heap:

  1. jmap

  2. -XX: + HeapDumpOnOutOfMemoryError

  3. jcmd

  4. JVisualVM

  5. JMX

  6. Phương pháp lập trình

  7. Bảng điều khiển hành chính

Chi tiết về từng lựa chọn có thể được tìm thấy trong bài viết này . Khi bạn đã bắt được đống heap, bạn có thể sử dụng các công cụ như công cụ Phân tích bộ nhớ Eclipse , HeapHero để phân tích các bãi chứa heap bị bắt.


-1

Trên một JDK của Oracle, chúng ta có một lệnh gọi là jmap (có sẵn trong thư mục bin của Java Home). việc sử dụng lệnh diễn ra như sau

jmap (tùy chọn) (pid)

Ví dụ: jmap -dump: live, format = b, file = heap.bin (pid)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.