Статьи

Анализ кучи Java в JRockit

Эта статья предоставит вам обзор и учебное пособие о том, как вы можете выполнить первоначальный анализ и изоляцию проблемы Java-кучи JRockit с помощью инструмента jrcmd. Более глубокий анализ и учебное пособие с использованием JRockit Mission Control и анализа дампа кучи (только версия JRockit R28 +) будут рассмотрены в следующих статьях.

Для быстрого обзора пространства кучи Java JRockit, пожалуйста, обратитесь к статье ниже:
Место кучи Java JRockit

 

Обзор инструмента JRCMD

jrcmd — это бесплатный инструмент, который доступен в бинарных файлах JRockit. Он позволяет вам генерировать и собирать важные данные из вашей виртуальной машины JRockit, такие как:

— Распределение памяти в памяти процесса Java (Java Heap против собственной памяти)
— Диагностика кучи Java (гистограмма)
— Java-загруженные классы
— Генерация дампа кучи JRockit по требованию ( только версия R28 +)
— Генерация дампа потока
— Подробнее …

Для этой статьи мы создали простую Java-программу с внутренними утечками. Мы будем использовать эту программу, чтобы продемонстрировать, как вы можете использовать jrcmd для первоначального анализа.

Пример программы утечки памяти Java

Эта простая Java-программа просто добавляет данные String в статический HashMap и медленно перетекает в точку, в которой JVM не хватает памяти Java Heap. Эта программа позволит вам визуализировать медленно растущую утечку Java Heap через JRockit jrcmd. Обратите внимание, что для этого примера использовалась куча Java размером 128 МБ (-Xms128m –Xmx128m).

/**
 * JavaHeapLeakSimulator
 * @author Pierre-Hugues Charbonneau
 * http://javaeesupportpatterns.blogspot.com
 */
public class JavaHeapLeakSimulator {

        private final static int NB_ITERATIONS = 500000000;

        // ~1 KB data footprint
        private final static String LEAKING_DATA_PREFIX = "datadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadatadata";

        // Map used to stored our leaking String instances
        private static Map<String, String> leakingMap;

        static {
               leakingMap = new HashMap<String, String>();
        }

        /**
         * @param args
         */
        public static void main(String[] args) {

               System.out.println("Java Heap Leak Simulator 1.0");
               System.out.println("Author: Pierre-Hugues Charbonneau");
               System.out.println("http://javaeesupportpatterns.blogspot.com/");

               try {

                       for (int i = 0; i < NB_ITERATIONS; i++) {

                              String data = LEAKING_DATA_PREFIX + i;
                             
                              // Add data to our leaking Map data structure...
                              leakingMap.put(data, data);
                             
                              // Slowdown the Java program so we can monitor the leak before the OutOfMemoryError condition
                              Thread.sleep(1);
                       }

               } catch (Throwable any) {
                       if (any instanceof java.lang.OutOfMemoryError) {
                              System.out.println("OutOfMemoryError triggered! "
                                             + any.getMessage() + " [" + any + "]");

                       } else {
                              System.out.println("Unexpected Exception! " + any.getMessage()
                                             + " [" + any + "]");
                       }
               }

               System.out.println("JavaHeapLeakSimulator done!");
        }
}

 

JRCMD — начальное исполнение

JRCMD может быть запущен с локального сервера, на котором размещена JVM, которую вы хотите отслеживать, или удаленно через JRockit Mission Control. Исполняемый файл находится в используемом вами JRockit JDK:

<JRockit_JDK_HOME> / bin / jrcmd

Выполнение jrcmd по умолчанию вернет список идентификатора активного процесса Java JRitit, который вы можете отслеживать:

C:\Apps\Weblogic1035\jrockit_160_24_D1.1.2-4\bin>jrcmd
5360 org.ph.javaee.tool.oom.JavaHeapLeakSimulator
5952
6852 jrockit.tools.jrcmd.JrCmd

 

JRCMD — мониторинг кучи Java

Следующий шаг — начать мониторинг использования памяти и гистограммы кучи Java. Гистограмма Java Heap — это снимок самых больших пулов экземпляров Java Class. Это позволит вам точно определить тип данных утечки. Ple

Вы можете выбрать между print_object_summary (краткая сводка) или heap_diagnostics (полная разбивка).

C:\Apps\Weblogic1035\jrockit_160_24_D1.1.2-4\bin>jrcmd 5360 heap_diagnostics

Invoked from diagnosticcommand
======== BEGIN OF HEAPDIAGNOSTIC =========================

Total memory in system: 8465022976 bytes
Available physical memory in system: 5279170560 bytes
-Xmx (maximal heap size) is 134217728 bytes
Heapsize: 134217728 bytes
Free heap-memory: 123592704 bytes


