還不會JVM調優嗎?照着做就行

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你好,我是看山。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這幾天壓測預生產環境,發現 TPS 各種不穩。因爲是重構的系統,據說原來的系統在高併發的時候一點問題沒有,結果重構的系統被幾十個併發壓一下就各種不穩定。雖然測試的同事沒有說啥,但自己感覺被啪啪的打臉。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"於是各種排查,最先想到的就是 JVM 參數,於是優化一番,希望能夠出一個好的結果。儘管後來證明不穩定的原因是安裝 LoadRunner 的壓測服務器不穩定,不關我的系統的事,不過也是記錄一下,一是做個備份,二是可以給別人做個參考。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"寫在前面的話","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲 Hotspot JDK 提供的參數默認值,在小版本之間不斷變化,參數之間也會互相影響。而且,服務器配置不同,都可能影響最後的效果。所以千萬不要迷信網上的某篇文章(包括這篇)裏面的參數配置,一切的配置都需要自己親身測試一番才能用。針對於 JVM 參數默認值不斷變化,可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-XX:+PrintFlagsFinal","attrs":{}}],"attrs":{}},{"type":"text","text":"打印當前環境 JVM 參數默認值,比如:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java -XX:PrintFlagsFinal -version","attrs":{}}],"attrs":{}},{"type":"text","text":",也可以用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java [生產環境參數] -XX:+PrintFlagsFinal –version | grep [待查證的參數]","attrs":{}}],"attrs":{}},{"type":"text","text":"查看具體的參數數據。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.howardliu.cn/files/java/global-flags.txt","title":"","type":null},"content":[{"type":"text","text":"這裏","attrs":{}}]},{"type":"text","text":" 是一個 8G 服務器的參數,JDK 版本信息如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"java version \"1.8.0_73\"\nJava(TM) SE Runtime Environment (build 1.8.0_73-b02)\nJava HotSpot(TM) 64-Bit Server VM (build 25.73-b02, mixed mode)\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"堆設置","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"堆內存設置應該算是一個 Java 程序猿的基本素養,最少也得修改過 Xms、Xmx、Xmn 這三個參數了。但是一個 2G 堆大小的 JVM,可能總共佔用多少內存的?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"堆內存 + 線程數 * 線程棧 + 永久代 + 二進制代碼 + 堆外內存","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2G + 1000 * 1M + 256M + 48/240M + (~2G) = 5.5G (3.5G)","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"堆內存: 存儲 Java 對象,默認爲物理內存的 1/64","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程棧: 存儲局部變量(原子類型,引用)及其他,默認爲 1M","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"永久代: 存儲類定義及常量池,注意 JDK7/8 的區別","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"二進制代碼:JDK7 與 8,打開多層編譯時的默認值不一樣,從 48 到 240M","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"堆外內存: 被 Netty,堆外緩存等使用,默認最大值約爲堆內存大小","attrs":{}}]}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"也就是說,堆內存設置爲 2G,那一個有 1000 個線程的 JVM 可能需要佔 5.5G,在考慮系統佔用、IO 佔用等等各種情況,一臺 8G 的服務器,也就啓動一個服務了。當然,如果線程數少、併發不高、壓力不大,還是可以啓動多個,而且也可以把堆內存降低。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"-Xms2g 與 -Xmx2g:堆內存大小,第一個是最小堆內存,第二個是最大堆內存,比較合適的數值是 2-4g,再大就得考慮 GC 時間","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"-Xmn1g 或 (-XX:NewSize=1g 和 -XX:MaxNewSize=1g) 或 -XX:NewRatio=1:設置新生代大小,JDK 默認新生代佔堆內存大小的 1/3,也就是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-XX:NewRatio=2","attrs":{}}],"attrs":{}},{"type":"text","text":"。這裏是設置的 1g,也就是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-XX:NewRatio=1","attrs":{}}],"attrs":{}},{"type":"text","text":"。可以根據自己的需要設置。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:MetaspaceSize=128m 和 -XX:MaxMetaspaceSize=512m,JDK8 的永生代幾乎可用完機器的所有內存,爲了保護服務器不會因爲內存佔用過大無法連接,需要設置一個 128M 的初始值,512M 的最大值保護一下。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:SurvivorRatio:新生代中每個存活區的大小,默認爲 8,即 1/10 的新生代, 1/(SurvivorRatio+2),有人喜歡設小點省點給新生代,但要避免太小使得存活區放不下臨時對象而要晉升到老生代,還是從 GC Log 裏看實際情況了。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"-Xss256k:在堆之外,線程佔用棧內存,默認每條線程爲 1M。存放方法調用出參入參的棧、局部變量、標量替換後的局部變量等,有人喜歡設小點節約內存開更多線程。但反正內存夠也就不必要設小,有人喜歡再設大點,特別是有 JSON 解析之類的遞歸調用時不能設太小。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:MaxDirectMemorySize:堆外內存/直接內存的大小,默認爲堆內存減去一個 Survivor 區的大小。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:ReservedCodeCacheSize:JIT 編譯後二進制代碼的存放區,滿了之後就不再編譯。默認開多層編譯 240M,可以在 JMX 裏看看 CodeCache 的大小。","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"GC 設置","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前比較主流的 GC 是 CMS 和 G1,有大神建議以 8G 爲界。(據說 JDK 9 默認的是 G1)。因爲應用設置的內存都比較小,所以選擇 CMS 收集器。下面的參數也是針對 CMS 收集器的,等之後如果有需要,再補充 G1 收集器的參數。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"CMS 設置","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+UseConcMarkSweepGC:啓用 CMS 垃圾收集器","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:CMSInitiatingOccupancyFraction=80 與 -XX:+UseCMSInitiatingOccupancyOnly:兩個參數需要配合使用,否則第一個參數的 75 只是一個參考值,JVM 會重新計算 GC 的時間。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:MaxTenuringThreshold=15:對象在 Survivor 區熬過多少次 Young GC 後晉升到年老代,默認是 15。Young GC 是最大的應用停頓來源,而新生代裏 GC 後存活對象的多少又直接影響停頓的時間,所以如果清楚 Young GC 的執行頻率和應用裏大部分臨時對象的最長生命週期,可以把它設的更短一點,讓其實不是臨時對象的新生代長期對象趕緊晉升到年老代。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:-DisableExplicitGC:允許使用 System.gc() 主動調用 GC。這裏需要說明下,有的 JVM 優化建議是設置-XX:-DisableExplicitGC,關閉手動調用 System.gc()。這是因爲 System.gc() 是觸發 Full GC,頻繁的 Full GC 會嚴重影響性能。但是很多 NIO 框架,比如 Netty,會使用堆外內存,如果沒有 Full GC 的話,堆外內存就無法回收。如果不主動調用 System.gc(),就需要等到 JVM 自己觸發 Full GC,這個時候,就可能引起長時間的停頓(STW),而且機器負載也會升高。所以不能夠完全禁止 System.gc(),又得縮短 Full GC 的時間,那就使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-XX:+ExplicitGCInvokesConcurrent","attrs":{}}],"attrs":{}},{"type":"text","text":"或","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses","attrs":{}}],"attrs":{}},{"type":"text","text":"選項,使用 CMS 收集器來觸發 Full GC。這兩個選項需要配合","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-XX:+UseConcMarkSweepGC","attrs":{}}],"attrs":{}},{"type":"text","text":"使用。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+ExplicitGCInvokesConcurrent:使用 System.gc() 時觸發 CMS GC,而不是 Full GC。默認是不開啓的,只有使用-XX:+UseConcMarkSweepGC 選項的時候才能開啓這個選項。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses:使用 System.gc() 時,永久代也被包括進 CMS 範圍內。只有使用-XX:+UseConcMarkSweepGC 選項的時候才能開啓這個選項。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+ParallelRefProcEnabled:默認爲 false,並行的處理 Reference 對象,如 WeakReference,除非在 GC log 裏出現 Reference 處理時間較長的日誌,否則效果不會很明顯。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":8,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+ScavengeBeforeFullGC:在 Full GC 執行前先執行一次 Young GC。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":9,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+UseGCOverheadLimit: 限制 GC 的運行時間。如果 GC 耗時過長,就拋 OOM。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":10,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+UseParallelGC:設置並行垃圾收集器","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":11,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+UseParallelOldGC:設置老年代使用並行垃圾收集器","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":12,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:-UseSerialGC:關閉串行垃圾收集器","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":13,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+CMSParallelInitialMarkEnabled 和 -XX:+CMSParallelRemarkEnabled:降低標記停頓","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":14,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+CMSScavengeBeforeRemark:默認爲關閉,在 CMS remark 前,先執行一次 minor GC 將新生代清掉,這樣從老生代的對象引用到的新生代對象的個數就少了,停止全世界的 CMS remark 階段就短一些。如果看到 GC 日誌裏 remark 階段的時間超長,可以打開此項看看有沒有效果,否則還是不要打開了,白白多了次 YGC。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":15,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:CMSWaitDuration=10000:設置垃圾收集的最大時間間隔,默認是 2000。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":16,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+CMSClassUnloadingEnabled:在 CMS 中清理永久代中的過期的 Class 而不等到 Full GC,JDK7 默認關閉而 JDK8 打開。看自己情況,比如有沒有運行動態語言腳本如 Groovy 產生大量的臨時類。它會增加 CMS remark 的暫停時間,所以如果新類加載並不頻繁,這個參數還是不開的好。","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"GC 日誌","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GC 過程可以通過 GC 日誌來提供優化依據。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+PrintGCDetails:啓用 gc 日誌打印功能","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"-Xloggc:/path/to/gc.log:指定 gc 日誌位置","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+PrintHeapAtGC:打印 GC 前後的詳細堆棧信息","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+PrintGCDateStamps:打印可讀的日期而不是時間戳","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+PrintGCApplicationStoppedTime:打印所有引起 JVM 停頓時間,如果真的發現了一些不知什麼的停頓,再臨時加上","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-XX:+PrintSafepointStatistics -XX: PrintSafepointStatisticsCount=1","attrs":{}}],"attrs":{}},{"type":"text","text":"找原因。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+PrintGCApplicationConcurrentTime:打印 JVM 在兩次停頓之間正常運行時間,與","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-XX:+PrintGCApplicationStoppedTime","attrs":{}}],"attrs":{}},{"type":"text","text":"一起使用效果更佳。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+PrintTenuringDistribution:查看每次 minor GC 後新的存活週期的閾值","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":8,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+UseGCLogFileRotation 與 -XX:NumberOfGCLogFiles=10 與 -XX:GCLogFileSize=10M:GC 日誌在重啓之後會清空,但是如果一個應用長時間不重啓,那 GC 日誌會增加,所以添加這 3 個參數,是 GC 日誌滾動寫入文件,但是如果重啓,可能名字會出現混亂。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":9,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:PrintFLSStatistics=1:打印每次 GC 前後內存碎片的統計信息","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"其他參數設置","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"-ea:啓用斷言,這個沒有什麼好說的,可以選擇啓用,或者選擇不啓用,沒有什麼大的差異。完全根據自己的系統進行處理。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+UseThreadPriorities:啓用線程優先級,主要是因爲我們可以給予週期性任務更低的優先級,以避免干擾客戶端工作。在我當前的環境中,是默認啓用的。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:ThreadPriorityPolicy=42:允許降低線程優先級","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+HeapDumpOnOutOfMemoryError:發生內存溢出時進行 heap-dump","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:HeapDumpPath=/path/to/java_pid.hprof:這個參數與","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-XX:+HeapDumpOnOutOfMemoryError","attrs":{}}],"attrs":{}},{"type":"text","text":"共同作用,設置 heap-dump 時內容輸出文件。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:ErrorFile=/path/to/hs_err_pid.log:指定致命錯誤日誌位置。一般在 JVM 發生致命錯誤時會輸出類似 hs_err_pid.log 的文件,默認是在工作目錄中(如果沒有權限,會嘗試在/tmp 中創建),不過還是自己指定位置更好一些,便於收集和查找,避免丟失。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:StringTableSize=1000003:指定字符串常量池大小,默認值是 60013。對 Java 稍微有點常識的應該知道,字符串是常量,創建之後就不可修改了,這些常量所在的地方叫做字符串常量池。如果自己系統中有很多字符串的操作,且這些字符串值比較固定,在允許的情況下,可以適當調大一些池子大小。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":8,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+AlwaysPreTouch:在啓動時把所有參數定義的內存全部捋一遍。使用這個參數可能會使啓動變慢,但是在後面內存使用過程中會更快。可以保證內存頁面連續分配,新生代晉升時不會因爲申請內存頁面使 GC 停頓加長。通常只有在內存大於 32G 的時候纔會有感覺。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":9,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:-UseBiasedLocking:禁用偏向鎖(在存在大量鎖對象的創建且高度併發的環境下(即非多線程高併發應用)禁用偏向鎖能夠帶來一定的性能優化)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":10,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:AutoBoxCacheMax=20000:增加數字對象自動裝箱的範圍,JDK 默認-128~127 的 int 和 long,超出範圍就會即時創建對象,所以,增加範圍可以提高性能,但是也是需要測試。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":11,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:-OmitStackTraceInFastThrow:不忽略重複異常的棧,這是 JDK 的優化,大量重複的 JDK 異常不再打印其 StackTrace。但是如果系統是長時間不重啓的系統,在同一個地方跑了 N 多次異常,結果就被 JDK 忽略了,那豈不是查看日誌的時候就看不到具體的 StackTrace,那還怎麼調試,所以還是關了的好。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":12,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+PerfDisableSharedMem:啓用標準內存使用。JVM 控制分爲標準或共享內存,區別在於一個是在 JVM 內存中,一個是生成/tmp/hsperfdata_{userid}/{pid}文件,存儲統計數據,通過 mmap 映射到內存中,別的進程可以通過文件訪問內容。通過這個參數,可以禁止 JVM 寫在文件中寫統計數據,代價就是 jps、jstat 這些命令用不了了,只能通過 jmx 獲取數據。但是在問題排查時,jps、jstat 這些小工具是很好用的,比 jmx 這種很重的東西好用很多,所以需要自己取捨。","attrs":{}},{"type":"link","attrs":{"href":"http://www.evanjones.ca/jvm-mmap-pause.html","title":"","type":null},"content":[{"type":"text","text":"這裏","attrs":{}}]},{"type":"text","text":" 有個 GC 停頓的例子。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":13,"align":null,"origin":null},"content":[{"type":"text","text":"-Djava.net.preferIPv4Stack=true:這個參數是屬於網絡問題的一個參數,可以根據需要設置。在某些開啓 ipv6 的機器中,通過","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"InetAddress.getLocalHost().getHostName()","attrs":{}}],"attrs":{}},{"type":"text","text":"可以獲取完整的機器名,但是在 ipv4 的機器中,可能通過這個方法獲取的機器名不完整,可以通過這個參數來獲取完整機器名。","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"大神給出的例子","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面貼上大神給出的例子,可以參考使用,不過還是建議在自己的環境中有針對的驗證之後再使用,畢竟大神的環境和自己的環境還是不同。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"性能相關","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:-UseBiasedLocking -XX:-UseCounterDecay -XX:AutoBoxCacheMax=20000-XX:+PerfDisableSharedMem(可選) -XX:+AlwaysPreTouch -Djava.security.egd=file:/dev/./urandom","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"內存大小相關 (JDK7)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"-Xms4096m -Xmx4096m -Xmn2048m -XX:MaxDirectMemorySize=4096m-XX:PermSize=128m -XX:MaxPermSize=512m -XX:ReservedCodeCacheSize=240M","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果使用 jdk8,就把-XX:PermSize=128m -XX:MaxPermSize=512m 換成-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m,正如前面所說的,這兩套參數是爲了保證安全的,建議還是加上。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"CMS GC 相關","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75-XX:+UseCMSInitiatingOccupancyOnly -XX:MaxTenuringThreshold=6-XX:+ExplicitGCInvokesConcurrent -XX:+ParallelRefProcEnabled","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"GC 日誌相關","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"-Xloggc:/dev/shm/app-gc.log -XX:+PrintGCApplicationStoppedTime-XX:+PrintGCDateStamps -XX:+PrintGCDetails","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"異常日誌相關","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"-XX:-OmitStackTraceInFastThrow -XX:ErrorFile=","attrs":{}},{"type":"katexinline","attrs":{"mathString":"{LOGDIR}/hs_err_%p.log-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath="}},{"type":"text","text":"{LOGDIR}/","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"JMX 相關","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"-Dcom.sun.management.jmxremote.port=${JMX_PORT} -Dcom.sun.management.jmxremote-Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false-Dcom.sun.management.jmxremote.ssl=false","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"參考","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://calvin1978.blogcn.com/articles/javatuning.html","title":"","type":null},"content":[{"type":"text","text":"Java 性能優化指南 1.8 版,及唯品會的實戰","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://blog.csdn.net/yangzl2008/article/details/43202969","title":"","type":null},"content":[{"type":"text","text":"Java 中的逃逸分析和 TLAB 以及 Java 對象分配","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://www.evanjones.ca/jvm-mmap-pause.html","title":"","type":null},"content":[{"type":"text","text":"The Four Month Bug: JVM statistics cause garbage collection pauses","attrs":{}}]}]}]}]},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你好,我是看山,10 年老猿,開源貢獻者。遊於碼界,戲享人生。關注公衆號:","attrs":{}},{"type":"link","attrs":{"href":"http://static.howardliu.cn/about/kanshanshuo.png","title":"","type":null},"content":[{"type":"text","text":"看山的小屋","attrs":{}}]},{"type":"text","text":",領取學習資料。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章