Як отримати датчик потоку та накопичення процесу Java у Windows, який не працює в консолі


232

У мене є програма Java, яку я запускаю з консолі, яка, в свою чергу, виконує інший процес Java. Я хочу отримати нитку / купу звалища цього дочірнього процесу.

У Unix я міг би зробити, kill -3 <pid>але в Windows AFAIK єдиний спосіб отримати дамп потоку - це Ctrl-Break в консолі. Але це тільки дає мені дамп батьківського процесу, а не дитини.

Чи є інший спосіб отримати цей відвал?


Відповіді:


376

Ви можете використовувати jmapдамп із будь-якого запущеного процесу, якщо припустити, що ви знаєте pid.

Використовуйте диспетчер завдань або монітор ресурсів, щоб отримати це pid. Тоді

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

щоб отримати купу для цього процесу.


jmap недоступний для JDK5 у Windows. Чи є спосіб зняти звалище з JDK5 на Windows?
Сантрон Манібхаратхі

173
Ця тема стала настільки популярною, що я щойно чув, як хтось посилається на кучу сміття як "cheap.bin"
mjaggard

7
Більш просте ім'я файлу: "heap.hprof", як це у форматі HPROF.
MGM

1
Не забудьте скористатися правильним користувачем, який запустив процес Java. У моєму випадку це був tomcat8 ps -C java -o pid sudo -u tomcat8 jmap -dump: format = b, file = <filename> <pid>
bitsabhi

115

Ви плутаєте два різних сміттєзвалища java. kill -3генерує дамп потоку, а не скидання.

Нитка 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}- це найкраща ставка. 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
У моєму Linux Ctrl-C перериває (припиняє) це, я роблю Ctrl- \
nafg

Розглянемо це та його загальний вплив на "Щоб зняти дамп потоку для Windows, CTRL + BREAK". Це фактично залежить від технічного рішення виробника. FE, Lenova, IIRC, є cntrl + fn + p.
ChiefTwoPencils

30

Я думаю, що найкращий спосіб створити .hprof-файл у процесі Linux - це команда jmap . Наприклад:jmap -dump:format=b,file=filename.hprof {PID}


19

Окрім використання згаданого jconsole / visualvm, ви можете використовувати jstack -l <vm-id>в іншому вікні командного рядка та фіксувати цей вихід.

<vm-id> можна знайти за допомогою диспетчера завдань (це ідентифікатор процесу у Windows та unix) або за допомогою jps.

І те, jstackі jpsінше, включено у версію 6 та новішої версії Sun JDK.


Ці інструменти не підтримуються в Java 1.6. У Java 1.6 є лише jconsole.
Vanchinathan Chandrasekaran

7
Ви, можливо, змішуєте JDK і JRE, я чітко згадував JDK. Дивіться документацію щодо інструментів: download.oracle.com/javase/6/docs/technotes/tools/share/… та download.oracle.com/javase/6/docs/technotes/tools/share/…
ankon

17

Я рекомендую Java VisualVM, що поширюється за допомогою JDK (jvisualvm.exe). Він може динамічно підключатися та отримувати доступ до потоків та купи. Я вважав неоціненним для деяких проблем.


2
Це в більшості випадків недоцільно, оскільки до нього прикріплені накладні витрати, а звалища з ниток зазвичай витягуються з виробничих машин.
Хаммад Дар

оригінальне питання стосується процесу "не запущеного". Можливо, jvisualvm не може підключитися.
Jaberino

3
@Jaberino: Ні, мова йде про поточно запущений процес Java в Windows, без консолі, пов'язаної з ним.
Лоуренс Дол

В останніх версіях Java Java VisualVM витіснив JMC / JFR . Дивіться також Які відмінності між JVisualVM та Java Mission Control?
Вадим

16

Якщо ви перебуваєте на сервері-jre 8 і вище, ви можете використовувати це:

jcmd PID GC.heap_dump /tmp/dump

1
У більшості виробничих систем у нас є лише jre, а не jdk. Тож це допомагає.
Pragalathan M

15

Спробуйте один із наведених нижче варіантів.

  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цієї статті


13

Якщо ви хочете купувати пам'ять, яка не вийшла з пам’яті, ви можете запустити Java з цим параметром -XX:-HeapDumpOnOutOfMemoryError

cf Сторінка довідника JVM


Спасибі Даніелю. Де цей файл створений на машині Windows? Чи існує шлях за замовчуванням?
лава

1
@lava Ви можете встановити шлях через -XX: HeapDumpPath, як описано на сторінці Oracle VM Options .
kamczak

Дивовижно. Я хотів провести тест протягом ночі в надії показати витік пам’яті, але переживав за OOM та краху, поки мене немає. Це прекрасно.
Василь

7

Ви можете запустити jconsole(додається до SDK Java 6), а потім підключитися до програми Java. Він покаже вам кожен пробіг потоку та його стеження.


найкраща відповідь на сьогодні! Я не знав цього досі, і це дійсно практично!
Ксерус

7

Ви можете надіслати повідомлення kill -3 <pid>від Cygwin. Ви повинні використовувати параметри Cygwin ps, щоб знайти процеси Windows, а потім просто надіслати сигнал до цього процесу.



3

