java命令--jmap命令使用


jdk安裝后會自帶一些小工具,jmap命令(Java Memory Map)是其中之一。主要用於打印指定Java進程(或核心文件、遠程調試服務器)的共享對象內存映射或堆內存細節。

jmap命令可以獲得運行中的jvm的堆的快照,從而可以離線分析堆,以檢查內存泄漏,檢查一些嚴重影響性能的大對象的創建,檢查系統中什么對象最多,各種對象所占內存的大小等等。可以使用jmap生成Heap Dump。 

java memory = direct memory(直接內存) + jvm memory(MaxPermSize +Xmx)

1)直接內存跟堆
直接內存則是一塊由程序本身管理的一塊內存空間,它的效率要比標准內存池要高,主要用於存放網絡通信時數據緩沖和磁盤數據交換時的數據緩沖。 
DirectMemory容量可以通過 -XX:MaxDirectMemorySize指定,如果不指定,則默認為與Java堆的最大值(-Xmx指定)一樣。但是,在OSX上的最新版本的 JVM,對直接內存的默認大小進行修訂,改為“在不指定直接內存大小的時默認分配的直接內存大小為64MB”,可以通過 -XX:MaxMemorySize來顯示指定直接內存的大小。 
2)堆(Heap)和非堆(Non-heap)內存

按照官方的說法:“Java 虛擬機具有一個堆,堆是運行時數據區域,所有類實例和數組的內存均從此處分配。堆是在 Java 虛擬機啟動時創建的。”“在JVM中堆之外的內存稱為非堆內存(Non-heap memory)”。
可以看出JVM主要管理兩種類型的內存:堆和非堆。簡單來說堆就是Java代碼可及的內存,是留給開發人員使用的;非堆就是JVM留給自己用的,
所以方法區、JVM內部處理或優化所需的內存(如JIT編譯后的代碼緩存)、每個類結構(如運行時常數池、字段和方法數據)以及方法和構造方法的代碼都在非堆內存中。

3)棧與堆

棧解決程序的運行問題,即程序如何執行,或者說如何處理數據;堆解決的是數據存儲的問題,即數據怎么放、放在哪兒。

在Java中一個線程就會相應有一個線程棧與之對應,這點很容易理解,因為不同的線程執行邏輯有所不同,因此需要一個獨立的線程棧。而堆則是所有線程共享的。棧因為是運行單位,因此里面存儲的信息都是跟當前線程(或程序)相關信息的。包括局部變量、程序運行狀態、方法返回值等等;而堆只負責存儲對象信息。

