Как получить нить и кучу дампа процесса 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

Попробуйте один из вариантов ниже.

  1. Для 32-разрядной JVM:

    jmap -dump:format=b,file=<heap_dump_filename> <pid>
    
  2. Для 64-разрядного JVM (явно цитирования):

    jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
    
  3. Для 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

Ответ 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:

  1. Перейдите к jcmd <pid> GC.heap_dump <file-path>
  2. В котором
  3. pid: идентификатор процесса Java, для которого будет захвачен дамп кучи.
  4. путь к файлу: путь к файлу, в котором печатается дамп кучи.

Проверьте это для получения дополнительной информации о получении дампа кучи Java.

Ответ 18

В Oracle JDK у нас есть команда jmap (доступна в папке bin в Java Home). использование команды происходит следующим образом

jmap (опция) (pid)

Пример: jmap -dump: live, format = b, file = heap.bin(pid)