Якщо ви використовуєте JDK 1.6 або новіші версії, ви можете скористатися jmapкомандою, щоб здійснити купівлю даху процесу Java, умова - вам слід знати ProcessID.

Якщо ви працюєте на машині Windows, ви можете скористатися диспетчером завдань, щоб отримати 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. Я порекомендую інструмент Visualvm Oracle, який простий у використанні.


3

Якщо ви не можете (або не хочете) використовувати консоль / термінал з якихось причин, є альтернативне рішення. Ви можете змусити програму 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());

3

Наведений нижче сценарій використовує PsExec для підключення до іншого сеансу Windows, щоб він працював навіть під час підключення через сервіс віддаленого робочого столу.

Я написав невеликий пакетний сценарій для 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, тому, якщо ви підключитесь через віддалений робочий стіл, можливо, вам знадобиться запустити командний рядок Session 0і запустити його звідти. напр

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

Це підкаже (натисніть значок панелі завдань внизу) до View the messageінтерактивного сеансу, який переведе вас до нової консолі в іншому сеансі, з якого ви можете запустити jvmdump.batскрипт.


2

Як отримати ідентифікатор процесу програми java?

Виконайте команду 'jcmd', щоб отримати ідентифікатор процесу програм Java.

Як отримати звалище теми?

jcmd PID Thread.print> thread.dump

Посилання на посилання

Ви навіть можете використовувати jstack, щоб отримати дамп потоку (jstack PID> thread.dump). Посилання на посилання

Як отримати відвал?

Використовуйте інструмент jmap, щоб отримати скидання. jmap -F -dump: live, format = b, файл = heap.bin PID

PID розшифровується як ідентифікатор процесу програми. Посилання на посилання


1

Може бути, jcmd ?

JcmdУтиліта використовується для надсилання діагностичних командних запитів до JVM, де ці запити корисні для контролю записів польотів Java, усунення несправностей та діагностики програм JVM та Java.

Інструмент jcmd був представлений з Java 7 від Oracle і є особливо корисним для усунення неполадок із програмами JVM, використовуючи його для виявлення ідентифікаторів процесів Java (схожих на jps), придбання купових дампів (схожих на jmap), придбання дампів потоків (подібних до jstack) ), перегляд характеристик віртуальної машини, таких як властивості системи та прапори командного рядка (подібні до jinfo), та отримання статистики збору сміття (подібний до jstat). Інструмент jcmd був названий "ножем швейцарської армії для розслідування та вирішення проблем із вашим додатком JVM" та "прихованим дорогоцінним каменем".

Ось процес, який потрібно використовувати для виклику jcmd:

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

Перегляньте це, щоб отримати додаткові відомості про прийняття Java-відвалив .


0

Підтримка Visualvm:

Якщо ви не можете "підключитися" до свого запущеного JVM від jvisualvm, тому що ви не запустили його з правильних аргументів JVM (а це на віддаленому вікні), запустіть jstatdу віддаленому вікні, а потім, припустивши, що у вас є прямий зв'язок, додайте він як "віддалений хост" у visualvm, двічі клацніть ім'я хоста, а всі інші JVM-адреси в цьому вікні будуть магічно відображатися у visualvm.

Якщо у вас немає "прямого з'єднання" з портами цього вікна, ви також можете це зробити через проксі .

Як тільки ви побачите потрібний процес, перевірте його в jvisualvm та скористайтеся вкладкою монітора -> "heapdump".


0

Нижче java-код використовується для отримання Heap Dump 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;
}

}


0

Для того, щоб прийняти дамп дампа / купі нитки з дочірнього Java-процесу у Windows, вам потрібно визначити дочірній процес Id як перший крок.

Видавши команду: jps, ви зможете отримати всі ідентифікатори процесу Java, які працюють на вашій машині Windows. З цього списку потрібно вибрати ідентифікатор дочірнього процесу. Після того, як у вас є дочірній процес Id, ви можете скористатися різними варіантами для захоплення відвалів потоку та скидів.

Захоплення відвалів нитки:

Існує 8 варіантів зйомки відвалів потоку:

  1. jstack
  2. вбити -3
  3. jvisualVM
  4. JMC
  5. Windows (Ctrl + Break)
  6. ThreadMXBean
  7. Інструменти APM
  8. jcmd

Детально про кожен варіант ви можете ознайомитись у цій статті . Після того, як у вас є скидання ниток захоплення, ви можете використовувати такі інструменти, як fastThread , Samuraito, аналізуючи скиди ниток.

Захоплення відвалів:

Існує 7 варіантів зйомки купи сміття:

  1. jmap

  2. -XX: + HeapDumpOnOutOfMemoryError

  3. jcmd

  4. JVisualVM

  5. JMX

  6. Програмний підхід

  7. Адміністративні консолі

Детально про кожен варіант ви можете ознайомитись у цій статті . Після того, як ви захопили накопичувальний відвал, ви можете використовувати такі інструменти, як інструмент аналізу пам’яті Eclipse , HeapHero для аналізу захоплених відвалів.


-1

У JDK Oracle у нас є команда під назвою jmap (доступна у папці bin Java Home). використання команди відбувається наступним чином

jmap (опція) (pid)

Приклад: jmap -dump: live, format = b, file = heap.bin (pid)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.