JVM內存:JVM內存能夠分爲堆區和非堆區。java
堆區:經過new的方式建立的對象(類實例)所佔用的內存空間,在 Java 中,堆被劃分紅兩個不一樣的區域:新生代 ( Young )、老年代 (Old )。新生代 ( Young ) 又被劃分爲三個區域:Eden、From Survivor、To Survivor。apache
非堆區:非堆區即爲代碼、常量、外部訪問(如文件訪問流所佔資源)等。windows
新生代 ( Young ):新生代用來存放JVM剛分配的Java對象。tomcat
老年代 (Old):新生代中通過垃圾回收沒有回收掉的對象將被Copy到老年代。jvm
Eden:用來存放JVM剛分配的對象。工具
Survivror:兩個Survivor空間同樣大,當Eden中的對象通過垃圾回收沒有被回收掉時,會在兩個Survivor之間來回Copy,當知足某個條件,好比Copy次數,就會被Copy到Old。顯然,Survivor只是增長了對象在年輕代中的逗留時間,增長了被垃圾回收的可能性。性能
垃圾回收:當新生代內存滿時,會引起一次普通GC,該GC僅回收新生代。須要強調的時,年輕代盡是指Eden代滿,Survivor滿不會引起GC。測試
當老年代滿時會引起Full GC,Full GC將會同時回收新生代、老年代。優化
##2 JVM參數spa
-Xms 初始堆大小 一般會將 -Xms 與 -Xmx兩個參數的配置相同的值,其目的是爲了可以在java垃圾回收機制清理完堆區後不須要從新分隔計算堆區的大小而浪費資源。
-Xmx 最大堆大小
-XX:newSize 新生代初始內存的大小
-XX:MaxnewSize 新生代最大內存的大小
-Xmn 新生代大小 對 -XX:newSize、-XX:MaxnewSize兩個參數的同時配置, 即-XX:newSize = -XX:MaxnewSize = -Xmn
-Xss 每一個線程的堆棧大小 JDK1.5+ 每一個線程堆棧大小爲 1M
-XX:NewRatio 新生代與老年代的比例 如 –XX:NewRatio=2,則新生代佔整個堆空間的1/3,老年代佔2/3 -XX:SurvivorRatio 新生代中 Eden 與 Survivor 的比值 默認值爲 8。即 Eden 佔新生代空間的 8/10,另外兩個 Survivor 各佔 1/10
-XX:PermSize 非堆區初始內存大小 慎重的考慮軟件所須要的非堆區內存大小,由於此處內存是不會被java垃圾回收機制進行處理的;而且最大堆內存與最大非堆內存的和絕對不可以超出操做系統的可用內存
-XX:MaxPermSize 非堆區最大內存大小
##3 Tomcat配置 經屢次測試,在此機器上使用如下JVM參數可達到較優的性能,在${ CATALINA_HOME}\bin\ catalina.bat中添加如下配置,並使用startup.bat啓動tomcat
set JAVA_OPTS=%JAVA_OPTS% -Xms1024m -Xmx1024m -Xmn384m -Xss256k -XX:PermSize=128m -XX:MaxPermSize=128m
備註:jvisualvm監控不到經過windows服務啓動的tomcat,jvisualvm是經過pid來查找本地Java應用的,pid文件存放在臨時目錄下的hsperfdata_<username>文件夾下;而使用windows服務啓動的Tomcat的臨時目錄使用的是系統臨時目錄。故這次內存調優使用startup.bat的方式啓動Tomcat。
##4 內存監控 ###4.1 jvisualvm 須要遠程監控時須在遠程機器的啓動參數中添加如下配置:
-Dcom.sun.management.jmxremote.port=8999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
在${JAVA_HOME}\bin下打開jvisualvm.exe,添加遠程主機並添加JMX鏈接
###4.2 jconsole
在${JAVA_HOME}\bin下打開jconsole.exe,並新建鏈接
###4.3 jps 命令格式:jps [options ] [ hostid ]
[options]選項 :
-q:僅輸出VM標識符,不包括classname,jar name,arguments in main method
-m:輸出main method的參數
-l:輸出徹底的包名,應用主類名,jar的徹底路徑名
-v:輸出jvm參數
-V:輸出經過flag文件傳遞到JVM中的參數(.hotspotrc文件或-XX:Flags=所指定的文件
-Joption:傳遞參數到vm,例如:-J-Xms512m
[hostid]:
[protocol:][[//]hostname][:port][/servername]
命令的輸出格式 :
lvmid [ [ classname| JARfilename | "Unknown"] [ arg* ] [ jvmarg* ] ]
使用jps命令查看正在運行的jvm進程,找到tomcat進程的pid
C:\Users\dev01>jps -l 4468 org.apache.catalina.startup.Bootstrap
###4.4 jstat 語法結構:
Usage: jstat -help|-options
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
參數解釋:
Options — 選項,咱們通常使用 -gcutil 查看gc狀況
vmid — VM的進程號,即當前運行的java進程號
interval– 間隔時間,單位爲秒或者毫秒
count — 打印次數,若是缺省則打印無數次
在遠程主機上使用jstat命令可查看JVM內存狀況
C:\Users\dev01> jstat -gc 4468 S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT 52416.0 52416.0 0.0 24837.2 419456.0 305254.0 524288.0 75588.2 131072.0 58023.9 365 4.633 0 0.000 4.633
S0 — Heap上的 Survivor space 0 區已使用空間的百分比
S1 — Heap上的 Survivor space 1 區已使用空間的百分比
E — Heap上的 Eden space 區已使用空間的百分比
O — Heap上的 Old space 區已使用空間的百分比
P — Perm space 區已使用空間的百分比
S0C、S1C、S0U、S1U:Survivor 0/1區容量(Capacity)和使用量(Used)
EC、EU:Eden區容量和使用量
OC、OU:年老代容量和使用量
PC、PU:永久代容量和使用量
YGC、YGT:年輕代GC次數和GC耗時
FGC、FGCT:Full GC次數和Full GC耗時
GCT:GC總耗時
C:\Users\dev01>jstat -gcutil 4468 1000 10 S0 S1 E O P YGC YGCT FGC FGCT GCT 1.70 0.00 100.00 100.00 51.56 19155 325.924 55782 86487.063 86812.987 100.00 0.00 100.00 100.00 51.56 19155 325.924 55783 86487.063 86812.987 100.00 0.00 100.00 100.00 51.56 19155 325.924 55783 86487.063 86812.987 100.00 0.00 100.00 100.00 51.57 19155 325.924 55784 86488.853 86814.777 100.00 0.00 100.00 100.00 51.57 19155 325.924 55784 86488.853 86814.777 100.00 0.00 100.00 100.00 51.56 19155 325.924 55785 86490.632 86816.556 100.00 0.00 100.00 100.00 51.56 19155 325.924 55785 86490.632 86816.556 100.00 0.00 100.00 100.00 51.56 19155 325.924 55786 86492.414 86818.337 100.00 0.00 100.00 100.00 51.56 19155 325.924 55786 86492.414 86818.337 0.00 0.00 100.00 100.00 51.56 19155 325.924 55787 86494.207 86820.131
YGCT/YGC=0.017秒(17毫秒),新生代空間上的GC平均花費17毫秒。
FGC/FGCT=1.55秒,Full GC平均花費1.55秒,平均花費時間不算長,但Full GC花費的總時間爲86494.207 (24小時),而服務只運行了70小時,這個結果顯然不能讓人接受。
##5 分析dump文件 在JVM啓動參數中添加-XX:+HeapDumpOnOutOfMemoryError,在發生OOME錯誤時,虛擬機會Dump出當前的堆轉存快照,本次調優中使用手動Dump,監控JVM堆使用狀況,在達到最大值以前手動Dump出dump文件。手動Dump生成的dump文件默認保存置%USERPROFILE%\AppData\Local\Temp\visualvm.dat\下。
分析dump文件的工具備不少種,在此咱們使用MAT(Memory Analyze Tool)進行分析工做。使用MAT工具打開dump文件,能夠在Overview中清晰的看到內存使用狀況。
在生成的default_report中,能夠看到MAT已經幫咱們找出了問題:0x1c491278佔用了830M
點擊Details連接,在Dominator Tree中能夠看到ResultMap佔用了大量內存
查看工程代碼發現ResultMap中的數據並無被清理掉,添加清理ResultMap的代碼,從新運行並監控服務。
C:\Users\dev01>jstat -gcutil 6804 1000 10 S0 S1 E O P YGC YGCT FGC FGCT GCT 0.00 53.29 64.59 93.17 54.58 35721 747.320 6 3.417 750.736 0.00 53.29 80.07 93.17 54.58 35721 747.320 6 3.417 750.736 0.00 53.29 95.00 93.17 54.58 35721 747.320 6 3.417 750.736 54.38 0.00 8.48 93.18 54.58 35722 747.341 6 3.417 750.758 54.38 0.00 23.79 93.18 54.58 35722 747.341 6 3.417 750.758 54.38 0.00 37.50 93.18 54.58 35722 747.341 6 3.417 750.758 54.38 0.00 53.72 93.18 54.58 35722 747.341 6 3.417 750.758 54.38 0.00 67.74 93.18 54.58 35722 747.341 6 3.417 750.758 54.38 0.00 83.52 93.18 54.58 35722 747.341 6 3.417 750.758 54.38 0.00 98.45 93.18 54.58 35722 747.341 6 3.417 750.758
YGCT/YGC=0.021秒(21毫秒),新生代空間上的GC平均花費21毫秒。
FGC/FGCT=0.57秒,服務運行了72小時,Full GC了5次,平均花費0.57秒。
##6 總結 大多數Java應用不須要進行GC優化,而且多數出現GC問題的Java應用是代碼問題致使的,並不是參數錯誤,GC優化是最後萬不得已的手段;在配置JVM參數時須要進行屢次測試,經過不斷的試驗和試錯,分析並找到最合適的參數。