最近在學(xué)習(xí) JVM来累,剛好論文的實驗代碼出現(xiàn)運行過慢的情況,所以想通過 Jconsle 查看代碼運行中 JVM 的情況颤介。
Jconsole中對內(nèi)存為如下結(jié)構(gòu):
其中 Metaspace 是 Java 1.8 之后取消方法區(qū)(永久代)后引入的觅闽。
Jconsle 連接后,可以看到內(nèi)存通殃、線程、CPU 等概覽厕宗。
堆內(nèi)存
查看程序運行過程中堆內(nèi)存的使用情況
堆內(nèi)存圖上下波動画舌,說明進(jìn)行了多次 GC ,并且基本沒有內(nèi)存泄漏已慢。從詳細(xì)信息中也可以看到 新生代用的是 PS Scavenge 垃圾收集器曲聂,這是一個并行的多線程的收集器,目標(biāo)時控制吞吐量佑惠。老年代用的是 PS MarkSweep (Serial Old)收集器朋腋,是一個單線程,使用標(biāo)記-整理算法膜楷。
從圖中可以看出旭咽,老年代GC了一次,新生代 GC 了4525次赌厅。新生代 GC 頻繁應(yīng)該與我代碼中在循環(huán)體里面創(chuàng)建矩陣對象有關(guān)穷绵。
以下分別是新生代 Eden 區(qū),Survivor 區(qū)和老年代的內(nèi)存狀態(tài)圖察蹲,也印證了 Minor GC 頻繁的現(xiàn)象请垛。但是 GC 所占用的時間與程序整體時間相比并不多,這個算法運行時間在10分鐘左右洽议。
GC 原理
新生代的GC使用復(fù)制算法宗收。在GC前To survivor區(qū)保持清空,對象保存在Eden和From survivor區(qū)中,GC運行時,Eden中的幸存對象被復(fù)制到 To survivor區(qū)亚兄。針對 From survivor取中的幸存對象混稽,會考慮對象年齡,如果年齡沒達(dá)到閥值(tenuring threshold),對象會被復(fù)制到To survivor區(qū)。如果達(dá)到閥值對象被復(fù)制到老年代匈勋。復(fù)制階段完成后礼旅,Eden 和From survivor區(qū)中只保存死對象,可以被視為全部清空洽洁。如果在復(fù)制過程中To survivor區(qū)被填滿了痘系,剩余的對象會被復(fù)制到老年代中。最后 From和To會對換饿自。
GC 日志
程序運行時加入 -XX:+PrintGCDetails
參數(shù)汰翠。
列出一部分GC日志如下
[Full GC (Ergonomics) [PSYoungGen: 69883K->0K(657920K)] [ParOldGen: 182607K->93569K(191488K)] 252491K->93569K(849408K), [Metaspace: 8524K->8524K(1056768K)], 0.0221501 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
[GC (Allocation Failure) [PSYoungGen: 111648K->32K(131584K)] 212646K->101030K(323072K), 0.0005877 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 131104K->32K(127488K)] 232102K->101030K(318976K), 0.0006413 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 126496K->64K(122880K)] 227494K->101062K(314368K), 0.0006811 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 122432K->32K(119296K)] 223430K->101038K(310784K), 0.0005787 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 118304K->64K(114688K)] 219310K->101070K(306176K), 0.0006089 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 114240K->32K(111616K)] 215246K->101038K(303104K), 0.0007545 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 111616K, used 17726K [0x0000000780900000, 0x000000079dd00000, 0x00000007c0000000)
eden space 110592K, 15% used [0x0000000780900000,0x0000000781a47890,0x0000000787500000)
from space 1024K, 3% used [0x000000079db00000,0x000000079db08000,0x000000079dc00000)
to space 512K, 0% used [0x000000079dc80000,0x000000079dc80000,0x000000079dd00000)
ParOldGen total 191488K, used 101006K [0x0000000701a00000, 0x000000070d500000, 0x0000000780900000)
object space 191488K, 52% used [0x0000000701a00000,0x0000000707ca3818,0x000000070d500000)
Metaspace used 8799K, capacity 9006K, committed 9216K, reserved 1056768K
class space used 948K, capacity 999K, committed 1024K, reserved 1048576K
(1)GC, Full GC說明了這次垃圾收集的停頓類型,而不是用來區(qū)分新生代GC還是老年代GC昭雌。如果有"Full"复唤,則表示這次GC發(fā)生了"Stop-The-World"。
(2)PSYoungGen, ParOldGen烛卧,Metaspace 表示GC發(fā)生的區(qū)域佛纫,這里顯示的區(qū)域名稱與使用的GC收集器密切相關(guān),不同收集器對于不同區(qū)域所顯示的名稱可能不同总放。
(3)接下來"111648K->32K(131584K)"的含義是:GC前該內(nèi)存區(qū)域已使用容量 -> GC后該內(nèi)存區(qū)域已使用容量(該內(nèi)存區(qū)域的總?cè)萘?呈宇。
(4)"212646K->101030K(323072K)"的含義是:GC前Java堆已使用容量 -> GC后Java堆已使用容量(Java堆總?cè)萘浚?。
(5)"0.0005877 secs" 表示該內(nèi)存區(qū)域GC所占用的時間间聊,單位是秒攒盈。
(6)[Times: user=0.01 sys=0.01, real=0.00 secs]:分別表示用戶態(tài)消耗CPU時間, 內(nèi)核態(tài)消耗CPU時間抵拘,操作從開始到結(jié)束所經(jīng)過的墻鐘時間哎榴。
(7)Allocation Failure 代表了 GC 的原因,沒有足夠的空間分配資源僵蛛。
PS尚蝌,CPU時間與墻鐘時間的區(qū)別是:墻鐘時間包括各種非運算的等待耗時,例如等待磁盤I/O充尉、等待線程阻塞等飘言;而CPU時間不包括這些耗時。
當(dāng)系統(tǒng)有多cpu或者多核的話驼侠,多線程操作會疊加這些CPU時間姿鸿,所以有時看到user或sys時間超過real時間是完全正常的。
嘗試減少 GC 次數(shù)的實踐
直接調(diào)整堆大小
-Xms1024m -Xmx1024m
倒源。
發(fā)現(xiàn) Minor GC 在 Eden 300m 左右開始發(fā)生苛预,GC 次數(shù)明顯減少,原因還是內(nèi)存無法分配笋熬。從程序運行結(jié)果來看热某,運行時間并沒有改變,所以可以確定程序執(zhí)行慢,不是 GC 的原因昔馋,是代碼邏輯的原因筹吐。
[GC (Allocation Failure) [PSYoungGen: 348672K->498K(347648K)] 587643K->240102K(1047040K), 0.0015896 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 347634K->64K(348160K)] 587238K->240182K(1047552K), 0.0013313 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 348160K, used 275395K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
eden space 347136K, 79% used [0x00000000eab00000,0x00000000fb7e0ef0,0x00000000ffe00000)
from space 1024K, 6% used [0x00000000ffe00000,0x00000000ffe10000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 699392K, used 240118K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
object space 699392K, 34% used [0x00000000c0000000,0x00000000cea7d958,0x00000000eab00000)
Metaspace used 12864K, capacity 13342K, committed 13696K, reserved 1060864K
class space used 1436K, capacity 1554K, committed 1664K, reserved 1048576K
將循環(huán)中不用的矩陣對象置 null 測試
一般 null 對象會被作為垃圾處理,有利于收集器判定垃圾秘遏,提高 GC 效率丘薛。
從結(jié)果來看基本沒有變化,思考原因邦危,應(yīng)該是矩陣對象本來就比較大榔袋,在 Eden 區(qū)存放不下時發(fā)生 GC,清楚無用對象铡俐。這里將較大對象置 null凰兑,對 GC 次數(shù)影響不大。
總結(jié)
通過 Jconsle 與 GC 日志审丘,簡單實踐了 JVM 相關(guān)參數(shù)應(yīng)用吏够,查看了程序運行時 JVM 內(nèi)存狀態(tài)。派出了論文代碼運行緩慢關(guān)于 GC 方面的原因滩报。