Как получить нить и кучу дампа процесса Java в Windows, который не работает в консоли
У меня есть приложение Java, которое я запускаю с консоли, которая, в свою очередь, выполняет другой процесс Java. Я хочу получить нить/кучу этого дочернего процесса.
В Unix я мог бы kill -3 <pid>
но в Windows AFAIK единственным способом получить дамп потока является Ctrl-Break в консоли. Но это дает мне только дамп родительского процесса, а не ребенка.
Есть ли другой способ получить кучу кучи?
Ответы
Ответ 1
Вы можете использовать jmap
чтобы получить дамп любого запущенного процесса, предполагая, что вы знаете pid
.
Используйте диспетчер задач или монитор ресурсов, чтобы получить pid
. затем
jmap -dump:format=b,file=cheap.hprof <pid>
чтобы получить кучу для этого процесса.
Ответ 2
Вы смешиваете две разные свалки java. kill -3
генерирует дамп потока, а не кучу дампа.
Thread dump = трассировка стека для каждого потока на выходе JVM в stdout в виде текста.
Heap dump = содержимое памяти для вывода процесса JVM в двоичный файл.
Чтобы взять дамп потока в Windows, CTRL + BREAK, если ваш JVM является процессом переднего плана, является самым простым способом. Если у вас есть UNIX-подобная оболочка в Windows, например Cygwin или MobaXterm, вы можете использовать kill -3 {pid}
как вы можете в Unix.
Чтобы взять дамп потока в Unix, CTRL + C, если ваш JVM является процессом переднего плана или kill -3 {pid}
будет работать до тех пор, пока вы получите правильный PID для JVM.
С любой платформой Java поставляется с несколькими утилитами, которые могут помочь. Для дампов потоков jstack {pid}
- ваш лучший jstack {pid}
. http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html
Просто, чтобы закончить вопрос свалить: кучи кучи обычно не используются, потому что их трудно интерпретировать. Но у них есть много полезной информации, если вы знаете, где и как смотреть на них. Наиболее распространенное использование - обнаружение утечек памяти. Рекомендуется установить -D
в командной строке java так, чтобы дамп кучи генерировался автоматически на OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError
Но вы также можете вручную запускать кучу кучи. Наиболее распространенным способом является использование java-утилиты jmap
.
ПРИМЕЧАНИЕ. Эта утилита недоступна на всех платформах. Начиная с JDK 1.6, jmap
доступен в Windows.
Пример командной строки будет выглядеть примерно так:
jmap -dump:file=myheap.bin {pid of the JVM}
Выход "myheap.bin" не читается человеком (для большинства из нас), и вам понадобится инструмент для его анализа. Мое предпочтение - MAT. http://www.eclipse.org/mat/
Ответ 3
Я думаю, что лучший способ создать файл .hprof в Linux-процессе - это команда jmap. Например: jmap -dump:format=b,file=filename.hprof {PID}
Ответ 4
В дополнение к использованию упомянутого jconsole/visualvm вы можете использовать jstack -l <vm-id>
в другом окне командной строки и захватить этот вывод.
< vm-id > можно найти с помощью диспетчера задач (это идентификатор процесса для окон и unix) или с помощью jps
.
Оба jstack
и jps
включены в Sun JDK версии 6 и выше.
Ответ 5
Я рекомендую Java VisualVM, распространяемый вместе с JDK (jvisualvm.exe). Он может подключаться динамически и получать доступ к потокам и куче. Я нашел в бесценных для некоторых проблем.
Ответ 6
Попробуйте один из вариантов ниже.
-
Для 32-разрядной JVM:
jmap -dump:format=b,file=<heap_dump_filename> <pid>
-
Для 64-разрядного JVM (явно цитирования):
jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
-
Для 64-битной JVM с алгоритмом G1GC в параметрах VM (только куча живых объектов генерируется алгоритмом G1GC):
jmap -J-d64 -dump:live,format=b,file=<heap_dump_filename> <pid>
Связанный вопрос с SE: ошибка дампа Java-кучи с помощью команды jmap: преждевременная EOF
Взгляните на различные варианты jmap
в этой статье
Ответ 7
Если вы находитесь на сервере-jre 8 и выше, вы можете использовать это:
jcmd PID GC.heap_dump /tmp/dump
Ответ 8
Если вы хотите использовать heapdump в памяти, вы можете запустить Java с опцией -XX:-HeapDumpOnOutOfMemoryError
c.f. Справочная страница опций JVM
Ответ 9
Вы можете запустить jconsole
(в комплекте с Java 6 SDK), затем подключиться к вашему Java-приложению. Он покажет вам каждый поток и его трассировку стека.
Ответ 10
Вы можете отправить kill -3 <pid>
из Cygwin. Вы должны использовать параметры Cygwin ps
для поиска оконных процессов, а затем просто отправить сигнал этому процессу.
Ответ 11
Вы должны перенаправить вывод из второго исполняемого файла Java в некоторый файл. Затем используйте SendSignal для отправки "-3" вашему второму процессу.
Ответ 12
Если вы используете JDK 1.6 или выше, вы можете использовать команду jmap
, чтобы взять кучу дампа процесса Java, условие - вы должны знать ProcessID.
Если вы находитесь на Windows Machine, вы можете использовать диспетчер задач для получения PID. Для Linux-машины вы можете использовать различные команды типа 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, который прост в использовании.
Ответ 13
Я написал небольшую партию script для Java 8 (используя PsExec
и jcmd
) с именем jvmdump.bat
, который сбрасывает потоки, кучи, свойства системы и аргументы 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%
Он должен быть запущен в том же сеансе Windows пользователя, который запустил JVM, поэтому, если вы подключаетесь через Remote Desktop, вам может потребоваться запустить командную строку в Session 0
и запустить ее оттуда. например.
%PsExec% -s -h -d -i 0 cmd.exe
Это предложит вам (щелкните значок панели задач внизу) до View the message
в интерактивном сеансе, который приведет вас к новой консоли в другом сеансе, из которого вы можете запустить jvmdump.bat
script.
Ответ 14
Если вы не можете (или не хотите) использовать консоль/терминал по какой-либо причине, есть альтернативное решение. Вы можете заставить приложение 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());
Ответ 15
Visualvm продолжение:
Если вы "не можете подключиться" к вашей работающей JVM из jvisualvm, потому что вы не запустили его с правильными аргументами JVM (и это на удаленном блоке), запустите jstatd
на удаленном блоке, а затем, если у вас есть прямое соединение, добавьте его в качестве "удаленного хоста" в visualvm, дважды щелкните имя хоста, и все остальные JVM в этом окне будут волшебным образом отображаться в visualvm.
Если у вас нет "прямого соединения" с портами на этом ящике, вы также можете сделать это через прокси.
Как только вы увидите нужный вам процесс, просмотрите его в jvisualvm и используйте вкладку монитора → кнопку "heapdump".
Ответ 16
Ниже приведен код Java, который используется для получения дампа кучи Java-процесса путем предоставления PID. Программа использует удаленное соединение 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;
}
}
Ответ 17
Может быть, JCMD?
Утилита Jcmd используется для отправки запросов диагностических команд в JVM, где эти запросы полезны для управления записями о полетах Java, устранения неполадок и диагностики приложений JVM и Java.
Инструмент jcmd был представлен в Oracle Java 7 и особенно полезен для устранения неполадок с приложениями JVM, поскольку он используется для определения идентификаторов процессов Java (сродни jps), получения дампов кучи (сродни jmap), получения дампов потоков (сродни jstack).), просмотр характеристик виртуальной машины, таких как системные свойства и флаги командной строки (сродни jinfo), и получение статистики сбора мусора (сродни jstat). Инструмент jcmd был назван "швейцарским армейским ножом для исследования и решения проблем с вашим приложением JVM" и "скрытым камнем".
Вот процесс, который вам нужно использовать при вызове jcmd
:
- Перейдите к
jcmd <pid> GC.heap_dump <file-path>
- В котором
- pid: идентификатор процесса Java, для которого будет захвачен дамп кучи.
- путь к файлу: путь к файлу, в котором печатается дамп кучи.
Проверьте это для получения дополнительной информации о получении дампа кучи Java.
Ответ 18
В Oracle JDK у нас есть команда jmap (доступна в папке bin в Java Home). использование команды происходит следующим образом
jmap (опция) (pid)
Пример: jmap -dump: live, format = b, file = heap.bin(pid)
Ответ 19
Хотя это старый пост, но с последними версиями Java, у нас есть новые доступные опции. Вы можете найти полный список доступных вариантов здесь https://technicalsand.com/how-to-take-heap-dumps-8-different-options/