1. 程式人生 > >eclipse MAT (二)進行OutOfMemoryError的診斷分析

eclipse MAT (二)進行OutOfMemoryError的診斷分析

 這篇帖子只介紹利用MemoryAnalyzer進行簡單的JVM的堆的分析,至於JVM的內部結構是怎麼樣的,這裡不進行分析。好吧,廢話不多說;首先如果我們要分析JVM某個時刻的Heap的物件分配情況,我們就必須要dump這個時刻的JVM的heap(堆);有以下幾個辦法進行dump某個時刻JVM的heap內容:

         1、 使用$JAVA_HOME/bin/jmap -dump來觸發, 
                eg:jmap-dump:format=b,file=/home/longhao/heamdump.out <pid>
         2、 使用$JAVA_HOME/bin/jcosole中的MBean,到MBean>com.sun.management>HotSpotDiagnostic>操作>dumpHeap中,點選 dumpHeap按鈕。生成的dump檔案在   java應用的根目錄下面。
         3、在應用啟動時配置相關的引數 -XX:+HeapDumpOnOutOfMemoryError,當應用丟擲OutOfMemoryError時生成dump檔案。
         4、使用hprof。啟動虛擬機器加入-Xrunhprof:head=site,會生成java.hprof.txt檔案。該配置會導致jvm執行非常的慢,不適合生產環境。

利用MemoryAnalyzer進行Heap分析
        去eclipse官網上去下載MemoryAnalyzer,可以下載非外掛版的,這樣MemoryAnalyzer執行起來比較快,如果是eclipse外掛版進行可能會導致eclipse卡死。本人下載的版本是MemoryAnalyzer-1.2.0.20120530-win32.win32.x86_64。

 一、Java程式碼樣例

package software.architect.MemoryAnalyzer.main;

import java.util.HashMap;
import java.util.Map;

public class JVMOutOfMemoryErrorSimulator {


    private final static int NB_ITERATIONS = 500000;

    // ~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>();
    }
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		System.out.println("JVM OutOfMemoryError 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);
               }

        } 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("simulator done!");

	}

}

二、設定JVM啟動引數
         在此我們把JVM堆的最大記憶體設定為512m,並且讓程式執行過程中出現記憶體溢位的時候會dump當時的JVM對記憶體的內容,所以需要加上XX:+HeapDumpOnOutOfMemoryError引數;因此按照以下步驟在eclipse中加入啟動引數

三、執行程式
        執行程式,然後觀察控制檯的輸出,輸出結果如下:

        從以上的輸出結果來看,此程式已經丟擲了OutOfMemoryError了,並且生成了一個heap檔案,檔名為java_pid3880.hprof,下一步我們就可以拿這個檔案在MemoryAnalyzer分析了。


 四、匯入heap檔案分析

        匯入後按照以下步驟執行進行記憶體洩露的可疑分析;

 滑鼠點選紅色的部分會出現選單,選中選單的第一行的List objects > With Incoming references,就會出現以下介面

        此圖展現了HashMap$Entry被引用的路徑,HashMap$Entry被一個java.util.HashMap的table例項變數引用,而這個HashMap又被JVMOutOfMemberyErrorSimulator類變數leakingMap引用,所以通過這些路徑就很容易找到是哪段程式碼導致的記憶體溢位。
        通過以上分析我們就發現,類JVMOutOfMemberyErrorSimulator的leakingMap變數的內容太大導致了記憶體溢位,所以這樣就能很快定位到問題。
        其中注意Shallow vs. Retained Heap的區別,這裡不進行解釋,點選MemoryAnalyzer的Help選單裡面的HelpContents就可以查詢到
        這裡解釋下Java Local,JVM裡面的類變數,例項變數的名字在JVM裡面都有記錄,而區域性變數是沒有記錄的,所以Java Local在這裡就代表區域性變數。

       總結:以上的這些分析方法是入門級別的,現實中的OOM分析肯定比這更復雜,本人就曾經遇到過很多詭異的OOM。但在一般情況下,如果出現OOM,那麼我們肯定需要對JVM的heap進行分析,這篇帖子是一個很好的思維方法;當然你也可以利用其他的工具進行heap分析,但思路大概都差不多;MemoryAnalyzer是一個不錯的工具,裡面有很多的小工具給我分析,可以花點時間看看。


--------------------- 
作者:zapldy 
來源:CSDN 
原文:https://blog.csdn.net/zapldy/article/details/7727572 
版權宣告:本文為博主原創文章,轉載請附上博文連結!