五、jdk工具之jmap(java memory map)、 mat之四--結合mat對內存泄露的分析、jhat之二--結合jmap生成的dump結果在瀏覽器上展示


目錄

一、jdk工具之jps(JVM Process Status Tools)命令使用

二、jdk命令之javah命令(C Header and Stub File Generator)

三、jdk工具之jstack(Java Stack Trace)

四、jdk工具之jstat命令(Java Virtual Machine Statistics Monitoring Tool)

四、jdk工具之jstat命令2(Java Virtual Machine Statistics Monitoring Tool)詳解

五、jdk工具之jmap(java memory map)、 mat之四--結合mat對內存泄露的分析

六、jdk工具之jinfo命令(Java Configuration Info)

七、jdk工具之jconsole命令(Java Monitoring and Management Console)

八、jdk工具之JvisualVM、JvisualVM之二--Java程序性能分析工具Java VisualVM

九、jdk工具之jhat命令(Java Heap Analyse Tool)

十、jdk工具之Jdb命令(The Java Debugger)

十一、jdk命令之Jstatd命令(Java Statistics Monitoring Daemon)

十一、jdk命令之Jstatd命令(Java Statistics Monitoring Daemon)

十二、jdk工具之jcmd介紹(堆轉儲、堆分析、獲取系統信息、查看堆外內存)

十三、jdk命令之Java內存之本地內存分析神器:NMT 和 pmap

 

 

1、介紹

打印出某個java進程(使用pid)內存內的,所有‘對象’的情況(如:產生那些對象,及其數量)。

可以輸出所有內存中對象的工具,甚至可以將VM 中的heap,以二進制輸出成文本。使用方法 jmap -histo pid。如果連用SHELL jmap -histo pid>a.log可以將其保存到文本中去,在一段時間后,使用文本對比工具,可以對比出GC回收了哪些對象。jmap -dump:format=b,file=outfile 3024可以將3024進程的內存heap輸出出來到outfile文件里,再配合MAT(內存分析工具(Memory Analysis Tool),使用參見:http://blog.csdn.net/fenglibing/archive/2011/04/02/6298326.aspx)或與jhat (Java Heap Analysis Tool)一起使用,能夠以圖像的形式直觀的展示當前內存是否有問題。

查看該進程(pid)下堆內存的使用情況:

jmap -heap pid

快速定位內存泄漏的方法:只統計存活的對象

jmap -histo:live pid

還可以導出:

jmap -histo:live pid >1.txt將信息輸出到指定文件中

 

2、命令格式

SYNOPSIS

       jmap [ option ] pid

       jmap [ option ] executable core

       jmap [ option ] [server-id@]remote-hostname-or-IP

3、參數說明

1)、options: 

executable Java executable from which the core dump was produced.

(可能是產生core dump的java可執行程序)

core 將被打印信息的core dump文件

remote-hostname-or-IP 遠程debug服務的主機名或ip

server-id 唯一id,假如一台主機上多個遠程debug服務 

2)、基本參數:

-dump:[live,]format=b,file=<filename> 使用hprof二進制形式,輸出jvm的heap內容到文件=. live子選項是可選的,假如指定live選項,那么只輸出活的對象到文件. 

-finalizerinfo 打印正等候回收的對象的信息.

-heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情況.

-histo[:live] 打印每個class的實例數目,內存占用,類全名信息. VM的內部類名字開頭會加上前綴”*”. 如果live子參數加上后,只統計活的對象數量. 

-permstat 打印classload和jvm heap長久層的信息. 包含每個classloader的名字,活潑性,地址,父classloader和加載的class數量. 另外,內部String的數量和占用內存數也會打印出來. 

-F 強迫.在pid沒有相應的時候使用-dump或者-histo參數. 在這個模式下,live子參數無效. 

-h | -help 打印輔助信息 

-J 傳遞參數給jmap啟動的jvm. 

pid 需要被打印配相信息的java進程id,創業與打工的區別 - 博文預覽,可以用jps查問.

4、使用示例

