簡書 占小狼,轉(zhuǎn)載請注明原創(chuàng)出處,謝謝!
當服務發(fā)生GC問題時,一般會使用jmap工具進行分析,jmap工具很強大,所以有必要了解它的方方面面。

jmap可以做什么?
1、jmap -histo[:live] <pid>
通過histo選項,打印當前java堆中各個對象的數(shù)量、大小。
如果添加了live,只會打印活躍的對象。

2、jmap -dump:[live,]format=b,file=<filename> <pid>
通過-dump選項,把java堆中的對象dump到本地文件,然后使用MAT進行分析。
如果添加了live,只會dump活躍的對象。
3、jmap -heap <pid>
通過-heap選項,打印java堆的配置情況和使用情況,還有使用的GC算法。
4、jmap -finalizerinfo <pid>
通過-finalizerinfo選項,打印那些正在等待執(zhí)行finalize方法的對象。
5、jmap -permstat <pid>
通過-permstat選項,打印java堆永久代的信息,包括class loader相關(guān)的信息,和interned Strings的信息。
jmap實現(xiàn)原理
通過jmap和jvm之間進行通信,有兩種實現(xiàn)方式:attach 和 SA。
attach
attach方式,簡單來說就是客戶端和服務端之間的通信,客戶端發(fā)送請求,主要邏輯在服務端執(zhí)行,jmap相當于客戶端,JVM相當于服務端。
在JVM中,有一個叫"Attach Listener"的線程,專門負責監(jiān)聽attach的請求,并執(zhí)行對應的操作。
比如現(xiàn)在執(zhí)行"jmap -histo:live 5409",一步一步的實現(xiàn)如下:
1、在Jmap.java類的main函數(shù)中,對參數(shù)進行解析。
2、解析出來參數(shù)中有“-histo:live”,則執(zhí)行histo方法:

attach方法建立了jmap進程和JVM之間的socket連接,建立過程可以查看笨神的文章JVM Attach機制實現(xiàn),后續(xù)基于該連接進行通信。
因為命令行中添加了[:live]選項,這里的live參數(shù)是true。
再看看heapHisto方法

executeCommand方法基于之前的socket連接向JVM發(fā)送了一條"inspectheap"命令,當然了,還有參數(shù)。
虛擬機的"Attach Listener"線程當發(fā)現(xiàn)有新的命令時,就拿出來處理它。
命令和具體的函數(shù)對應關(guān)系如下:

和"inspectheap"對應的是heap_inspection方法,實現(xiàn)如下:

live_objects_only的值取決于請求中是否有"-live",再jmap中,取決于是否有":live",所以不管是不是添加了":live",都會有STW過程,時間長短而已。
在VM_GC_HeapInspection的doit方法中

_full_gc的值就是live_objects_only,如果為true,可能會執(zhí)行一次full gc,清空非活躍的對象,但是可能會因為GC locker,導致跳過本次的GC。
"jmap -dump"實現(xiàn)的原理和"jmap -histo"類似,都是通過attach的方式實現(xiàn),
attach API的實現(xiàn)方式是:
1、客戶端連接到目標JVM,向其發(fā)出一個類似“inspectheap”命令;
2、目標JVM接收到命令,執(zhí)行JVM內(nèi)相關(guān)函數(shù),將收集到的結(jié)果以文本形式返回;
3、客戶端接收到返回的文本并將其顯示出來;
SA
假如執(zhí)行"jmap -heap 5409",就不會使用attach方式實現(xiàn)了。
在參數(shù)解析中,如果參數(shù)是"-heap|-heap:format=b|-permstat|-finalizerinfo"中的一種,或者添加了"-F",比如"jmap -histo -F 5409",則使用SA的方式。

SA方式,和attach方式不同的是,相關(guān)的主要邏輯都在SA中實現(xiàn),從JVM中獲取數(shù)據(jù)即可。
可以大概看下"jmap -heap"的實現(xiàn),對應的實現(xiàn)類是"HeapSummary",內(nèi)部通過BugSpotAgent工具類attach到目標VM,更具體的底層細節(jié),可以參考HotSpot Serviceability Agent 實現(xiàn)淺析
執(zhí)行jmap -heap有些時候可能會導致進程變T,一般是有一個線程在等信號量,這時會block住其它所有線程,可以執(zhí)行kill -CONT <pid>進行恢復,不過還是強烈建議別執(zhí)行這個命令。