Java的堆是一個運行時數據區,類的(對象從中分配空間。這些對象通過new、newarray、anewarray和multianewarray等 指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時 動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢。 棧的優勢是,存取速度比堆要快,僅次於寄存器,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類 型的變量(,int, short, long, byte, float, double, boolean, char)和對象句柄。

線程占用大小在MaxPermSize中進行內存申請和分配

 

什么是堆Dump

堆Dump是反應Java堆使用情況的內存鏡像,其中主要包括系統信息、虛擬機屬性、完整的線程Dump、所有類和對象的狀態等。 一般,在內存不足、GC異常等情況下,我們就會懷疑有內存泄露。這個時候我們就可以制作堆Dump來查看具體情況。分析原因。

基礎知識

Java虛擬機的內存組成以及堆內存介紹 Java GC工作原理 常見內存錯誤:

outOfMemoryError 年老代內存不足。
outOfMemoryError:PermGen Space 永久代內存不足。
outOfMemoryError:GC overhead limit exceed 垃圾回收時間占用系統運行時間的98%或以上。

 

一、介紹

打印出某個java進程(使用pid)內存內的,所有‘對象’的情況(如:產生那些對象,及其數量)。它的用途是為了展示java進程的內存映射信息,或者堆內存詳情。 

可以輸出所有內存中對象的工具,甚至可以將VM 中的heap,以二進制輸出成文本。

 

jmap -heap:format=b pid   bin格式  javaversion 1.5
jmap -dump:format=b,file=filename pid javaversion >1.6
 

jmap -dump:format=b,file=outfile 3024可以將3024進程的內存heap輸出出來到outfile文件里,再配合MAT(內存分析工具(Memory Analysis Tool)或與jhat (Java Heap Analysis Tool)一起使用,能夠以圖像的形式直觀的展示當前內存是否有問題。

 

64位機上使用需要使用如下方式:

jmap -J-d64 -heap pid

 

二、命令格式

SYNOPSIS
jmap [ option ] pid  (to connect to remote debug server)   
jmap [ option ] executable core  (to connect to remote debug server)   
jmap [ option ] [server-id@]remote-hostname-or-IP  (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的直方圖。其輸出信息包括類名,對象數量,對象占用大小。
-histo:live :同上,但是只答應存活對象的情況
-permstat:打印permanent generation heap情況
復制代碼

 

參數說明:

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)基本參數:

<no option> 如果使用不帶選項參數的jmap打印共享對象映射,將會打印目標虛擬機中加載的每個共享對象的起始地址、映射大小以及共享對象文件的路徑全稱。這與Solaris的pmap工具比較相似。
-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<flag> 傳遞參數給jmap啟動的jvm. 
pid 需要被打印配相信息的java進程id,可以用jps查問

 

從安全點日志看,從Heap Dump開始,整個JVM都是停頓的,考慮到IO(雖是寫到Page Cache,但或許會遇到background flush),幾G的Heap可能產生幾秒的停頓,在生產環境上執行時謹慎再謹慎。

live的選項,實際上是產生一次Full GC來保證只看還存活的對象。有時候也會故意不加live選項,看歷史對象。

Dump出來的文件建議用JDK自帶的VisualVM或Eclipse的MAT插件打開,對象的大小有兩種統計方式:

  • 本身大小(Shallow Size):對象本來的大小。
  • 保留大小(Retained Size): 當前對象大小 + 當前對象直接或間接引用到的對象的大小總和。

看本身大小時,占大頭的都是char[] ,byte[]之類的,沒什么意思(用jmap -histo:live pid 看的也是本身大小)。所以需要關心的是保留大小比較大的對象,看誰在引用這些char[], byte[]。

(MAT能看的信息更多,但VisualVM勝在JVM自帶,用法如下:命令行輸入jvisualvm,文件->裝入->堆Dump->檢查 -> 查找20保留大小最大的對象,就會觸發保留大小的計算,然后就可以類視圖里瀏覽,按保留大小排序了)

 

三、使用實例

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

#ps -ef|grep tomcat  #獲取tomcat的pid

復制代碼
[root@localhost ~]# jmap -heap 27900
Attaching to process ID 27900, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 20.45-b01
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration: #堆內存初始化配置
   MinHeapFreeRatio = 40     #-XX:MinHeapFreeRatio設置JVM堆最小空閑比率  
   MaxHeapFreeRatio = 70   #-XX:MaxHeapFreeRatio設置JVM堆最大空閑比率  
   MaxHeapSize = 100663296 (96.0MB)   #-XX:MaxHeapSize=設置JVM堆的最大大小
   NewSize = 1048576 (1.0MB)     #-XX:NewSize=設置JVM堆的‘新生代’的默認大小
   MaxNewSize = 4294901760 (4095.9375MB) #-XX:MaxNewSize=設置JVM堆的‘新生代’的最大大小
   OldSize = 4194304 (4.0MB)  #-XX:OldSize=設置JVM堆的‘老生代’的大小
   NewRatio = 2    #-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
   SurvivorRatio = 8  #-XX:SurvivorRatio=設置年輕代中Eden區與Survivor區的大小比值
   PermSize = 12582912 (12.0MB) #-XX:PermSize=<value>:設置JVM堆的‘持久代’的初始大小  
   MaxPermSize = 67108864 (64.0MB) #-XX:MaxPermSize=<value>:設置JVM堆的‘持久代’的最大大小  
Heap Usage:
New Generation (Eden + 1 Survivor Space): #新生代區內存分布,包含伊甸園區+1個Survivor區
   capacity = 30212096 (28.8125MB)
   used = 27103784 (25.848182678222656MB)
   free = 3108312 (2.9643173217773438MB)
   89.71169693092462% used
Eden Space: #Eden區內存分布
   capacity = 26869760 (25.625MB)
   used = 26869760 (25.625MB)
   free = 0 (0.0MB)
   100.0% used
From Space: #其中一個Survivor區的內存分布
   capacity = 3342336 (3.1875MB)
   used = 234024 (0.22318267822265625MB)
   free = 3108312 (2.9643173217773438MB)
   7.001809512867647% used
To Space: #另一個Survivor區的內存分布
   capacity = 3342336 (3.1875MB)
   used = 0 (0.0MB)
   free = 3342336 (3.1875MB)
   0.0% used
tenured generation:   #當前的Old區內存分布  
   capacity = 67108864 (64.0MB)
   used = 67108816 (63.99995422363281MB)
   free = 48 (4.57763671875E-5MB)
   99.99992847442627% used
Perm Generation:     #當前的 “持久代” 內存分布
   capacity = 14417920 (13.75MB)
   used = 14339216 (13.674942016601562MB)
   free = 78704 (0.0750579833984375MB)
   99.45412375710227% used
復制代碼

 

新生代的內存回收就是采用空間換時間的方式;
如果from區使用率一直是100% 說明程序創建大量的短生命周期的實例,使用jstat統計一下jvm在內存回收中發生的頻率耗時以及是否有full gc,使用這個數據來評估一內存配置參數、gc參數是否合理;

多次ygc后如果s區沒變化,這種情況不出意外就是擔保了,可以jstat持續觀察下;

 

復制代碼
[root@localhost ~]# jmap -heap 4951
Attaching to process ID 4951, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 20.45-b01

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

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 734003200 (700.0MB)
   NewSize          = 314572800 (300.0MB)
   MaxNewSize       = 314572800 (300.0MB)
   OldSize          = 4194304 (4.0MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 104857600 (100.0MB)
   MaxPermSize      = 104857600 (100.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 235929600 (225.0MB)
   used     = 169898184 (162.02753448486328MB)
   free     = 66031416 (62.97246551513672MB)
   72.01223754882812% used
From Space:
   capacity = 39321600 (37.5MB)
   used     = 0 (0.0MB)
   free     = 39321600 (37.5MB)
   0.0% used
To Space:
   capacity = 39321600 (37.5MB)
   used     = 0 (0.0MB)
   free     = 39321600 (37.5MB)
   0.0% used
PS Old Generation
   capacity = 419430400 (400.0MB)
   used     = 0 (0.0MB)
   free     = 419430400 (400.0MB)
   0.0% used
PS Perm Generation
   capacity = 104857600 (100.0MB)
   used     = 14140688 (13.485610961914062MB)
   free     = 90716912 (86.51438903808594MB)
   13.485610961914062% used
復制代碼

 

根據打印的結果:默認存活區與eden比率=2:8

1)查看eden區:225M

2)兩個存活區大小:都為37.5M

3)年輕代大小:300M

4)老年代大小:400M

