콘솔에서 실행하는 Java 응용 프로그램이 있으며 다른 Java 프로세스를 실행합니다. 해당 자식 프로세스의 스레드 / 힙 덤프를 가져오고 싶습니다.
유닉스에서는 할 수 kill -3 <pid>
있지만 Windows AFAIK에서는 스레드 덤프를 얻는 유일한 방법은 콘솔의 Ctrl-Break입니다. 그러나 그것은 저에게 부모 프로세스의 덤프를 제공하지만 자식은 아닙니다.
해당 힙 덤프를 얻는 다른 방법이 있습니까?
콘솔에서 실행하는 Java 응용 프로그램이 있으며 다른 Java 프로세스를 실행합니다. 해당 자식 프로세스의 스레드 / 힙 덤프를 가져오고 싶습니다.
유닉스에서는 할 수 kill -3 <pid>
있지만 Windows AFAIK에서는 스레드 덤프를 얻는 유일한 방법은 콘솔의 Ctrl-Break입니다. 그러나 그것은 저에게 부모 프로세스의 덤프를 제공하지만 자식은 아닙니다.
해당 힙 덤프를 얻는 다른 방법이 있습니까?
답변:
jmap
을 알고 있다고 가정하면 실행중인 프로세스의 덤프를 얻는 데 사용할 수 있습니다 pid
.
작업 관리자 또는 리소스 모니터를 사용하여 pid
. 그때
jmap -dump:format=b,file=cheap.hprof <pid>
해당 프로세스의 힙을 가져옵니다.
두 개의 다른 Java 덤프를 혼동하고 있습니다. kill -3
힙 덤프가 아닌 스레드 덤프를 생성합니다.
스레드 덤프 = JVM 출력의 각 스레드에 대한 스택 추적으로 텍스트로 stdout합니다.
힙 덤프 = JVM 프로세스의 메모리 내용이 2 진 파일로 출력됩니다.
Windows에서 스레드 덤프를 수행하려면 JVM이 포 그라운드 프로세스 인 경우 CTRL+ BREAK가 가장 간단한 방법입니다. Cygwin 또는 MobaXterm과 같은 Windows kill -3 {pid}
에 유닉스 계열 쉘이 있으면 Unix에서와 같이 사용할 수 있습니다.
Unix에서 스레드 덤프를 가져 오려면 JVM이 포 그라운드 프로세스이거나 JVM에 대한 올바른 PID를 얻는 한 작동 하는 경우 CTRL+ .Ckill -3 {pid}
두 플랫폼 중 하나에 Java에는 도움이되는 여러 유틸리티가 제공됩니다. 스레드 덤프의 경우 jstack {pid}
가장 좋은 방법입니다. http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html
덤프 문제를 해결하는 것 : 힙 덤프는 해석하기 어렵 기 때문에 일반적으로 사용되지 않습니다. 그러나 어디에서 어떻게 볼 것인지 알고 있다면 유용한 정보가 많이 있습니다. 가장 일반적인 사용법은 메모리 누수를 찾는 것입니다. -D
OutOfMemoryError시 힙 덤프가 자동으로 생성되도록 java 명령 행 에서 on 을 설정하는 것이 좋지만 -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/
언급 된 jconsole / visualvm을 사용하는 것 외에도 jstack -l <vm-id>
다른 명령 행 창에서 사용하여 해당 출력을 캡처 할 수 있습니다 .
<vm-id>는 작업 관리자 (Windows 및 UNIX의 프로세스 ID) 또는을 사용하여 찾을 수 jps
있습니다.
둘 다 jstack
하고 jps
있는 일 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
jmap
이 기사 에서 다양한 옵션을 살펴보십시오.
메모리 부족에서 힙 덤프를 원하면 옵션으로 Java를 시작할 수 있습니다. -XX:-HeapDumpOnOutOfMemoryError
두 번째 Java 실행 파일의 출력을 일부 파일로 경로 재지 정해야합니다. 그런 다음 SendSignal 을 사용 하여 "-3" 을 두 번째 프로세스 로 보냅니다 .
JDK 1.6 이상을 사용하는 경우 jmap
명령을 사용 하여 Java 프로세스의 힙 덤프를 사용할 수 있습니다 . 조건은 ProcessID를 알고 있어야합니다.
Windows 시스템에있는 경우 작업 관리자를 사용하여 PID를 얻을 수 있습니다. 리눅스 머신의 경우는 같은 명령의 종류를 사용 ps -A | grep java
하거나 netstat -tupln | grep java
또는 top | grep java
, 응용 프로그램에 따라 다릅니다.
그런 다음 jmap -dump:format=b,file=sample_heap_dump.hprof 1234
1234가 PID 인 경우 와 같은 명령을 사용할 수 있습니다 .
hprof 파일을 해석하는 데 사용할 수 있는 다양한 도구가 있습니다 . 사용하기 쉬운 Oracle의 visualvm 도구를 추천합니다.
어떤 이유로 콘솔 / 터미널을 사용할 수 없거나 원하지 않는 경우 다른 해결책이 있습니다. Java 응용 프로그램이 스레드 덤프를 인쇄하도록 할 수 있습니다. 스택 추적을 수집하는 코드는 간단하며 버튼이나 웹 인터페이스에 연결할 수 있습니다.
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> 스레드. 덤프
참조 링크
jstack을 사용하여 스레드 덤프를 얻을 수도 있습니다 (jstack PID> thread.dump). 참조 링크
힙 덤프를 얻는 방법?
jmap 도구를 사용하여 힙 덤프를 가져 오십시오. jmap -F-덤프 : 실시간, 형식 = b, 파일 = heap.bin PID
PID는 애플리케이션의 프로세스 ID를 나타냅니다. 참조 링크
아마도 jcmd ?
Jcmd 유틸리티는 진단 명령 요청을 JVM으로 전송하는 데 사용되며,이 요청은 Java Flight Recording을 제어하고 JVM 및 Java 응용 프로그램의 문제점을 해결하고 진단하는 데 유용합니다.
jcmd 도구는 Oracle의 Java 7에 도입되었으며 특히 Java 프로세스의 ID (aks에서 jps)를 식별하고 힙 덤프 (akin에서 jmap으로)를 획득하고 스레드 덤프를 가져 오기 (jstack으로)로 JVM 응용 프로그램의 문제점을 해결하는 데 특히 유용합니다. ), 시스템 속성 및 명령 줄 플래그 (akin to jinfo) 및 가비지 수집 통계 수집 (akin to jstat)과 같은 가상 시스템 특성보기 jcmd 도구는 "JVM 응용 프로그램의 문제를 조사하고 해결하기위한 스위스 군용 칼"과 "숨겨진 보석"이라고합니다.
다음을 호출 할 때 사용해야하는 프로세스는 다음과 같습니다 jcmd
.
jcmd <pid> GC.heap_dump <file-path>
Java 힙 덤프 수행에 대한 자세한 정보는이를 확인하십시오 .
Visualvm 후속 조치 :
올바른 JVM 인수로 시작하지 않고 (원격 상자에 있음) jvisualvm에서 실행중인 JVM에 "연결할 수없는 경우"원격 상자에서 실행 jstatd
한 다음 직접 연결되어 있다고 가정하면 visualvm에서 "원격 호스트"로 호스트 이름을 두 번 클릭하면 해당 상자의 다른 모든 JVM이 마술 적으로 visualvm에 표시됩니다.
해당 상자의 포트에 "직접 연결"하지 않은 경우 프록시를 통해이 작업을 수행 할 수도 있습니다 .
원하는 프로세스를 볼 수 있으면 jvisualvm에서 드릴하고 모니터 탭-> "힙 덤프"단추를 사용하십시오.
아래 자바 코드는 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 Memory Analysis tool , HeapHero 와 같은 도구 를 사용하여 캡처 된 힙 덤프를 분석 할 수 있습니다 .