一、jvm組成部分
名稱 | 內容 | 備註 |
堆 | 數組,對象、分爲新生代、存活區、老年代 |
新生代:存活區 8:2 新生代:老年代 1:2
|
棧 | 局部變量表、對象的引用、操作數棧、方法出口 | |
程序計數器 | 行號指示器、通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成,也就是記錄當前線程的執行點,調度線程執行 | |
方法區 | 類的編譯信息,靜態變量,常量 | |
直接內存 | nio操作
在jdk1.4中新加入了NIO類,他可以調用native函數庫直接分配堆外內存,然後通過java堆中的DirectByteBuffer 對象來指向這塊內存,進行內存分配等工作。 |
|
二、參數配置
名稱 | 參數 | 參數說明 |
堆 |
-Xms:堆最小值(新生代,老年代之後) -Xmx:堆最大值(新生代,老年代之後) -Xmn指定eden區的大小 -XX:SruvirorRation調整倖存區的大小 -XX:PretenureSizeThreshold設置進入老年代的閾值 |
例:-Xms1024 -Xmx1024 通常將-Xms與-Xmx設置爲一樣大小來減小gc的次數,堆內存不足時拋出OutOfMemoryError異常 |
棧 | -Xss |
例:-Xss128k 單線程下無論棧幀太大還是棧容量太小,及應用深度超過虛擬機允許深度都會拋出StackOverflowError每個方法壓入棧的幀大小是不一致的。多線程下當每個線程分配棧幀太大內存不夠擴展時拋出OutOfMemoryError異常線程棧幀越大,可創建的線程越少 |
方法區 |
-XX:PermSize方法區內存最小值 -XX:MaxPermSize方法區內存最大值 |
例: -XX:PermSize=20M -XX:MaxPermSize=20M
各個線程共享的內存區域,主要用來存儲類的元數據、常量、靜態變量、即時編譯後的代碼 異常類型 OutOfMemoryError 原因:常量過多,或代理反射等使用頻繁 |
直接內存 | -XX:MaxDirectMemorySize |
例: -XX:MaxDirectMemorySize=10M 不足時拋出OutOfMemory異常 |
輔助參數 |
-XX:+HeapDumpOnOutOfMemoryError 打印堆內存異常時快照信息 -XX:+HeapDumpPath快照輸出路徑 - |
|
三、垃圾回收算法
名稱 | 優缺點 | 備註 |
標記--清除算法(Mark-Sweep) |
算法執行分爲兩個階段標記與清除,所有的回收算法基本都是基於標記回收算法深度優化 缺點:效率問題,內存空間碎片(不連續的空間) |
|
複製回收算法(包括分代算法) |
比較標記清除算法,避免回收造成的碎片化問題 缺點:以局部的內存空間犧牲爲代價,不過空間的浪費比較小,默認8:1比例1是浪費的,複製也有一定的效率與空間成本 |
|
標記整理算法(Mark-Compact) |
避免了空間的浪費,與內存碎片問題 缺點:整理時複製有效率成本 |
四、垃圾收集器
名稱 | 用法 | 備註 |
Serial(串行GC) | -XX:+UseSerialGC |
串行收集器採用單線程stop-the-world的方式進行收集。當內存不足時,串行GC設置停頓標識,待所有線程都進入安全點(Safepoint)時,應用線程暫停,串行GC開始工作,採用單線程方式回收空間並整理內存。單線程也意味着複雜度更低、佔用內存更少,但同時也意味着不能有效利用多核優勢。事實上,串行收集器特別適合堆內存不高、單核甚至雙核CPU的場合。 |
Serial Old收集器(串行GC) | -XX:+UseSerialGC | |
ParNew(並行GC) | -XX:+UseParNewGC | 新生代收集器,可以認爲是Serial收集器的多線程版本,在多核CPU環境下悠着比Serial更好的表現 |
Parallel Scavenge(並行回收GC) |
-XX:UserParallelGC -XX:parallelGcThreads=4指定線程數 |
並行收集器,追求高吞吐量,高效利用CPU。吞吐量一般爲99%,吞吐量=用戶線程時間/(用戶線程時間+GC線程時間)。適合後臺應用等對交互響應要求不高的廠家,是server級別默認採用的GC方式 |
Parallel Old | -XX:+UseParallelOldGC | Parallel Scavenge收集器的老年代版本,並行收集器,吞吐量有限 |
CMS(併發GC) | -XX:+UseConcMarkSweepGC |
年輕代ParNew與並行收集器類似,而老年代CMS每個收集週期都要經歷:初始標記、併發標記、重新標記、併發清除。其中,初始標記以STW的方式標記所有的根對象;併發標記則同應用線程一起並行,標記出根對象的可達路徑;在進行垃圾回收前,CMS再以一個STW進行重新標記,標記那些由mutator線程(指引起數據變化的線程,即應用線程)修改而可能錯過的可達對象;最後得到的不可達對象將在併發清除階段進行回收。值得注意的是,初始標記和重新標記都已優化爲多線程執行。CMS非常適合堆內存大、CPU核數多的服務器端應用,也是G1出現之前大型應用的首選收集器。 但是CMS並不完美,它有以下缺點: 由於併發進行,CMS在收集與應用線程會同時會增加對堆內存的佔用,也就是說,CMS必須要在老年代堆內存用盡之前完成垃圾回收,否則CMS回收失敗時,將觸發擔保機制,串行老年代收集器將會以STW的方式進行一次GC,從而造成較大停頓時間; |
G1 |
-XX:+UseG1GC
|
五、常用組合
Serial/Serial Old |
年輕代Serial收集器採用單個GC線程實現"複製"算法(包括掃描、複製) 年老代Serial Old收集器採用單個GC線程實現"標記-整理"算法 Serial與Serial Old都會暫停所有用戶線程(即STW) 適用場合: CPU核數<2,物理內存<2G的機器(簡單來講,單CPU,新生代空間較小且對STW時間要求不高的情況下使用) -XX:UseSerialGC:強制使用該GC組合 -XX:PrintGCApplicationStoppedTime:查看STW時間 |
ParNew/Serial Old | 與上邊相比,只是比年輕代多了多線程垃圾回收而已 |
ParNew/CMS | 當下比較高效的組合 |
Parallel Scavenge/Parallel Old |
自動管理的組合 年輕代Parallel Scavenge收集器採用多個GC線程實現"複製"算法(包括掃描、複製)年老代Parallel Old收集器採用多個GC線程實現"標記-整理"算ParallelScavenge與Parallel Old都會暫停所有用戶線程(即STW) 適用於 很多的CPU計算任務而用戶交互任務較少的情況不想自己去過多的關注GC參數,想讓虛擬機自己進行調優工作 |
G1 | 最先進的收集器,但是需要JDK1.7update14以上 |
六、調優方法
1.預留新生代空間
由於fullGC的成本遠比minorGC的成本打,所以給應用分配一個合理的新生代空間,儘量將對象分配到新生代減少fullGC的頻率
2.大對象進入老年代
將大對象直接分配到老年代,保持新生代對象的結構完整性,以提高GC效率,以通過-XX:PretenureSizeThreshold設置進入老年代的閥值
3.穩定與震盪的堆大小
穩定的對大小是對垃圾回收有利的,方法將-Xms和-Xmx的大小一致
4.吞吐量優先
儘可能減少系統執行垃圾回收的總時間,故採用並行垃圾回收器XX:+UseParallelGC或使用-XX:+UseParallelOldGC
5.減低停頓
使用CMS回收器,同時減少fullGC的次數
七、調優工具
jps | 虛擬機進程狀況工具 |
jstat | 虛擬機統計信息監視工具 |
jmap | java內存印象工具 |
jhat | 虛擬機堆轉儲快照分析工具 |
jstack | java線程堆棧跟蹤工具 |
jinfo | java配置信息工具 |
如果各項參數設置合理,系統沒有超時日誌出現,GC頻率不高,CG耗時不高,那麼沒有必要進行GC優化;如果GC時間超過1-3S,或者平方GC,則必須優化。如果滿足下面的指標,則一般不需要進行GC優化:
(1)Minor GC執行時間不到50ms;
(2)MinorGC執行不頻繁,約10秒一次;
(3)Full GC執行時間不到1s
(4)FullGC執行平路不算頻繁,不低於10分鐘1次