5)持久代大小:100M

6)最大堆內存大小:年輕代大小+老年代大小=700M

7)java應用程序占用內存大小:最大堆內存大小+持久代大小=700M+100M=800M

 

對應java參數(在tomcat的啟動文件里)配置如下:

 JAVA_OPTS="-Xms700m -Xmx700m -Xmn300m -Xss1024K -XX:PermSize=100m -XX:MaxPermSize=100 -XX:+UseParallelGC -XX:ParallelGCThreads=1 -XX:+PrintGCTimeStamps
 -XX:+PrintGCDetails -Xloggc:/opt/logs/gc.log -verbose:gc -XX:+DisableExplicitGC"

 

注意參數配置時,上述參數為一行。

各參數意義:

  • Xms
  • Xmx
  • Xmn
  • Xss
  • -XX:PermSize
  • -XX:MaxPermSize
  • NewRatio = 2   表示新生代(e+2s):老年代(不含永久區)=1:2,指新生代占整個堆的1/3
  • SurvivorRatio = 8  表示2個S:eden=2:8,一個S占年輕代的1/10

 

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

說明:instances(實例數)、bytes(大小)、classs name(類名)。它基本是按照使用使用大小逆序排列的。 

 

復制代碼
#instance 是對象的實例個數 
#bytes 是總占用的字節數 
class name 對應的就是 Class 文件里的 class 的標識 
B 代表 byte
C 代表 char
D 代表 double
F 代表 float
I 代表 int
J 代表 long
Z 代表 boolean
前邊有 [ 代表數組, [I 就相當於 int[]
對象用 [L+ 類名表示
復制代碼

 

 從打印結果可看出,類名中存在[C、[B等內容,

  • 只知道它占用了那么大的內存,但不知道由什么對象創建的。下一步需要將其他dump出來,使用內存分析工具進一步明確它是由誰引用的、由什么對象。

 

3、 jmap -histo:live pid>a.log

可以觀察heap中所有對象的情況(heap中所有生存的對象的情況)。包括對象數量和所占空間大小。 可以將其保存到文本中去,在一段時間后,使用文本對比工具,可以對比出GC回收了哪些對象。

jmap -histo:live 這個命令執行,JVM會先觸發gc,然后再統計信息。

 

 

4、dump 將內存使用的詳細情況輸出到文件

jmap -dump:live,format=b,file=a.log pid

說明:內存信息dump到a.log文件中。

 這個命令執行,JVM會將整個heap的信息dump寫入到一個文件,heap如果比較大的話,就會導致這個過程比較耗時,並且執行的過程中為了保證dump的信息是可靠的,所以會暫停應用。

 

 

該命令通常用來分析內存泄漏OOM,通常做法是:

1)首先配置JVM啟動參數,讓JVM在遇到OutOfMemoryError時自動生成Dump文件

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path

 

2)然后使用命令

jmap  -dump:format=b,file=/path/heap.bin 進程ID  

如果只dump heap中的存活對象,則加上選項-live。 

 

 3)然后使用MAT分析工具,如jhat命令,eclipse的mat插件。

最后在eclipse中安裝MAT插件(http://www.eclipse.org/mat/),然后在eclipse中,file---->open,打開這個文件heap.bin,利用現成的OOM工具進行分析。
具體操作方法:
首先輸入網址http://www.eclipse.org/mat/previousReleases.php,然后查看你自己的Eclipse版本,我的是Indigo的,所以點擊鏈接“Previous Releases”,選擇Indigo版本的URLhttp://download.eclipse.org/mat/1.1.0/update-site/ 

 用jhat命令可以參看 jhat -port 5000 heapDump 在瀏覽器中訪問:http://localhost:5000/ 查看詳細信息

四、性能問題查找

轉自:http://www.cnblogs.com/gaojk/articles/3886503.html

1、發現問題
1)、使用uptime命令查看CPU的Load情況,Load越高說明問題越嚴重;
2)、使用jstat查看FGC發生的頻率及FGC所花費的時間,FGC發生的頻率越快、花費的時間越高,問題越嚴重;
 
2、導出數據:在應用快要發生FGC的時候把堆導出來
1)、查看快要發生FGC使用命令:
jmap -heap <pid>
會看到如下圖結果:
    以上截圖包括了新生代、老年代及持久代的當前使用情況,如果不停的重復上面的命令,會看到這些數字的變化,變化越大說明系統存在問題的可能性越大,特別是被紅色圈起來的老年代的變化情況。現在看到的這個值為使用率為99%或才快接近的時候,就立即可以執行導出堆棧的操作了。
    注:這是因為我這里沒有在jvm參數中使用"-server"參數,也沒有指定FGC的閥值,在線上的應用中通過會指定CMSInitiatingOccupancyFraction這個參數來指定當老年代使用了百分之多少的時候,通過CMS進行FGC,當然這個參數需要和這些參數一起使用“-XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly”,CMSInitiatingOccupancyFraction的默認值是68,現在中文站線上的應用都是70,也就是說當老年代使用率真達到或者超過70%時,就會進行FGC。
    2)、將數據導出:
