對Tomcat 8.0進行JVM層面的優化(基於Oracle JDK 8)
目錄
Tomcat容器是執行在JVM上的, 其預設記憶體一般都很小(實體記憶體的1/64), 在實際生產環境中, 若不配置則會極大浪費伺服器資源, 影像系統的效能. 可以通過調整JVM啟動引數, 使得Tomcat擁有更好的效能.
對於JVM的優化主要有兩個方面:記憶體調優 和垃圾收集策略調優 .
1 Tomcat的記憶體調優
1.1 Tomcat的記憶體佔用
Tomcat 的執行記憶體 = Xmx(初始記憶體大小) + Perm Generation(JDK 7中的永久代大小) + Java應用建立的執行緒數 * 1MB.
Java 應用每建立一個執行緒, JVM 程序的記憶體中就會建立一個 Thread 物件, 同時也會在作業系統中建立一個真正的物理執行緒(參考JVM規範), 作業系統會在 Tomcat 的空閒記憶體中建立這個物理執行緒, 而不是在 JVM 的 Xmx 堆記憶體中建立.
在 JDK 1.4中, 預設的棧大小是256KB/執行緒, 但自 JDK 5(為了推廣的方便, JDK 後續版本不再是1.x命名)開始, 預設的棧大小變為1M/執行緒. 舉例: 如果系統剩餘記憶體為400M, 則Java應用最多能建立400個可用執行緒.
結論: 要想建立更多的執行緒, 必須減少分配給JVM的最大記憶體.
1.2 記憶體配置相關引數
-server # JVM的server模式, 在多CPU伺服器中效能可以得到更好地發揮. 預設為client. 配置server模式時要將其作為第一個引數. -Xmx4g # Java Heap的最大可用記憶體, 預設為實體記憶體的1/4(已在JDK 7下驗證, 最大值為30638MB, 總記憶體126GB的23.75%). # 在只執行Tomcar容器的伺服器中, 建議設定為實體記憶體的50%~80%. -Xms4g # Java Heap的初始大小, 預設值為實體記憶體的1/64(已在JDK 7下驗證, 記憶體126GB, 初始值為2GB). -Xss128k # 每個執行緒的Stack大小. 在相同實體記憶體下, 減小這個值能生成更多的執行緒, # 但是作業系統對一個程序內的執行緒數是有限制的, 經驗範圍是3000~5000. -XX:NewRatio=4 # 設定新生代(包括Eden和兩個Survivor區)與老年代的比值(除去持久代), 預設為2, 即新生代與老年代所佔比值為1:2, 新生代佔整個堆疊的1/3. -XX:SurvivorRatio=4 # 設定新生代中Eden區與1個Survivor區的大小比值. 預設為8, 即Eden區佔新生代的80%, 2個Survivor分別佔新生代的10%. # 設定為4, 則兩個Survivor區與一個Eden區的比值為2:4, 一個Survivor區佔整個新生代的1/6. -Xmn1024m # 設定Young Generation所佔用的Java Heap大小為1g. 此值對系統性能影響較大, Sun官方推薦配置為整個堆的3/8(或Xmx的1/4~1/3左右). # 也可使用-XX:NewSize和-XX:MaxNewsize設定新生代的初始值和最大值. # 注意: -Xmn 與 -XX:NewSize、-XX:MaxNewSize 的優先順序: -XX:NewRatio的值會被忽略. # 1. 高優先順序: -XX:NewSize/-XX:MaxNewSize # 2. 中優先順序: -Xmn, 等效於同時設定 -Xmn = -XX:NewSize = -XX:MaxNewSize 三者的值 # 3. 低優先順序: -XX:NewRatio # -Xmn引數是在JDK 1.4 開始支援, 推薦使用之. -XX:NewSize=1g # 設定新生代的大小, 預設為1.25MB(已在JDK 7下驗證). 若顯示設定此值, 將使得NewRatio選項失效. -XX:OldSize=2g # 設定老年代的大小, 預設為5.1875MB(已在JDK 7下驗證). -XX:PermSize=128m # JDK 7及以下版本適用: 設定Java Heap中永久代的初始大小, 預設為20.75MB(已在JDK 7下驗證, client、server模式下均相同). -XX:MaxPermSize=256m # JDK 7及以下版本適用: 設定Java Heap中永久代的最大值. 預設為82.0MB(已在JDK 7下驗證, client、server模式下均相同). -XX:MetaspaceSize=128m # JDK 8及以上版本適用: 初始元空間的大小, 預設為21MB(實際為20.79MB左右, 已驗證). -XX:MaxMetaspaceSize=256m # JDK 8及以上版本適用: 最大元空間的大小. 預設無上限(在126GB實體記憶體的伺服器中, 預設值為(2^44-1)MB, 已驗證).
總結:
- 情形一: 如果不指定Xmx、Xms和NewSize、OldSize, 則系統將基於Xms=1/64總記憶體大小, 對各個Space按照NewRatio=2進行空間的分配, 此時NewSize與OldSize的預設大小將失效.
- 情形二: 如果指定了Xmx、Xms, 未指定NewSize、OldSize, 則系統將優先滿足 NewRatio=2, 且OldSize+NewSize=Xms, 此時NewSize與OldSize的預設大小將失效.
- 結論: 除非顯式指定NewSize與OldSize的值, 否則它們的預設配置一般都不會得到滿足. 顯式指定其中任一個, 另一個就會基於預設值, 並根據應用程式的消耗動態分配空間大小.
1.3 記憶體調優實踐
JVM記憶體方面的調優, 需要在${TOMCAT_HOME}/bin/catalina.sh
檔案中調整, 配置JAVA_OPTS
變數即可. 在啟動Tomcat時, 會執行catalina.sh中的指令碼, 將JAVA_OPTS
作為JVM的啟動引數進行處理.
具體可參考如下配置(示例伺服器配置: 126g的實體記憶體, 2個10核心20執行緒的物理CPU):
# 在檔案最前面(即cygwin=false之前)設定, $JAVA_OPTS 的作用是保留原有的設定, 防止此次修改覆蓋之前的設定 JAVA_OPTS="$JAVA_OPTS -Xmx96g -Xms96g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
-
說明:
- 若不指定Xmx, 即Java Heap的最大值, 程式啟動時, JVM會調整其大小以滿足程式執行的需要. 每次調整時, 都會對堆進行一次完全垃圾收集(即Full GC), 比較影響效能. 因此推薦明確指定Xmx的大小.
-
令
Xmx=Xms
會有更好地效能表現: 能避免JVM在每次垃圾收集後重新動態調節堆空間, 因為頻繁伸縮堆大小將帶來額外的效能消耗. -
JVM有2中模式:client客戶端模式
和server服務端模式
, 平時開發中使用的多是預設的client模式. 可通過命令列引數
-server
強制開啟server模式. 兩者之間的最大區別是, JVM對server模式做了大量優化: 雖然server模式下應用程式啟動較慢, 但在長時間執行下, 程式執行效率會明顯高於client模式, 即client模式不適合需要長時間執行的專案 .
-
元空間的調優:
元空間的大小將受限於機器的記憶體的限制. 限制類的元資料的記憶體大小, 以避免出現虛擬記憶體切換以及本地記憶體分配失敗.
如果可能出現類載入器洩漏, 應當配置此引數指定大小. 32位機器上, 如果地址空間可能會被耗盡, 也應當配置此引數.
元空間的初始大小是21M——這是GC的初始高水位線, 超過這個大小會進行Full GC來進行類的收集.
如果啟動後GC過於頻繁, 請將該值設定得大一些, 以便推遲GC的執行時間.
1.4 驗證配置效果
# 由於配置了環境變數, 在任意目錄下使用jps及jmap命令, 即可呼叫相關工具: # 使用 jps 檢視 Java 的所有程序 [root@localhost ~]# jps 14308 Bootstrap 15620 Jps # 其中 Bootstrap 程序就是 Tomcat, 其程序號為14308. # 通過 jmap 工具檢視其記憶體相關配置 [root@localhost ~]# jmap -heap 14308 # 顯示以下結果 Attaching to process ID 24980, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.151-b12 using thread-local object allocation. Parallel GC with 28 thread(s)# 預設使用併發GC策略 Heap Configuration:# 堆的配置引數 MinHeapFreeRatio= 0 MaxHeapFreeRatio= 100 MaxHeapSize= 103079215104 (98304.0MB) # 最大堆記憶體 NewSize= 34359738368 (32768.0MB)# 新生代大小, 由於預設的NewRatio=2, 所以是1/3的堆大小 MaxNewSize= 34359738368 (32768.0MB) OldSize= 68719476736 (65536.0MB)# 老年代大小, 由於預設的NewRatio=2, 所以是2/3的堆大小 NewRatio= 2 SurvivorRatio= 8 MetaspaceSize= 21807104 (20.796875MB)# 元空間大小 CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize= 17592186044415 MB# 最大元空間大小 G1HeapRegionSize= 0 (0.0MB) Heap Usage:# 堆的使用情況 PS Young Generation Eden Space:# 新生代的伊甸區, 由於預設的SurvivorRatio=8, 所以是6/8的NewSize capacity = 25769803776 (24576.0MB) used= 9742123824 (9290.813278198242MB) free= 16027679952 (15285.186721801758MB) 37.80441600829363% used From Space:# 新生代的From區, 由於預設的SurvivorRatio=8, 所以是1/8的NewSize capacity = 4294967296 (4096.0MB) used= 0 (0.0MB) free= 4294967296 (4096.0MB) 0.0% used To Space:# 新生代的To區, 由於預設的SurvivorRatio=8, 所以是1/8的NewSize capacity = 4294967296 (4096.0MB) used= 0 (0.0MB) free= 4294967296 (4096.0MB) 0.0% used PS Old Generation# 老年代, 由於預設的NewRatio=2, 所以是2/3的堆大小 capacity = 68719476736 (65536.0MB) used= 254298704 (242.5181427001953MB) free= 68465178032 (65293.481857299805MB) 0.37005331832915545% used 23928 interned Strings occupying 3198312 bytes.
2 GC策略調優實踐
Tomcat的GC策略一般都是與其記憶體引數一起配置的, 與應用複雜度相匹配的GC策略、與伺服器效能相適應的記憶體比例, 都將使得系統性能得到大幅提升.
GC策略方面的調優, 也是在${TOMCAT_HOME}/bin/catalina.sh
檔案中調整 -- 同樣是配置JAVA_OPTS
變數即可.
以JDK 8、Tomcat 8為例, 伺服器記憶體為126GB, 作出如下配置:
通過Solr叢集大批量匯入資料的應用中, Parallel GC策略的暫停時間太長, 所以選擇CMS收集器.
# 下述配置各自獨佔一行, 要置於"cygwin=false"之前. # 配置記憶體 JAVA_OPTS="-server -Xmx96g -Xms96g -Xmn35g -XX:OldSize=55g -Xss128k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m" # 配置GC策略 JAVA_OPTS="$JAVA_OPTS -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15 -XX:CMSInitiatingOccupancyFraction=40-XX:CMSFullGCsBeforeCompaction=0 -XX:+ExplicitGCInvokesConcurrent -XX:SoftRefLRUPolicyMSPerMB=0 -XX:MaxGCPauseMillis=100 -Xnoclassgc "
其中Java Heap的初始大小和最大大小均設定為96g, 76%的實體記憶體(以不超過80%為宜).
參考資料:
ofollow,noindex" target="_blank">Tomcat中Java垃圾收集調優
版權宣告
作者: ma_shoufeng(馬瘦風)
出處: 部落格園馬瘦風的部落格
您的支援是對博主的極大鼓勵, 感謝您的閱讀.
本文版權歸博主所有, 歡迎轉載, 但未經博主同意必須保留此段宣告, 且在文章頁面明顯位置給出原文連結, 否則博主保留追究法律責任的權利.