一、第五十課 jstat指令
1、指令
jstat -gc [PID] 間隔時間 次數 每隔多少時間列印一次JVM記憶體狀況,總共列印指定次數
二、第五十一課 jmap、jhat指令分析大對象
1、使用jmap
jmap -dump:live,format=b,file=dump.hprof [PID] 将堆記憶體快照放到dump.hprof檔案中,這個是二進制檔案,不能直接打開
2、使用jhat
jhat dump.hprof -port 7000 啟動jhat伺服器,指定端口7000,就可以通過圖形化的方式去分析堆記憶體中的對象分布情況了
三、第五十九課 案例分析,中繼資料區不斷加載類導緻頻繁FULL GC
1、JVM啟動時新加兩個參數,用來追蹤類加載和解除安裝的情況
-XX:TraceClassLoading -XX:TraceClassUnloading
2、設定後發現,輸出的日志中有類似以下的内容
這個類是Java反射時,通過類似getDeclaredMethod方法産生的。在使用反射時,JVM在反射調用15次後,會動态生成一些軟引用對象(ReflectionData),并把類資訊放到Metadata中(具體邏輯需要研究反射源碼)。軟引用對象在GC是否要被回收是基于如下公式判斷的
clock - timestamp <= freespace * SoftRefLRUPolicyMSPerMB
clock - timestamp:一個軟引用對象多久沒被通路過了
freespace:JVM中的空閑記憶體空間
SoftRefLRUPolicyMSPerMB:每一MB的空閑記憶體空間可以允許SoftReference對象存活多久
3、産生頻繁FULL GC的原始,就是将SoftRefLRUPolicyMSPerMB設定為了0。
一般情況下,軟引用的對象隻有在快發送OOM時才會回收,而設定為0後,軟引用的對象會立刻被young gc回收掉一些,然後反射就會繼續建立軟引用對象,并往Metaspace中存放類資訊,是以會導緻Metaspace滿了
四、案例分析 其他導緻頻繁FULL GC的原因
1、記憶體配置設定不合理,導緻對象頻繁進入老年代
2、存在記憶體洩漏問題,老年代裡存放了大量對象無法被回收,比如使用本地緩存
3、永久代裡太多,比如上面那個案例
4、産生了大對象,比如SQL查詢傳回了一個幾十萬資料的對象
5、主動調用了System.gc
備注:系統建立大量線程,頻繁進行FULL GC,這兩種情況會導緻CPU負載過高
五、反射導緻方法區占滿的原因
1、getDeclaredMethod的流程
2、上一步擷取Method對象後,調用Method.invoke的流程
3、方法區占滿原因
調用Class.getDeclaredMethod方法時,每個Class對象内部有個ReflectionData對象,它是個軟引用,如果是null,就會去jvm中取出類資訊并指派。後續調用就可以直接從這個ReflectionData對象中擷取,提高效率。
從ReflectionData中擷取到Method對象後,就開始調用invoke方法了,這裡實際執行invoke方法的有2個實作類,NativeMethodAccessorImpl和GeneratedMethodAccessorXXX,第一次調用時,預設會建立NativeMethodAccessorImpl類。NativeMethodAccessorImpl的invoke方法會有一個判斷,當調用不滿15次時,使用自身,當調用滿15次後,會建立GeneratedMethodAccessorXXX對象,而為了建立這個對象,會先建立DelegatingClassLoader這個類加載器,它會加載所需的類資訊到方法區中,後續再調用這個Method的invoke方法時,就會直接使用GeneratedMethodAccessorXXX,這樣可以提高效率。
但建立GeneratedMethodAccessorXXX會往方法區中寫入類資訊,而當ReflectionData被回收了,那麼下次調用getDeclaredMethod方法,又會重寫上面的步驟,又會往方法區中寫入類資訊。并且NativeMethodAccessorImpl的invoke方法是不加鎖的,是以在高并發下可能有好幾個線程同時建立了GeneratedMethodAccessorXXX對象,雖然最後隻會有一個,但卻會往方法區寫入n次類資訊。進而導緻方法區被占滿
使用Enhancer時,有個方法,設定是否使用緩存,預設都是true。如果設定成false,就可能導緻方法區溢出
enhancer.setUseCache(true);
因為如果使用緩存,Enhancer的create方法會從緩存中取類資訊,否則會調用類加載往方法區寫入類資訊,是以如果不斷建立Enhancer而又無法回收,就會導緻方法區溢出