tasklist命令查看進程id(Tasklist"是 winxp/win2003/vista/win7/win8下的命令,用來顯示運行在本地或遠程計算機上的所有進程,帶有多個執行參數。)

 

常用的參數如下:

histo

jmap -histo pid 展示class的內存情況

展示的信息為編號,實例數,字節,類名

-finalizerinfo   

打印等待回收的對象信息

jmap -finalizerinfo 4783
Attaching to process ID 4783, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.171-b11
Number of objects pending for finalization: 0
[root@ip-172-29-206-104 applogs]#

 

heap

jmap -heap pid 展示pid的整體堆信息

復制代碼
    jmap -heap 2464  
    JVM version is 16.3-b01  
      
    using thread-local object allocation.  
    Parallel GC with 13 thread(s)  
      
    Heap Configuration:  
       MinHeapFreeRatio = 40  
       MaxHeapFreeRatio = 70  
       MaxHeapSize      = 8436842496 (8046.0MB)  
       NewSize          = 5439488 (5.1875MB)  
       MaxNewSize       = 17592186044415 MB  
       OldSize          = 5439488 (5.1875MB)  
       NewRatio         = 2  
       SurvivorRatio    = 8  
       PermSize         = 21757952 (20.75MB)  
       MaxPermSize      = 88080384 (84.0MB)  
      
    Heap Usage:  
    PS Young Generation  
    Eden Space:  
       capacity = 87883776 (83.8125MB)  
       used     = 31053080 (29.614524841308594MB)  
       free     = 56830696 (54.197975158691406MB)  
       35.33425782706469% used  
    From Space:  
       capacity = 13828096 (13.1875MB)  
       used     = 196608 (0.1875MB)  
       free     = 13631488 (13.0MB)  
       1.4218009478672986% used  
    To Space:  
       capacity = 16384000 (15.625MB)  
       used     = 0 (0.0MB)  
       free     = 16384000 (15.625MB)  
       0.0% used  
    PS Old Generation  
       capacity = 156172288 (148.9375MB)  
       used     = 27098208 (25.842864990234375MB)  
       free     = 129074080 (123.09463500976562MB)  
       17.35148299805917% used  
    PS Perm Generation  
       capacity = 88080384 (84.0MB)  
       used     = 50847592 (48.492042541503906MB)  
       free     = 37232792 (35.507957458496094MB)  
       57.728622073218936% used  
復制代碼

說明如下

Parallel GC with 13 thread(s)   #13個gc線程  
  
Heap Configuration:#堆內存初始化配置  
   MinHeapFreeRatio = 40  #-XX:MinHeapFreeRatio設置JVM堆最小空閑比率  
   MaxHeapFreeRatio = 70  #-XX:MaxHeapFreeRatio設置JVM堆最大空閑比率  
   MaxHeapSize      = 8436842496 (8046.0MB)#-XX:MaxHeapSize=設置JVM堆的最大大小  
   NewSize          = 5439488 (5.1875MB) #-XX:NewSize=設置JVM堆的‘新生代’的默認大小  
   MaxNewSize       = 17592186044415 MB  #-XX:MaxNewSize=設置JVM堆的‘新生代’的最大大小  
   OldSize          = 5439488 (5.1875MB) #-XX:OldSize=設置JVM堆的‘老生代’的大小  
   NewRatio         = 2 #-XX:NewRatio=:‘新生代’和‘老生代’的大小比率  
   SurvivorRatio    = 8 #-XX:SurvivorRatio=設置年輕代中Eden區與Survivor區的大小比值  
   PermSize         = 21757952 (20.75MB) #-XX:PermSize=<value>:設置JVM堆的‘永生代’的初始大小  
   MaxPermSize      = 88080384 (84.0MB) #-XX:MaxPermSize=<value>:設置JVM堆的‘永生代’的最大大小  
  
Heap Usage:  
PS Young Generation  
Eden Space:#Eden區內存分布  
   capacity = 87883776 (83.8125MB)  
   used     = 31053080 (29.614524841308594MB)  
   free     = 56830696 (54.197975158691406MB)  
   35.33425782706469% used  
From Space:#其中一個Survivor區的內存分布  
   capacity = 13828096 (13.1875MB)  
   used     = 196608 (0.1875MB)  
   free     = 13631488 (13.0MB)  
   1.4218009478672986% used  
To Space:#另一個Survivor區的內存分布  
   capacity = 16384000 (15.625MB)  
   used     = 0 (0.0MB)  
   free     = 16384000 (15.625MB)  
   0.0% used  
PS Old Generation#當前的Old區內存分布  
   capacity = 156172288 (148.9375MB)  
   used     = 27098208 (25.842864990234375MB)  
   free     = 129074080 (123.09463500976562MB)  
   17.35148299805917% used  
PS Perm Generation#當前的 “永生代” 內存分布  
   capacity = 88080384 (84.0MB)  
   used     = 50847592 (48.492042541503906MB)  
   free     = 37232792 (35.507957458496094MB)  
   57.728622073218936% used

mat為eclipse的一個內存分析插件,幫助查找內存泄漏和減少內存消耗。

首先基於jmap導出的堆信息

jmap -dump:live,format=b,file=test.bin 29030  

 准備代碼:

class User {  
    private String id;  
    private String name;  
  
    public String getId() {  
        return id;  
    }  
  
    public void setId(String id) {  
        this.id = id;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public User(String id, String name) {  
        super();  
        this.id = id;  
        this.name = name;  
    }  
  
}  

 main方法:

public static void main(String[] args) {  
    List<User> list = new ArrayList<User>();  
    for (int i = 1; i < 10000; i++) {  
        User o = new User(i + "", System.currentTimeMillis() + "");  
        list.add(o);  
        o = null;  
    }  
    System.out.println("end");  
    try {  
        Thread.sleep(100000000l);  
    } catch (InterruptedException e) {  
        e.printStackTrace();  
    }  
}  

 執行之后用jmap輸出堆信息

然后導入分析工具

我們可以看到圖形化展示:



 然后我們點擊

Problem Suspect 1

如下所示:



 然后點擊詳情



 我們可以看到有很多的User對象



 這些對象有可能會溢出,然后我們打開OQL窗口看他是否為null,執行如下OQL語句

SELECT u FROM org.learn.util.User u WHERE (u.value = null)  

 

 結果如下:


 也就是說這個是null,但是仍然有強引用存在,gc的時候是不能回收的,這樣就會出現內存的溢出問題

 示例2:如何用mat分析內存問題

我用MAT打開了heap.bin,很容易看出,char[]的數量出其意料的多,占用90%以上的內存 。一般來說,char[]在JVM確實會占用很多內存,數量也非常多,因為String對象以char[]作為內部存儲。但是這次的char[]太貪婪 了,仔細一觀察,發現有數萬計的char[],每個都占用數百K的內存 。這個現象說明,Java程序保存了數以萬計的大String對象 。結合程序的邏輯,這個是不應該的,肯定在某個地方出了問題。

在可疑的char[]中,任意挑了一個,使用Path To GC Root功能,找到該char[]的引用路徑,發現String對象是被一個HashMap中引用的 。這個也是意料中的事情,Java的內存泄露多半是因為對象被遺留在全局的HashMap中得不到釋放。不過,該HashMap被用作一個緩存,設置了緩 存條目的閾值,導達到閾值后會自動淘汰。從這個邏輯分析,應該不會出現內存泄露的。雖然緩存中的String對象已經達到數萬計,但仍然沒有達到預先設置 的閾值(閾值設置地比較大,因為當時預估String對象都比較小)。

 

    但是,另一個問題引起了我的注意:為什么緩存的String對象如此巨大?內部char[]的長度達數百K。雖然緩存中的 String對象數量還沒有達到閾值,但是String對象大小遠遠超出了我們的預期,最終導致內存被大量消耗,形成內存泄露的跡象(准確說應該是內存消 耗過多) 。

 

    就這個問題進一步順藤摸瓜,看看String大對象是如何被放到HashMap中的。通過查看程序的源代碼,我發現,確實有String大對象,不 過並沒有把String大對象放到HashMap中,而是把String大對象進行split(調用String.split方法),然后將split出 來的String小對象放到HashMap中 了。

 

    這就奇怪了,放到HashMap中明明是split之后的String小對象,怎么會占用那么大空間呢?難道是String類的split方法有問題?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM