性能分析4~jmap命令分析:堆信息、內存溢出

先看一個例子:jmap -heap pid該命令用於:展示pid的整體堆信息,運行結果如下

zhengchao1991deMacBook-Pro:~ zhengchao1991$ jmap -heap 33628
Attaching to process ID 33628, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.79-b02

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 0
   MaxHeapFreeRatio = 100
   MaxHeapSize      = 536870912 (512.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 21757952 (20.75MB)
   MaxPermSize      = 268435456 (256.0MB)
   G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 145227776 (138.5MB)
   used     = 3721808 (3.5493927001953125MB)
   free     = 141505968 (134.9506072998047MB)
   2.5627384116933665% used
From Space:
   capacity = 7864320 (7.5MB)
   used     = 0 (0.0MB)
   free     = 7864320 (7.5MB)
   0.0% used
To Space:
   capacity = 7864320 (7.5MB)
   used     = 0 (0.0MB)
   free     = 7864320 (7.5MB)
   0.0% used
PS Old Generation
   capacity = 157810688 (150.5MB)
   used     = 111650416 (106.47813415527344MB)
   free     = 46160272 (44.02186584472656MB)
   70.74959080084614% used
PS Perm Generation
   capacity = 129499136 (123.5MB)
   used     = 129255872 (123.26800537109375MB)
   free     = 243264 (0.23199462890625MB)
   99.81215009805162% used

45138 interned Strings occupying 4690064 bytes.

這些參數有什麼用呢,我們看如下幾行:

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 

我們可以看出:年輕代三個區域(eden、From Space、To Space)的大小、年老代的大小,以及已經使用、剩餘的大小;
根據這些結果、再結合:
jstat   -gcnew pid:查詢new對象的信息
jstat   -gcold   pid:查詢old對象的信息
這兩條命令中的gc頻率,我們可以得出設置的各個代的大小是否合理,若不合理,如何調整。調整的策略必須以實際應用爲主,比如說:full gc的時間停頓讓客戶感覺明顯,我們分析原因得知是old區域設置的過大,導致一次full gc需要的時間過久;如果我們直接縮小old區域的大小,又回發現full gc過於頻繁;這個時候也許我們需要更換gc的算法........

我們現在開始看jmap的定義:

Usage:
    jmap [option] <pid>
        (to connect to running process)
    jmap [option] <executable <core>
        (to connect to a core file)
    jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)
 
where <option> is one of:
    <none>               to print same info as Solaris pmap
    -heap                to print java heap summary
    -histo[:live]        to print histogram of java object heap; if the "live"
                         suboption is specified, only count live objects
    -permstat            to print permanent generation statistics
    -finalizerinfo       to print information on objects awaiting finalization
    -dump:<dump-options> to dump java heap in hprof binary format
                         dump-options:
                           live         dump only live objects; if not specified,
                                        all objects in the heap are dumped.
                           format=b     binary format
                           file=<file>  dump heap to <file>
                         Example: jmap -dump:live,format=b,file=heap.bin <pid>
    -F                   force. Use with -dump:<dump-options> <pid> or -histo
                         to force a heap dump or histogram when <pid> does not
                         respond. The "live" suboption is not supported
                         in this mode.
    -h | -help           to print this help message
    -J<flag>             to pass <flag> directly to the runtime system

我們先只看幾個簡單的:

 -heap:打印jvm heap的情況

 -histo:打印jvm heap的直方圖。其輸出信息包括類名,對象數量,對象佔用大小。


-heap上面已經給出過例子了,下面我們用-histo來分析內存溢出;
首先寫一個能導致內存溢出的程序:

package deadlock;

import java.math.BigDecimal;
public class Apple {
	public String name;
	public BigDecimal price;
	
	public Apple(String name, BigDecimal price) {
		this.name = name;
		this.price = price;
		System.out.println(name);
	}
}



package deadlock;

import java.math.BigDecimal;
public class TestJmap {
	public static void main(String[] args){
		int i=0;
		while(true){
			new Apple("apple"+i++,BigDecimal.ONE);
		}
	}
}

運行後,執行如下命令:

zhengchao1991deMacBook-Pro:~ zhengchao1991$ jmap -histo 38876

 num     #instances         #bytes  class name
----------------------------------------------
   1:        293271       14077008  java.nio.HeapCharBuffer
   2:        147692        5957408  [C
   3:        147672        3544128  java.lang.String
   4:        146636        3519264  deadlock.Apple
   5:           116        2852184  [I
   6:          6162         794048  <methodKlass>
   7:          6162         718552  <constMethodKlass>
   8:           403         488912  <constantPoolKlass>
   9:           367         300000  <constantPoolCacheKlass>
  10:           403         278312  <instanceKlassKlass>
  11:           565          94464  [B
  12:           466          45816  java.lang.Class
  13:           688          45360  [[I
  14:           613          38072  [S
  15:            46          25024  <objArrayKlassKlass>
  16:            73          24952  <meth
我們看這幾行:
num     #instances         #bytes  class name
----------------------------------------------
   1:        293271       14077008  java.nio.HeapCharBuffer
   2:        147692        5957408  [C
   3:        147672        3544128  java.lang.String
   4:        146636        3519264  deadlock.Apple

發現產生了140000多個Apple實例,140000多個String對象,並且在不斷的增加,這是肯定有問題的,至於怎麼修改,由於我們寫的程序沒有太大的意義,就不做說明了,根據實際情況來修改,此處只是爲了展示。

總結:給的例子並不好,想說的是,根據命令的輸出結果去發現異常信息最重要。

分析問題流程:
1.
如果程序內存不足或者頻繁GC,很有可能存在內存泄露情況,這時候就要藉助JavaDump查看對象的情況。
2.
要製作堆Dump可以直接使用jvm自帶的jmap命令
3.
可以先使用jmap -heap命令查看堆的使用情況,看一下各個堆空間的佔用情況。
4.
使用jmap -histo:[live]查看堆內存中的對象的情況。如果有大量對象在持續被引用,並沒有被釋放掉,那就產生了內存泄露,就要結合代碼,把不用的對象釋放掉。
5.
也可以使用 jmap -dump:format=b,file=<fileName>命令將堆信息保存到一個文件中,再借助jhat命令查看詳細內容
6.
在內存出現泄露、溢出或者其它前提條件下,建議多dump幾次內存,把內存文件進行編號歸檔,便於後續內存整理分析。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章