--------- Detailed Heap Statistics: ---------
90.9% 3948k     5468  +3948k [C
 3.0% 128k     5490   +128k java/lang/String
 2.1% 92k     3941    +92k java/util/HashMap$Entry
 1.2% 50k      461    +50k java/lang/Class
 0.8% 35k       21    +35k [Ljava/util/HashMap$Entry;
 0.6% 24k        7    +24k [B
 0.3% 15k      305    +15k [Ljava/lang/Object;
 0.3% 14k      260    +14k java/net/URL
 0.2% 6k      213     +6k java/util/LinkedHashMap$Entry
 0.1% 4k      211     +4k java/io/ExpiringCache$Entry
 0.1% 2k        4     +2k [Ljrockit/vm/FCECache$FCE;
 0.0% 1k       50     +1k [Ljava/lang/String;
 0.0% 1k       10     +1k java/lang/Thread
 0.0% 1k       61     +1k java/util/Hashtable$Entry
 0.0% 1k        7     +1k [I
 0.0% 0k       19     +0k java/util/HashMap
 0.0% 0k       19     +0k java/lang/ref/WeakReference
 0.0% 0k        7     +0k [Ljava/util/Hashtable$Entry;
 0.0% 0k       19     +0k java/util/Locale
 0.0% 0k       11     +0k java/lang/ref/SoftReference
 0.0% 0k        1     +0k [S
…………………………………………………

— Первый столбец соответствует вкладу типа объекта Class в размер кучи Java в%
— Второй столбец соответствует фрагменту памяти типа объекта Class в K
— Третий столбец соответствует # экземплярам Class определенного типа
— Четвертый столбец соответствует размеру памяти delta — / + определенного типа.

Как видно из приведенного выше снимка, самый большой тип данных — это [C (char в нашем случае) & java.lang.String. Чтобы увидеть, какие типы данных просочились, вам нужно сгенерировать несколько снимков. Частота будет зависеть от скорости утечки. В нашем примере найдите еще один снимок, сделанный через 5 минут:

# After 5 minutes
--------- Detailed Heap Statistics: ---------
93.9% 26169k    28746 +12032k [C
 2.4% 674k    28768   +295k java/lang/String
 2.3% 637k    27219   +295k java/util/HashMap$Entry
 0.9% 259k       21   +128k [Ljava/util/HashMap$Entry;
 0.2% 50k      462     +0k java/lang/Class
 0.1% 24k        7     +0k [B

# After 5 more minutes
--------- Detailed Heap Statistics: ---------
94.5% 46978k    50534 +20809k [C
 2.4% 1184k    50556   +510k java/lang/String
 2.3% 1148k    49007   +510k java/util/HashMap$Entry
 0.5% 259k       21     +0k [Ljava/util/HashMap$Entry;
 0.1% 50k      462     +0k java/lang/Class

Третий и четвертый столбцы показывают постоянный рост. Как вы можете видеть, в нашем случае текущие данные — это [C, java.lang.String и java.util.HashMap $ Entry, которые увеличились с ~ 4 МБ до 28 МБ, 50 МБ и растут…

Легко определить Утечки типа данных с этим подходом, но как насчет источника (первопричины) утечки типа данных? Здесь jrcmd больше не нужен. Более глубокий анализ утечек памяти потребует от вас использовать либо JRockit Mission Control, либо анализ дампа кучи (только JRockit R28 +).

В заключение, прежде чем вы сможете сделать вывод об истинной утечке кучи Java, убедитесь, что моментальные снимки jrcmd сделаны после хотя бы одного полного GC между перехватами (что вас интересует, это утечка OldGen, например, объекты Java, сохранившиеся в основных коллекциях GC).

Генерация дампа потоков JRCMD

Анализ дампа потока имеет решающее значение для застрявших проблем, связанных с потоками, но также может быть полезен для устранения некоторых типов проблем Java Heap. Например, он может точно определить источник внезапного увеличения кучи Java, обнажив виновный поток (ы), выделяющий большой объем памяти на кучи Java за короткий промежуток времени. Дамп потока может быть сгенерирован с помощью опции jrcmd print_threads.

** Thread Dump captured from our sample Java program after removing the Thread.sleep() and increasing the Java Heap capacity **

C:\Apps\Weblogic1035\jrockit_160_24_D1.1.2-4\bin>jrcmd 5808 print_threads
5808:

===== FULL THREAD DUMP ===============
Mon Apr 09 09:08:08 2012
Oracle JRockit(R) R28.1.3-11-141760-1.6.0_24-20110301-1429-windows-ia32

"Main Thread" id=1 idx=0x4 tid=6076 prio=5 alive, native_blocked
    at jrockit/vm/Allocator.getNewTla(II)V(Native Method)
    at jrockit/vm/Allocator.allocObjectOrArray(Allocator.java:354)[optimized]
    at java/util/Arrays.copyOfRange(Arrays.java:3209)[inlined]
    at java/lang/String.<init>(String.java:215)[inlined]
    at java/lang/StringBuilder.toString(StringBuilder.java:430)[optimized]
    at org/ph/javaee/tool/oom/JavaHeapLeakSimulator.main(JavaHeapLeakSimulator.java:38)
    at jrockit/vm/RNI.c2java(IIIII)V(Native Method)
    -- end of trace
……………………………………….

Мы можем видеть, что наш пример Java-программы создает множество объектов java.lang.String из «основного потока», выполняющего нашу программу JavaHeapLeakSimulator.

Вывод

Я надеюсь, что эта статья помогла вам понять, что вы можете использовать инструмент JRockit jrcmd для быстрого анализа кучи Java. Я с нетерпением жду ваших комментариев и вопросов.

Будущие статьи будут включать более глубокое руководство по анализу Java-кучи и дампа кучи JRockit.