jmap -dump:format=b,file=heap.bin <pid>
這個時候會在當前目錄以生成一個heap.bin這個二進制文件。
 
3、通過命令查看大對象
也是使用jmap的命令,只不過參數使用-histo
使用:jmap -histo <pid>|less
可得到如下包含對象序號、某個對象示例數、當前對象所占內存的大小、當前對象的全限定名,如下圖:
查看對象數最多的對象,並按降序排序輸出:
執行:jmap -histo <pid>|grep alibaba|sort -k 2 -g -r|less
結果如圖:
查看占用內存最多的最象,並按降序排序輸出:
執行:jmap -histo <pid>|grep alibaba|sort -k 3 -g -r|less
結果如圖:

4、數據分析
這個時候將dump出的文件在ECLIPSE中打開,使用MAT進行分析(ECLIPSE需要先安裝MAT插件),會展示如下截圖:
可以從這個圖看出這個類java.lang.ref.Finalizer占用500多M,表示這其中很多不能夠被回對象的對象,此時點開hisgogram視圖,並通過Retained Heap進行排序,如下截圖:
從圖中可以看出,被線線框圈起來的三個對象占用量非常大,那說明這幾個大的對象並沒有被釋放,那現在就可以有針對性的從代碼中去找這幾個對象為什么沒有被釋放了。
再切換到dominator_tree視圖:
這里可以看到velocity渲染也存在着問題,以及數據庫的請求也比較多。
 
5、優化
優化的思路就是上面所列出來的問題,查看實現代碼中所存在問題,具體問題具體分析。
 

總結

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

7.在用cms gc的情況下,執行jmap -heap有些時候會導致進程變T,因此強烈建議別執行這個命令,如果想獲取內存目前每個區域的使用狀況,可通過jstat -gc或jstat -gccapacity來拿到。

 

 

Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can’t attach to the process

在ubuntu中第一次使用jmap會報錯:Error attaching to process: sun.jvm.hotspot.debugger.DebuggerException: Can't attach to the process,這是oracla文檔中提到的一個bug:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7050524,解決方式如下:

  1. echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope 該方法在下次重啟前有效。

  2. 永久有效方法 sudo vi /etc/sysctl.d/10-ptrace.conf 編輯下面這行: kernel.yama.ptrace_scope = 1 修改為: kernel.yama.ptrace_scope = 0 重啟系統,使修改生效。

 


windows查看進程號方法:

由於任務管理器默認的情況下是不顯示進程id號的,所以可以通過如下方法加上。

ctrl+alt+del打開任務管 理器,選擇‘進程’選項卡,點‘查看’->''選擇列''->加上''PID'',就可以了。當然還有其他很好的選項。 

 

參考:

1、http://blog.csdn.net/fenglibing/article/details/6411953

2、http://www.cnblogs.com/o-andy-o/category/488695.html

3、性能問題的查找

4、Java命令學習系列(三)——Jmap


免責聲明!

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



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