最近看了《深入理解Java虛擬機》感觸很深,就試著對自己的idea進行調優實踐,記錄如下:
(第一次在網上發文章,如有不對指出,希望大神指正)
第一步
查看gc日志
添加配置如下:
# 打印gc
? -XX:+PrintGC
? # 打印gc詳情
? -XX:+PrintGCDetails
? # 打印gc停頓耗時
? -XX:+PrintGCTimeStamps
? # 打印gc的時候添加時間標志
? -XX:+PrintGCDateStamps
? # 輸出位置
? -Xloggc:/Users/Danny/Documents/java/log/idea/gc.log
日志輸出如下:
2019-03-20T18:34:47.664-0800: 0.335: [GC (Allocation Failure) 2019-03-20T18:34:47.664-0800: 0.335: [ParNew: 34944K->4352K(39296K), 0.0235984 secs] 34944K->9107K(126720K), 0.0237457 secs] [Times: user=0.08 sys=0.02, real=0.02 secs]
? 2019-03-20T18:34:47.777-0800: 0.448: [GC (Allocation Failure) 2019-03-20T18:34:47.777-0800: 0.448: [ParNew: 39296K->4352K(39296K), 0.0087480 secs] 44051K->12278K(126720K), 0.0088293 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
? 2019-03-20T18:34:48.059-0800: 0.729: [GC (Allocation Failure) 2019-03-20T18:34:48.059-0800: 0.730: [ParNew: 39296K->4352K(39296K), 0.0101041 secs] 47222K->16725K(126720K), 0.0102179 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
? 2019-03-20T18:34:48.692-0800: 1.363: [GC (Allocation Failure) 2019-03-20T18:34:48.692-0800: 1.363: [ParNew: 39296K->4352K(39296K), 0.0200235 secs] 51669K->26196K(126720K), 0.0201311 secs] [Times: user=0.06 sys=0.01, real=0.02 secs]
? 2019-03-20T18:34:50.712-0800: 3.383: [GC (CMS Initial Mark) [1 CMS-initial-mark: 21844K(87424K)] 47248K(126720K), 0.0024990 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
? 2019-03-20T18:34:50.715-0800: 3.386: [CMS-concurrent-mark-start]
? 2019-03-20T18:34:50.725-0800: 3.396: [CMS-concurrent-mark: 0.010/0.010 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
? 2019-03-20T18:34:50.725-0800: 3.396: [CMS-concurrent-preclean-start]
? 2019-03-20T18:34:50.726-0800: 3.397: [CMS-concurrent-preclean: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
? 2019-03-20T18:34:50.726-0800: 3.397: [CMS-concurrent-abortable-preclean-start]
? 2019-03-20T18:34:51.149-0800: 3.820: [GC (Allocation Failure) 2019-03-20T18:34:51.149-0800: 3.820: [ParNew: 39296K->4352K(39296K), 0.0135339 secs] 61140K->35671K(126720K), 0.0136103 secs] [Times: user=0.06 sys=0.01, real=0.02 secs]
? 2019-03-20T18:34:51.404-0800: 4.075: [GC (Allocation Failure) 2019-03-20T18:34:51.404-0800: 4.075: [ParNew: 39296K->4351K(39296K), 0.0064494 secs] 70615K->39107K(126720K), 0.0065322 secs] [Times: user=0.04 sys=0.00, real=0.01 secs]
? 2019-03-20T18:34:51.411-0800: 4.082: [CMS-concurrent-abortable-preclean: 0.183/0.685 secs] [Times: user=2.43 sys=0.09, real=0.69 secs]
? 2019-03-20T18:34:51.411-0800: 4.082: [GC (CMS Final Remark) [YG occupancy: 4956 K (39296 K)]2019-03-20T18:34:51.411-0800: 4.082: [Rescan (parallel) , 0.0023462 secs]2019-03-20T18:34:51.414-0800: 4.085: [weak refs processing, 0.0000408 secs]2019-03-20T18:34:51.414-0800: 4.085: [class unloading, 0.0033510 secs]2019-03-20T18:34:51.417-0800: 4.088: [scrub symbol table, 0.0027581 secs]2019-03-20T18:34:51.420-0800: 4.091: [scrub string table, 0.0003787 secs][1 CMS-remark: 34755K(87424K)] 39712K(126720K), 0.0094268 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
? 2019-03-20T18:34:51.421-0800: 4.092: [CMS-concurrent-sweep-start]
? 2019-03-20T18:34:51.433-0800: 4.104: [CMS-concurrent-sweep: 0.012/0.012 secs] [Times: user=0.07 sys=0.00, real=0.01 secs]
? 2019-03-20T18:34:51.433-0800: 4.104: [CMS-concurrent-reset-start]
? 2019-03-20T18:34:51.438-0800: 4.109: [CMS-concurrent-reset: 0.006/0.006 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
? 2019-03-20T18:34:51.515-0800: 4.185: [GC (Allocation Failure) 2019-03-20T18:34:51.515-0800: 4.186: [ParNew: 39295K->4029K(39296K), 0.0026156 secs] 69328K->34062K(126720K), 0.0026858 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
關于cms的幾個階段可以參考如下
[https://www.cnblogs.com/zhangxiaoguang/p/5792468.html]
? log里頭99%都是GC (Allocation Failure)造成的young gc。Allocation Failure表示向young generation(eden)給新對象申請空間,但是young generation(eden)剩余的合適空間不夠所需的大小導致的minor gc。
最后看full gc 的擴容
2019-03-20T20:36:00.277-0800: 3632.918: [Full GC (System.gc())
? 2019-03-20T20:36:00.278-0800: 3632.918: [CMS: 184874K->98447K(289064K), 1.3009498 secs] 200372K->98447K(328360K), [Metaspace: 240354K->240354K(1271808K)], 1.3052543 secs] [Times: user=0.90 sys=0.40, real=1.31 secs]
? 2019-03-20T20:43:18.915-0800: 4071.564: [GC (Allocation Failure) 2019-03-20T20:43:18.915-0800: 4071.564: [ParNew: 115712K->1768K(130176K), 0.0092860 secs] 214159K->100216K(419240K), 0.0096646 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
? 2019-03-20T20:50:39.596-0800: 4512.254: [GC (Allocation Failure)
? 2019-03-20T20:50:39.681-0800: 4512.339: [ParNew: 117480K->1873K(130176K), 0.0098727 secs] 215928K->100321K(419240K), 0.0955434 secs] [Times: user=0.05 sys=0.01, real=0.10 secs]
圖如下:
Screen Shot 2019-03-22 at 01.41.32.png
Pasted Graphic 3.png
最終確定配置如下
?
# eden 113m + from 14.125m + to 14.125m(上圖程序自動擴容顯示)
? -Xmn156m
? # new 150m + old 282.289m
? -Xms468m
? -Xmx768m
? # 283.953 Metaspace擴容時觸發FullGC的初始化閾值(默認值FGC的閾值是約20.8m)
? -XX:MetaspaceSize=300m
? # Metaspace最大值
? -XX:MaxMetaspaceSize=300m
調優后效果如下:
Screen Shot 2019-03-21 at 14.03.20.png
? 還是發生了兩次old的gc,按常理內存是夠的所以在此查看日志:
16.577: [GC (Allocation Failure) 16.577: [ParNew Desired survivor size 8159232 bytes, new threshold 1 (max 6) - age 1: 11982624 bytes, 11982624 total
: 142742K->15936K(143808K), 0.0211620 secs] 296485K->180210K(463296K), 0.0212467 secs] [Times: user=0.12 sys=0.01, real=0.02 secs]
16.599: [GC (CMS Initial Mark) [1 CMS-initial-mark: 164274K(319488K)] 180483K(463296K), 0.0188112 secs] [Times: user=0.04 sys=0.00, real=0.02 secs]
16.618: [CMS-concurrent-mark-start]
16.753: [CMS-concurrent-mark: 0.125/0.135 secs] [Times: user=0.67 sys=0.02, real=0.14 secs]
16.753: [CMS-concurrent-preclean-start]
16.759: [CMS-concurrent-preclean: 0.005/0.006 secs] [Times: user=0.04 sys=0.00, real=0.00 secs]
16.759: [CMS-concurrent-abortable-preclean-start]
17.095: [CMS-concurrent-abortable-preclean: 0.324/0.336 secs] [Times: user=1.99 sys=0.08, real=0.34 secs]
17.096: [GC (CMS Final Remark) [YG occupancy: 83210 K (143808 K)]17.096: [Rescan (parallel) , 0.0317955 secs]17.127: [weak refs processing, 0.0002594 secs][1 CMS-remark: 164274K(319488K)] 247484K(463296K), 0.0321534 secs] [Times: user=0.20 sys=0.01, real=0.03 secs]
17.128: [CMS-concurrent-sweep-start]
17.312: [CMS-concurrent-sweep: 0.165/0.184 secs] [Times: user=1.11 sys=0.05, real=0.19 secs]
17.312: [CMS-concurrent-reset-start]
17.338: [CMS-concurrent-reset: 0.023/0.026 secs] [Times: user=0.12 sys=0.01, real=0.02 secs]
? 經過分析發現在old空間還有124M不應該發生gc,通過查閱書籍《深入了解java虛擬機》發現CMS有一個浮動垃圾預留空間默認為68%,但是是一個保守估計,現在的技術已經可以提高到92%,所以跳高參數比值如下:
這里分析的有誤,后查資料發現java8這個值的默認值為92%,Java8之前的為68%,那為什么還會有FGC呢,這是因為默認值只是第一次使用有效,之后虛擬機會動態調整的,所以這里可以調小點,然后加上XX:+UseCMSInitiatingOccupancyOnly 使其不會改變。
# new 150m + old 282.289m/0.85 = 330 (因為處理浮動垃圾需要預留空間CMSInitiatingOccupancyFraction=85)
-Xms480m
-Xmx480m
# eden 113m + from 14.125m + to 14.125m(上圖程序自動擴容顯示) 只能使用90%
-Xmn150m
# 如果沒有 -XX:+UseCMSInitiatingOccupancyOnly 這個參數, 只有第一次會使用85這個值. 后面的情況會自動調整。 注意這里,java8之前默認值為68%,java8的時候變成了92%
-XX:CMSInitiatingOccupancyFraction=85
-XX:+UseCMSInitiatingOccupancyOnly
效果如下圖:
Pasted Graphic 5.png
果然如我所料,FGC一次也沒有了,真的是好興奮。
? 最后在適當加大new的內存,因為觀察gc日志發現每次的回收大小大概20M左右,但是survivor的大小只有15M,所以在此調整所有的空間如下:
# new 150m + old 282.289m/0.85 = 330 (因為處理浮動垃圾需要預留空間CMSInitiatingOccupancyFraction=85)
-Xms500m
-Xmx500m
# eden 113m + from 14.125m + to 14.125m(上圖程序自動擴容顯示) 只能使用90%
-Xmn200m
# 如果沒有 -XX:+UseCMSInitiatingOccupancyOnly 這個參數, 只有第一次會使用85這個值. 后面的情況會自動調整。
-XX:CMSInitiatingOccupancyFraction=85
-XX:+UseCMSInitiatingOccupancyOnly
Pasted Graphic 7.png
? 效果完美,而且使用的內存最小化的方案,哈哈,可能我平時也是比較摳門吧,對于節省方面達到了斤斤計較的地步了。但是做技術應該有精益求精的精神,在對于微服務方面,上千上萬的服務中,每個節省一些空間,最后都是巨大效益。而且空間大不僅浪費空間,而且還會浪費gc的時間。
最終配置如下:
# 設置為服務器模式 使用C2深度編譯,雖然編譯耗時長,但是后期運行深度優化的代碼速度快
# 但是對于一直不關idea的情況,推薦使用
-server
# new 150m + old 282.289m/0.85 = 330 (因為處理浮動垃圾需要預留空間CMSInitiatingOccupancyFraction=85)
-Xms500m
-Xmx500m
# eden 113m + from 14.125m + to 14.125m(上圖程序自動擴容顯示) 只能使用90%
-Xmn200m
-Xss256k
# 283.953 Metaspace擴容時觸發FullGC的初始化閾值(默認值FGC的閾值是約20.8m)
-XX:MetaspaceSize=300m
# Metaspace最大值
-XX:MaxMetaspaceSize=300m
# 關閉System.gc()
-XX:+DisableExplicitGC
-XX:+UseConcMarkSweepGC
# 設置cms在老年代空間被使用多少百分比之后觸發垃圾回收默認68(因為有浮動垃圾)
-XX:CMSInitiatingOccupancyFraction=85
# 如果沒有 -XX:+UseCMSInitiatingOccupancyOnly 這個參數, 只有第一次會使用85這個值. 后面的情況會自動調整。
-XX:+UseCMSInitiatingOccupancyOnly
# 節省64位指針占用的空間,代價是JVM額外開銷 由ergonomics控制
-XX:+UseCompressedOops
# 增大軟引用在JVM中的存活時長(堆空閑空間越大越久)
-XX:SoftRefLRUPolicyMSPerMB=50
-Dfile.encoding=UTF-8
-ea
-Dsun.io.useCanonCaches=false
-Djava.net.preferIPv4Stack=true
-Djdk.http.auth.tunneling.disabledSchemes=""
-XX:+HeapDumpOnOutOfMemoryError
# 禁止字節碼校驗
-Xverify:none
# 省略異常棧信息從而快速拋出,JVM對一些特定的異常類型做了FastThrow優化,如果檢測到在代碼里某個位置連續
# 多次拋出同一類型異常的話,C2會決定用FastThrow方式來拋出異常,而異常Trace即詳細的異常棧信息會被清空。
# 這種異常拋出速度非常快,因為不需要在堆里分配內存,也不需要構造完整的異常棧信息。
-XX:-OmitStackTraceInFastThrow
-XX:ErrorFile=$USER_HOME/java_error_in_idea_%p.log
-XX:HeapDumpPath=$USER_HOME/java_error_in_idea.hprof
# 打印gc
-XX:+PrintGC
# 打印gc詳情
-XX:+PrintGCDetails
# 打印gc停頓耗時
-XX:+PrintGCTimeStamps
# 輸出位置
-Xloggc:/Users/Danny/Documents/java/log/idea/gc.log
# 打印每次的年齡閥值
# -XX:+PrintTenuringDistribution
# 啰嗦的gc信息 上面已經包含了這個參數
# -verbose:gc
# 打印gc的時候添加時間標志
# -XX:+PrintGCDateStamps
# 代碼緩存,用于存放Just In Time編譯后的本地代碼,如果塞滿,JVM將只解釋執行,不再編譯native代碼,server 64位的默認值
# -XX:ReservedCodeCacheSize=240m
# 當頻繁執行某個方法時,生成字節碼來加快反射的執行速度,查看jvm優化陷阱
# 些多態方法調用點的性能反而會顯著下降。所以,為了適應多層編譯模式,JDK 7里這兩個參數的默認值就被改為false了
# -XX:+UseFastAccessorMethods
# 每次永久存儲區滿了后一般GC算法在做擴展分配內存前都會觸發一次FULL GC,除非設置了次選項 感覺沒用
# -Xnoclassgc
# 默認開
# -XX:+UseParNewGC
# 設置后發現沒有用 虛擬機會自動調節
# -XX:MaxTenuringThreshold=6
# 默認開為了減少第二次暫停的時間,開啟并行remark(第三階段的CMS Remark) 會增加gc總時間
# -XX:+CMSParallelRemarkEnabled
# 默認開當FCC 的時候啟用CMS壓縮(因為cms會產生碎片,所以要手動壓縮參數)
# -XX:+UseCMSCompactAtFullCollection