1败富、我們?yōu)槭裁匆獙?duì)jvm做優(yōu)化讥此?
在本地開發(fā)環(huán)境中我們很少會(huì)遇到需要對(duì)jvm進(jìn)行優(yōu)化的需求拢锹,但是到了生產(chǎn)環(huán)境,我們可能將有下面的需求:
- 運(yùn)行的應(yīng)用“卡住了”萄喳,日志不輸出卒稳,程序沒有反應(yīng)
- 服務(wù)器的CPU負(fù)載突然升高
- 在多線程應(yīng)用下,如何分配線程的數(shù)量他巨?
- ……
我們不僅要讓程序能跑起來充坑,而且是可以跑的更快!可以分析解決在生產(chǎn)環(huán)境中所遇到的各種“棘手”的問題染突。
說明:使用的jdk版本為1.8捻爷。
2、jvm的運(yùn)行參數(shù)
在jvm中有很多的參數(shù)可以進(jìn)行設(shè)置份企,這樣可以讓jvm在各種環(huán)境中都能夠高效的運(yùn)行也榄。絕大部分的參數(shù)保持默認(rèn)即可。
2.1司志、三種參數(shù)類型
jvm的參數(shù)類型分為三類手蝎,分別是:
- 標(biāo)準(zhǔn)參數(shù)
- -help
- -version
- -X參數(shù) (非標(biāo)準(zhǔn)參數(shù))
- -Xint
- -Xcomp
- -XX參數(shù)(使用率較高)
- -XX:newSize
- -XX:+UseSerialGC
2.2榕莺、標(biāo)準(zhǔn)參數(shù)
jvm的標(biāo)準(zhǔn)參數(shù),一般都是很穩(wěn)定的棵介,在未來的JVM版本中不會(huì)改變钉鸯,可以使用java -help檢索出所有的標(biāo)準(zhǔn)參數(shù)。
[root@node01 ~]# java -help
用法: java [-options] class [args...]
(執(zhí)行類)
或 java [-options] -jar jarfile [args...]
(執(zhí)行 jar 文件)
其中選項(xiàng)包括:
-d32 使用 32 位數(shù)據(jù)模型 (如果可用)
-d64 使用 64 位數(shù)據(jù)模型 (如果可用)
-server 選擇 "server" VM
默認(rèn) VM 是 server,
因?yàn)槟窃诜?wù)器類計(jì)算機(jī)上運(yùn)行邮辽。
-cp <目錄和 zip/jar 文件的類搜索路徑>
-classpath <目錄和 zip/jar 文件的類搜索路徑>
用 : 分隔的目錄, JAR 檔案
和 ZIP 檔案列表, 用于搜索類文件唠雕。
-D<名稱>=<值>
設(shè)置系統(tǒng)屬性
-verbose:[class|gc|jni]
啟用詳細(xì)輸出
-version 輸出產(chǎn)品版本并退出
-version:<值>
警告: 此功能已過時(shí), 將在
未來發(fā)行版中刪除。
需要指定的版本才能運(yùn)行
-showversion 輸出產(chǎn)品版本并繼續(xù)
-jre-restrict-search | -no-jre-restrict-search
警告: 此功能已過時(shí), 將在
未來發(fā)行版中刪除吨述。
在版本搜索中包括/排除用戶專用 JRE
-? -help 輸出此幫助消息
-X 輸出非標(biāo)準(zhǔn)選項(xiàng)的幫助
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
按指定的粒度啟用斷言
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
禁用具有指定粒度的斷言
-esa | -enablesystemassertions
啟用系統(tǒng)斷言
-dsa | -disablesystemassertions
禁用系統(tǒng)斷言
-agentlib:<libname>[=<選項(xiàng)>]
加載本機(jī)代理庫 <libname>, 例如 -agentlib:hprof
另請(qǐng)參閱 -agentlib:jdwp=help 和 -agentlib:hprof=help
-agentpath:<pathname>[=<選項(xiàng)>]
按完整路徑名加載本機(jī)代理庫
-javaagent:<jarpath>[=<選項(xiàng)>]
加載 Java 編程語言代理, 請(qǐng)參閱 java.lang.instrument
-splash:<imagepath>
使用指定的圖像顯示啟動(dòng)屏幕
2.2.1岩睁、實(shí)戰(zhàn)
實(shí)戰(zhàn)1:查看jvm版本
[root@node01 ~]# java -version
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
# -showversion參數(shù)是表示,先打印版本信息揣云,再執(zhí)行后面的命令捕儒,在調(diào)試時(shí)非常有用,后面會(huì)使用到邓夕。
實(shí)戰(zhàn)2:通過-D設(shè)置系統(tǒng)屬性參數(shù)
public class TestJVM {
public static void main(String[] args) {
String str = System.getProperty("str");
if (str == null) {
System.out.println("hello world");
} else {
System.out.println(str);
}
}
}
進(jìn)行編譯刘莹、測試:
#編譯
[root@node01 test]# javac TestJVM.java
#測試
[root@node01 test]# java TestJVM
hello world
[root@node01 test]# java -Dstr=123 TestJVM
123
2.2.2、-server與-client參數(shù)
可以通過-server或-client設(shè)置jvm的運(yùn)行參數(shù)焚刚。
- 它們的區(qū)別是Server VM的初始堆空間會(huì)大一些点弯,默認(rèn)使用的是并行垃圾回收器,啟動(dòng)慢運(yùn)行快矿咕。
- Client VM相對(duì)來講會(huì)保守一些抢肛,初始堆空間會(huì)小一些,使用串行的垃圾回收器碳柱,它的目標(biāo)是為了讓JVM的啟動(dòng)速度更快捡絮,但運(yùn)行速度會(huì)比Serverm模式慢些。
- JVM在啟動(dòng)的時(shí)候會(huì)根據(jù)硬件和操作系統(tǒng)自動(dòng)選擇使用Server還是Client類型的JVM莲镣。
- 32位操作系統(tǒng)
- 如果是Windows系統(tǒng)锦援,不論硬件配置如何,都默認(rèn)使用Client類型的JVM剥悟。
- 如果是其他操作系統(tǒng)上灵寺,機(jī)器配置有2GB以上的內(nèi)存同時(shí)有2個(gè)以上CPU的話默認(rèn)使用server模式,否則使用client模式区岗。
- 64位操作系統(tǒng)
- 只有server類型略板,不支持client類型。
測試:
[root@node01 test]# java -client -showversion TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
hello world
[root@node01 test]# java -server -showversion TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
hello world
#由于機(jī)器是64位系統(tǒng)慈缔,所以不支持client模式
2.3叮称、-X參數(shù)
jvm的-X參數(shù)是非標(biāo)準(zhǔn)參數(shù),在不同版本的jvm中,參數(shù)可能會(huì)有所不同瓤檐,可以通過java -X查看非標(biāo)準(zhǔn)參數(shù)赂韵。
[root@node01 test]# java -X
-Xmixed 混合模式執(zhí)行 (默認(rèn))
-Xint 僅解釋模式執(zhí)行
-Xbootclasspath:<用 : 分隔的目錄和 zip/jar 文件>
設(shè)置搜索路徑以引導(dǎo)類和資源
-Xbootclasspath/a:<用 : 分隔的目錄和 zip/jar 文件>
附加在引導(dǎo)類路徑末尾
-Xbootclasspath/p:<用 : 分隔的目錄和 zip/jar 文件>
置于引導(dǎo)類路徑之前
-Xdiag 顯示附加診斷消息
-Xnoclassgc 禁用類垃圾收集
-Xincgc 啟用增量垃圾收集
-Xloggc:<file> 將 GC 狀態(tài)記錄在文件中 (帶時(shí)間戳)
-Xbatch 禁用后臺(tái)編譯
-Xms<size> 設(shè)置初始 Java 堆大小
-Xmx<size> 設(shè)置最大 Java 堆大小
-Xss<size> 設(shè)置 Java 線程堆棧大小
-Xprof 輸出 cpu 配置文件數(shù)據(jù)
-Xfuture 啟用最嚴(yán)格的檢查, 預(yù)期將來的默認(rèn)值
-Xrs 減少 Java/VM 對(duì)操作系統(tǒng)信號(hào)的使用 (請(qǐng)參閱文檔)
-Xcheck:jni 對(duì) JNI 函數(shù)執(zhí)行其他檢查
-Xshare:off 不嘗試使用共享類數(shù)據(jù)
-Xshare:auto 在可能的情況下使用共享類數(shù)據(jù) (默認(rèn))
-Xshare:on 要求使用共享類數(shù)據(jù), 否則將失敗。
-XshowSettings 顯示所有設(shè)置并繼續(xù)
-XshowSettings:all
顯示所有設(shè)置并繼續(xù)
-XshowSettings:vm 顯示所有與 vm 相關(guān)的設(shè)置并繼續(xù)
-XshowSettings:properties
顯示所有屬性設(shè)置并繼續(xù)
-XshowSettings:locale
顯示所有與區(qū)域設(shè)置相關(guān)的設(shè)置并繼續(xù)
-X 選項(xiàng)是非標(biāo)準(zhǔn)選項(xiàng), 如有更改, 恕不另行通知挠蛉。
2.3.1祭示、-Xint、-Xcomp谴古、-Xmixed
- 在解釋模式(interpreted mode)下质涛,-Xint標(biāo)記會(huì)強(qiáng)制JVM執(zhí)行所有的字節(jié)碼,當(dāng)然這會(huì)降低運(yùn)行速度掰担,通常低10倍或更多汇陆。
- -Xcomp參數(shù)與它(-Xint)正好相反,JVM在第一次使用時(shí)會(huì)把所有的字節(jié)碼編譯成本地代碼带饱,從而帶來最大程度的優(yōu)化毡代。
- 然而,很多應(yīng)用在使用-Xcomp也會(huì)有一些性能損失勺疼,當(dāng)然這比使用-Xint損失的少教寂,原因是-xcomp沒有讓JVM啟用JIT編譯器的全部功能。JIT編譯器可以對(duì)是否需要編譯做判斷恢口,如果所有代碼都進(jìn)行編譯的話孝宗,對(duì)于一些只執(zhí)行一次的代碼就沒有意義了穷躁。
- -Xmixed是混合模式耕肩,將解釋模式與編譯模式進(jìn)行混合使用,由jvm自己決定问潭,這是jvm默認(rèn)的模式猿诸,也是推薦使用的模式。
示例:強(qiáng)制設(shè)置運(yùn)行模式
#強(qiáng)制設(shè)置為解釋模式
[root@node01 test]# java -showversion -Xint TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, interpreted mode)
#強(qiáng)制設(shè)置為編譯模式
[root@node01 test]# java -showversion -Xcomp TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, compiled mode)
#注意:編譯模式下狡忙,第一次執(zhí)行會(huì)比解釋模式下執(zhí)行慢一些梳虽,注意觀察。
#默認(rèn)的混合模式
[root@node01 test]# java -showversion TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
hello world
2.4灾茁、-XX參數(shù)
-XX參數(shù)也是非標(biāo)準(zhǔn)參數(shù)窜觉,主要用于jvm的調(diào)優(yōu)和debug操作。
-XX參數(shù)的使用有2種方式北专,一種是boolean類型禀挫,一種是非boolean類型:
- boolean類型
- 格式:-XX:[+-]<name> 表示啟用或禁用<name>屬性
- 如:-XX:+DisableExplicitGC 表示禁用手動(dòng)調(diào)用gc操作,也就是說調(diào)用System.gc()無效
- 非boolean類型
- 格式:-XX:<name>=<value> 表示<name>屬性的值為<value>
- 如:-XX:NewRatio=1 表示新生代和老年代的比值
用法:
[root@node01 test]# java -showversion -XX:+DisableExplicitGC TestJVM
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
hello world
2.5拓颓、-Xms與-Xmx參數(shù)
-Xms與-Xmx分別是設(shè)置jvm的堆內(nèi)存的初始大小和最大大小语婴。
-Xmx2048m:等價(jià)于-XX:MaxHeapSize,設(shè)置JVM最大堆內(nèi)存為2048M。
-Xms512m:等價(jià)于-XX:InitialHeapSize砰左,設(shè)置JVM初始堆內(nèi)存為512M匿醒。
適當(dāng)?shù)恼{(diào)整jvm的內(nèi)存大小,可以充分利用服務(wù)器資源缠导,讓程序跑的更快廉羔。
示例:
[root@node01 test]# java -Xms512m -Xmx2048m TestJVM
hello world
2.6、查看jvm的運(yùn)行參數(shù)
有些時(shí)候我們需要查看jvm的運(yùn)行參數(shù)酬核,這個(gè)需求可能會(huì)存在2種情況:
第一蜜另,運(yùn)行java命令時(shí)打印出運(yùn)行參數(shù);
第二嫡意,查看正在運(yùn)行的java進(jìn)程的參數(shù)举瑰;
2.6.1、運(yùn)行java命令時(shí)打印參數(shù)
運(yùn)行java命令時(shí)打印參數(shù)蔬螟,需要添加-XX:+PrintFlagsFinal參數(shù)即可此迅。
[root@node01 test]# java -XX:+PrintFlagsFinal -version
[Global flags]
uintx AdaptiveSizeDecrementScaleFactor = 4 {product}
uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product}
uintx AdaptiveSizePausePolicy = 0 {product}
uintx AdaptiveSizePolicyCollectionCostMargin = 50 {product}
uintx AdaptiveSizePolicyInitializingSteps = 20 {product}
uintx AdaptiveSizePolicyOutputInterval = 0 {product}
uintx AdaptiveSizePolicyWeight = 10 {product}
uintx AdaptiveSizeThroughPutPolicy = 0 {product}
uintx AdaptiveTimeWeight = 25 {product}
bool AdjustConcurrency = false {product}
bool AggressiveOpts = false {product}
intx AliasLevel = 3 {C2 product}
bool AlignVector = true {C2 product}
intx AllocateInstancePrefetchLines = 1 {product}
intx AllocatePrefetchDistance = 256 {product}
intx AllocatePrefetchInstr = 0 {product}
…………………………略…………………………………………
bool UseXmmI2D = false {ARCH product}
bool UseXmmI2F = false {ARCH product}
bool UseXmmLoadAndClearUpper = true {ARCH product}
bool UseXmmRegToRegMoveAll = true {ARCH product}
bool VMThreadHintNoPreempt = false {product}
intx VMThreadPriority = -1 {product}
intx VMThreadStackSize = 1024 {pd product}
intx ValueMapInitialSize = 11 {C1 product}
intx ValueMapMaxLoopSize = 8 {C1 product}
intx ValueSearchLimit = 1000 {C2 product}
bool VerifyMergedCPBytecodes = true {product}
bool VerifySharedSpaces = false {product}
intx WorkAroundNPTLTimedWaitHang = 1 {product}
uintx YoungGenerationSizeIncrement = 20 {product}
uintx YoungGenerationSizeSupplement = 80 {product}
uintx YoungGenerationSizeSupplementDecay = 8 {product}
uintx YoungPLABSize = 4096 {product}
bool ZeroTLAB = false {product}
intx hashCode = 5 {product}
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
由上述的信息可以看出,參數(shù)有boolean類型和數(shù)字類型旧巾,值的操作符是=或:=耸序,分別代表默認(rèn)值和被修改的值。
示例:
java -XX:+PrintFlagsFinal -XX:+VerifySharedSpaces -version
intx ValueMapInitialSize = 11 {C1 product}
intx ValueMapMaxLoopSize = 8 {C1 product}
intx ValueSearchLimit = 1000 {C2 product}
bool VerifyMergedCPBytecodes = true {product}
bool VerifySharedSpaces := true {product}
intx WorkAroundNPTLTimedWaitHang = 1 {product}
uintx YoungGenerationSizeIncrement = 20 {product}
uintx YoungGenerationSizeSupplement = 80 {product}
uintx YoungGenerationSizeSupplementDecay = 8 {product}
uintx YoungPLABSize = 4096 {product}
bool ZeroTLAB = false {product}
intx hashCode = 5 {product}
java version "1.8.0_141"
Java(TM) SE Runtime Environment (build 1.8.0_141-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
#可以看到VerifySharedSpaces這個(gè)參數(shù)已經(jīng)被修改了鲁猩。
2.6.2坎怪、查看正在運(yùn)行的jvm參數(shù)
如果想要查看正在運(yùn)行的jvm就需要借助于jinfo命令查看。
首先廓握,啟動(dòng)一個(gè)tomcat用于測試搅窿,來觀察下運(yùn)行的jvm參數(shù)。
cd /tmp/
rz 上傳
tar -xvf apache-tomcat-7.0.57.tar.gz
cd apache-tomcat-7.0.57
cd bin/
./startup.sh
#http://192.168.40.133:8080/ 進(jìn)行訪問
訪問成功: #查看所有的參數(shù)隙券,用法:jinfo -flags <進(jìn)程id>
#通過jps 或者 jps -l 查看java進(jìn)程
[root@node01 bin]# jps
6346 Jps
6219 Bootstrap
[root@node01 bin]# jps -l
6358 sun.tools.jps.Jps
6219 org.apache.catalina.startup.Bootstrap
[root@node01 bin]#
[root@node01 bin]# jinfo -flags 6219
Attaching to process ID 6219, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.141-b15
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=31457280 -XX:MaxHeapSize=488636416 -XX:MaxNewSize=162529280 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=10485760 -XX:OldSize=20971520 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
Command line: -Djava.util.logging.config.file=/tmp/apache-tomcat-7.0.57/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/tmp/apache-tomcat-7.0.57/endorsed -Dcatalina.base=/tmp/apache-tomcat-7.0.57 -Dcatalina.home=/tmp/apache-tomcat-7.0.57 -Djava.io.tmpdir=/tmp/apache-tomcat-7.0.57/temp
#查看某一參數(shù)的值男应,用法:jinfo -flag <參數(shù)名> <進(jìn)程id>
[root@node01 bin]# jinfo -flag MaxHeapSize 6219
-XX:MaxHeapSize=488636416
3、jvm的內(nèi)存模型
jvm的內(nèi)存模型在1.7和1.8有較大的區(qū)別娱仔,雖然本套課程是以1.8為例進(jìn)行講解沐飘,但是我們也是需要對(duì)1.7的內(nèi)存模型有所了解,所以接下里牲迫,我們將先學(xué)習(xí)1.7再學(xué)習(xí)1.8的內(nèi)存模型耐朴。
3.1、jdk1.7的堆內(nèi)存模型
-
Young 年輕區(qū)(代)
Young區(qū)被劃分為三部分盹憎,Eden區(qū)和兩個(gè)大小嚴(yán)格相同的Survivor區(qū)筛峭,其中,Survivor區(qū)間中脚乡,某一時(shí)刻只有其中一個(gè)是被使用的蜒滩,另外一個(gè)留做垃圾收集時(shí)復(fù)制對(duì)象用滨达,在Eden區(qū)間變滿的時(shí)候, GC就會(huì)將存活的對(duì)象移到空閑的Survivor區(qū)間中俯艰,根據(jù)JVM的策略捡遍,在經(jīng)過幾次垃圾收集后,任然存活于Survivor的對(duì)象將被移動(dòng)到Tenured區(qū)間竹握。
-
Tenured 年老區(qū)
Tenured區(qū)主要保存生命周期長的對(duì)象画株,一般是一些老的對(duì)象,當(dāng)一些對(duì)象在Young復(fù)制轉(zhuǎn)移一定的次數(shù)以后啦辐,對(duì)象就會(huì)被轉(zhuǎn)移到Tenured區(qū)谓传,一般如果系統(tǒng)中用了application級(jí)別的緩存,緩存中的對(duì)象往往會(huì)被轉(zhuǎn)移到這一區(qū)間芹关。
-
Perm 永久區(qū)
Perm代主要保存class,method,filed對(duì)象续挟,這部份的空間一般不會(huì)溢出,除非一次性加載了很多的類侥衬,不過在涉及到熱部署的應(yīng)用服務(wù)器的時(shí)候诗祸,有時(shí)候會(huì)遇到j(luò)ava.lang.OutOfMemoryError : PermGen space 的錯(cuò)誤,造成這個(gè)錯(cuò)誤的很大原因就有可能是每次都重新部署轴总,但是重新部署后直颅,類的class沒有被卸載掉,這樣就造成了大量的class對(duì)象保存在了perm中怀樟,這種情況下功偿,一般重新啟動(dòng)應(yīng)用服務(wù)器可以解決問題。
-
Virtual區(qū):
- 最大內(nèi)存和初始內(nèi)存的差值往堡,就是Virtual區(qū)械荷。
3.2、jdk1.8的堆內(nèi)存模型
由上圖可以看出投蝉,jdk1.8的內(nèi)存模型是由2部分組成养葵,年輕代 + 年老代征堪。
年輕代:Eden + 2*Survivor
年老代:OldGen
在jdk1.8中變化最大的Perm區(qū)瘩缆,用Metaspace(元數(shù)據(jù)空間)進(jìn)行了替換。
需要特別說明的是:Metaspace所占用的內(nèi)存空間不是在虛擬機(jī)內(nèi)部佃蚜,而是在本地內(nèi)存空間中庸娱,這也是與1.7的永久代最大的區(qū)別所在。
3.3谐算、為什么要廢棄1.7中的永久區(qū)?
官網(wǎng)給出了解釋:http://openjdk.java.net/jeps/122
This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.
移除永久代是為融合HotSpot JVM與 JRockit VM而做出的努力,因?yàn)镴Rockit沒有永久代芍锦,不需要配置永久代励饵。
現(xiàn)實(shí)使用中剧包,由于永久代內(nèi)存經(jīng)常不夠用或發(fā)生內(nèi)存泄露,爆出異常java.lang.OutOfMemoryError: PermGen往果。
基于此疆液,將永久區(qū)廢棄,而改用元空間陕贮,改為了使用本地內(nèi)存空間堕油。
3.4、通過jstat命令進(jìn)行查看堆內(nèi)存使用情況
jstat命令可以查看堆內(nèi)存各部分的使用量肮之,以及加載類的數(shù)量掉缺。命令的格式如下:
jstat [-命令選項(xiàng)] [vmid] [間隔時(shí)間/毫秒] [查詢次數(shù)]
3.4.1、查看class加載統(tǒng)計(jì)
[root@node01 ~]# jps
7080 Jps
6219 Bootstrap
[root@node01 ~]# jstat -class 6219
Loaded Bytes Unloaded Bytes Time
3273 7122.3 0 0.0 3.98
說明:
- Loaded:加載class的數(shù)量
- Bytes:所占用空間大小
- Unloaded:未加載數(shù)量
- Bytes:未加載占用空間
- Time:時(shí)間
3.4.2戈擒、查看編譯統(tǒng)計(jì)
[root@node01 ~]# jstat -compiler 6219
Compiled Failed Invalid Time FailedType FailedMethod
2376 1 0 8.04 1 org/apache/tomcat/util/IntrospectionUtils setProperty
說明:
- Compiled:編譯數(shù)量眶明。
- Failed:失敗數(shù)量
- Invalid:不可用數(shù)量
- Time:時(shí)間
- FailedType:失敗類型
- FailedMethod:失敗的方法
3.4.3、垃圾回收統(tǒng)計(jì)
[root@node01 ~]# jstat -gc 6219
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
9216.0 8704.0 0.0 6127.3 62976.0 3560.4 33792.0 20434.9 23808.0 23196.1 2560.0 2361.6 7 1.078 1 0.244 1.323
#也可以指定打印的間隔和次數(shù)筐高,每1秒中打印一次赘来,共打印5次
[root@node01 ~]# jstat -gc 6219 1000 5
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
9216.0 8704.0 0.0 6127.3 62976.0 3917.3 33792.0 20434.9 23808.0 23196.1 2560.0 2361.6 7 1.078 1 0.244 1.323
9216.0 8704.0 0.0 6127.3 62976.0 3917.3 33792.0 20434.9 23808.0 23196.1 2560.0 2361.6 7 1.078 1 0.244 1.323
9216.0 8704.0 0.0 6127.3 62976.0 3917.3 33792.0 20434.9 23808.0 23196.1 2560.0 2361.6 7 1.078 1 0.244 1.323
9216.0 8704.0 0.0 6127.3 62976.0 3917.3 33792.0 20434.9 23808.0 23196.1 2560.0 2361.6 7 1.078 1 0.244 1.323
9216.0 8704.0 0.0 6127.3 62976.0 3917.3 33792.0 20434.9 23808.0 23196.1 2560.0 2361.6 7 1.078 1 0.244 1.323
說明:
- S0C:第一個(gè)Survivor區(qū)的大小(KB)
- S1C:第二個(gè)Survivor區(qū)的大锌痢(KB)
- S0U:第一個(gè)Survivor區(qū)的使用大腥健(KB)
- S1U:第二個(gè)Survivor區(qū)的使用大小(KB)
- EC:Eden區(qū)的大斜ァ(KB)
- EU:Eden區(qū)的使用大谢戏臁(KB)
- OC:Old區(qū)大小(KB)
- OU:Old使用大薪肭贰(KB)
- MC:方法區(qū)大泻选(KB)
- MU:方法區(qū)使用大小(KB)
- CCSC:壓縮類空間大谢牡稹(KB)
- CCSU:壓縮類空間使用大薪钨恕(KB)
- YGC:年輕代垃圾回收次數(shù)
- YGCT:年輕代垃圾回收消耗時(shí)間
- FGC:老年代垃圾回收次數(shù)
- FGCT:老年代垃圾回收消耗時(shí)間
- GCT:垃圾回收消耗總時(shí)間
4、jmap的使用以及內(nèi)存溢出分析
前面通過jstat可以對(duì)jvm堆的內(nèi)存進(jìn)行統(tǒng)計(jì)分析被廓,而jmap可以獲取到更加詳細(xì)的內(nèi)容坏晦,如:內(nèi)存使用情況的匯總、對(duì)內(nèi)存溢出的定位與分析嫁乘。
4.1昆婿、查看內(nèi)存使用情況
[root@node01 ~]# jmap -heap 6219
Attaching to process ID 6219, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.141-b15
using thread-local object allocation.
Parallel GC with 2 thread(s)
Heap Configuration: #堆內(nèi)存配置信息
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 488636416 (466.0MB)
NewSize = 10485760 (10.0MB)
MaxNewSize = 162529280 (155.0MB)
OldSize = 20971520 (20.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage: # 堆內(nèi)存的使用情況
PS Young Generation #年輕代
Eden Space:
capacity = 123731968 (118.0MB)
used = 1384736 (1.320587158203125MB)
free = 122347232 (116.67941284179688MB)
1.1191416594941737% used
From Space:
capacity = 9437184 (9.0MB)
used = 0 (0.0MB)
free = 9437184 (9.0MB)
0.0% used
To Space:
capacity = 9437184 (9.0MB)
used = 0 (0.0MB)
free = 9437184 (9.0MB)
0.0% used
PS Old Generation #年老代
capacity = 28311552 (27.0MB)
used = 13698672 (13.064071655273438MB)
free = 14612880 (13.935928344726562MB)
48.38545057508681% used
13648 interned Strings occupying 1866368 bytes.
4.2、查看內(nèi)存中對(duì)象數(shù)量及大小
#查看所有對(duì)象蜓斧,包括活躍以及非活躍的
jmap -histo <pid> | more
#查看活躍對(duì)象
jmap -histo:live <pid> | more
[root@node01 ~]# jmap -histo:live 6219 | more
num #instances #bytes class name
----------------------------------------------
1: 37437 7914608 [C
2: 34916 837984 java.lang.String
3: 884 654848 [B
4: 17188 550016 java.util.HashMap$Node
5: 3674 424968 java.lang.Class
6: 6322 395512 [Ljava.lang.Object;
7: 3738 328944 java.lang.reflect.Method
8: 1028 208048 [Ljava.util.HashMap$Node;
9: 2247 144264 [I
10: 4305 137760 java.util.concurrent.ConcurrentHashMap$Node
11: 1270 109080 [Ljava.lang.String;
12: 64 84128 [Ljava.util.concurrent.ConcurrentHashMap$Node;
13: 1714 82272 java.util.HashMap
14: 3285 70072 [Ljava.lang.Class;
15: 2888 69312 java.util.ArrayList
16: 3983 63728 java.lang.Object
17: 1271 61008 org.apache.tomcat.util.digester.CallMethodRule
18: 1518 60720 java.util.LinkedHashMap$Entry
19: 1671 53472 com.sun.org.apache.xerces.internal.xni.QName
20: 88 50880 [Ljava.util.WeakHashMap$Entry;
21: 618 49440 java.lang.reflect.Constructor
22: 1545 49440 java.util.Hashtable$Entry
23: 1027 41080 java.util.TreeMap$Entry
24: 846 40608 org.apache.tomcat.util.modeler.AttributeInfo
25: 142 38032 [S
26: 946 37840 java.lang.ref.SoftReference
27: 226 36816 [[C
仓蛆。。挎春。看疙。豆拨。。能庆。辽装。。相味。拾积。。丰涉。拓巧。。一死。肛度。。投慈。承耿。。伪煤。加袋。。抱既。职烧。。防泵。蚀之。。捷泞。
#對(duì)象說明
B byte
C char
D double
F float
I int
J long
Z boolean
[ 數(shù)組足删,如[I表示int[]
[L+類名 其他對(duì)象
4.3、將內(nèi)存使用情況dump到文件中
有些時(shí)候我們需要將jvm當(dāng)前內(nèi)存中的情況dump到文件中锁右,然后對(duì)它進(jìn)行分析失受,jmap也是支持dump到文件中的。
#用法:
jmap -dump:format=b,file=dumpFileName <pid>
#示例
jmap -dump:format=b,file=/tmp/dump.dat 6219
可以看到已經(jīng)在/tmp下生成了dump.dat的文件骡湖。
4.4贱纠、通過jhat對(duì)dump文件進(jìn)行分析
在上一小節(jié)中峻厚,我們將jvm的內(nèi)存dump到文件中响蕴,這個(gè)文件是一個(gè)二進(jìn)制的文件,不方便查看惠桃,這時(shí)我們可以借助于jhat工具進(jìn)行查看浦夷。
#用法:
jhat -port <port> <file>
#示例:
[root@node01 tmp]# jhat -port 9999 /tmp/dump.dat
Reading from /tmp/dump.dat...
Dump file created Mon Sep 10 01:04:21 CST 2018
Snapshot read, resolving...
Resolving 204094 objects...
Chasing references, expect 40 dots........................................
Eliminating duplicate references........................................
Snapshot resolved.
Started HTTP server on port 9999
Server is ready.
打開瀏覽器進(jìn)行訪問:http://192.168.40.133:9999/
在最后面有OQL查詢功能辖试。
4.5、通過MAT工具對(duì)dump文件進(jìn)行分析
4.5.1劈狐、MAT工具介紹
MAT(Memory Analyzer Tool)罐孝,一個(gè)基于Eclipse的內(nèi)存分析工具,是一個(gè)快速肥缔、功能豐富的JAVA heap分析工具莲兢,它可以幫助我們查找內(nèi)存泄漏和減少內(nèi)存消耗。使用內(nèi)存分析工具從眾多的對(duì)象中進(jìn)行分析续膳,快速的計(jì)算出在內(nèi)存中對(duì)象的占用大小改艇,看看是誰阻止了垃圾收集器的回收工作,并可以通過報(bào)表直觀的查看到可能造成這種結(jié)果的對(duì)象坟岔。
官網(wǎng)地址:https://www.eclipse.org/mat/
4.5.2谒兄、下載安裝
下載地址:https://www.eclipse.org/mat/downloads.php
將下載得到的MemoryAnalyzer-1.8.0.20180604-win32.win32.x86_64.zip進(jìn)行解壓:
4.5.3、使用
查看對(duì)象以及它的依賴:
查看可能存在內(nèi)存泄露的分析:
5社付、實(shí)戰(zhàn):內(nèi)存溢出的定位與分析
內(nèi)存溢出在實(shí)際的生產(chǎn)環(huán)境中經(jīng)常會(huì)遇到承疲,比如,不斷的將數(shù)據(jù)寫入到一個(gè)集合中鸥咖,出現(xiàn)了死循環(huán)燕鸽,讀取超大的文件等等,都可能會(huì)造成內(nèi)存溢出啼辣。
如果出現(xiàn)了內(nèi)存溢出绵咱,首先我們需要定位到發(fā)生內(nèi)存溢出的環(huán)節(jié),并且進(jìn)行分析熙兔,是正常還是非正常情況悲伶,如果是正常的需求,就應(yīng)該考慮加大內(nèi)存的設(shè)置住涉,如果是非正常需求麸锉,那么就要對(duì)代碼進(jìn)行修改,修復(fù)這個(gè)bug舆声。
首先花沉,我們得先學(xué)會(huì)如何定位問題,然后再進(jìn)行分析媳握。如何定位問題呢碱屁,我們需要借助于jmap與MAT工具進(jìn)行定位分析。
接下來蛾找,我們模擬內(nèi)存溢出的場景娩脾。
5.1、模擬內(nèi)存溢出
編寫代碼打毛,向List集合中添加100萬個(gè)字符串柿赊,每個(gè)字符串由1000個(gè)UUID組成俩功。如果程序能夠正常執(zhí)行,最后打印ok碰声。
package cn.test.jvm;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class TestJvmOutOfMemory {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
String str = "";
for (int j = 0; j < 1000; j++) {
str += UUID.randomUUID().toString();
}
list.add(str);
}
System.out.println("ok");
}
}
為了演示效果诡蜓,我們將設(shè)置執(zhí)行的參數(shù),這里使用的是Idea編輯器胰挑。
#參數(shù)如下:
-Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
5.2蔓罚、運(yùn)行測試
測試結(jié)果如下:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid5348.hprof ...
Heap dump file created [8137186 bytes in 0.032 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at cn.test.jvm.TestJvmOutOfMemory.main(TestJvmOutOfMemory.java:14)
Process finished with exit code 1
可以看到,當(dāng)發(fā)生內(nèi)存溢出時(shí)瞻颂,會(huì)dump文件到j(luò)ava_pid5348.hprof脚粟。
5.3、導(dǎo)入到MAT工具中進(jìn)行分析
可以看到蘸朋,有91.03%的內(nèi)存由Object[]數(shù)組占有核无,所以比較可疑。
分析:這個(gè)可疑是正確的藕坯,因?yàn)橐呀?jīng)有超過90%的內(nèi)存都被它占有团南,這是非常有可能出現(xiàn)內(nèi)存溢出的。
查看詳情:
可以看到集合中存儲(chǔ)了大量的uuid字符串炼彪。
6吐根、jstack的使用
有些時(shí)候我們需要查看下jvm中的線程執(zhí)行情況,比如辐马,發(fā)現(xiàn)服務(wù)器的CPU的負(fù)載突然增高了拷橘、出現(xiàn)了死鎖、死循環(huán)等喜爷,我們該如何分析呢冗疮?
由于程序是正常運(yùn)行的,沒有任何的輸出檩帐,從日志方面也看不出什么問題术幔,所以就需要看下jvm的內(nèi)部線程的執(zhí)行情況,然后再進(jìn)行分析查找出原因湃密。
這個(gè)時(shí)候诅挑,就需要借助于jstack命令了,jstack的作用是將正在運(yùn)行的jvm的線程情況進(jìn)行快照泛源,并且打印出來:
#用法:jstack <pid>
[root@node01 bin]# jstack 2203
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.141-b15 mixed mode):
"Attach Listener" #24 daemon prio=9 os_prio=0 tid=0x00007fabb4001000 nid=0x906 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"http-bio-8080-exec-5" #23 daemon prio=5 os_prio=0 tid=0x00007fabb057c000 nid=0x8e1 waiting on condition [0x00007fabd05b8000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f8508360> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-exec-4" #22 daemon prio=5 os_prio=0 tid=0x00007fab9c113800 nid=0x8e0 waiting on condition [0x00007fabd06b9000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f8508360> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-exec-3" #21 daemon prio=5 os_prio=0 tid=0x0000000001aeb800 nid=0x8df waiting on condition [0x00007fabd09ba000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f8508360> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-exec-2" #20 daemon prio=5 os_prio=0 tid=0x0000000001aea000 nid=0x8de waiting on condition [0x00007fabd0abb000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f8508360> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-exec-1" #19 daemon prio=5 os_prio=0 tid=0x0000000001ae8800 nid=0x8dd waiting on condition [0x00007fabd0bbc000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f8508360> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
"ajp-bio-8009-AsyncTimeout" #17 daemon prio=5 os_prio=0 tid=0x00007fabe8128000 nid=0x8d0 waiting on condition [0x00007fabd0ece000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at org.apache.tomcat.util.net.JIoEndpoint$AsyncTimeout.run(JIoEndpoint.java:152)
at java.lang.Thread.run(Thread.java:748)
"ajp-bio-8009-Acceptor-0" #16 daemon prio=5 os_prio=0 tid=0x00007fabe82d4000 nid=0x8cf runnable [0x00007fabd0fcf000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFactory.java:60)
at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:220)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-AsyncTimeout" #15 daemon prio=5 os_prio=0 tid=0x00007fabe82d1800 nid=0x8ce waiting on condition [0x00007fabd10d0000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at org.apache.tomcat.util.net.JIoEndpoint$AsyncTimeout.run(JIoEndpoint.java:152)
at java.lang.Thread.run(Thread.java:748)
"http-bio-8080-Acceptor-0" #14 daemon prio=5 os_prio=0 tid=0x00007fabe82d0000 nid=0x8cd runnable [0x00007fabd11d1000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFactory.java:60)
at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:220)
at java.lang.Thread.run(Thread.java:748)
"ContainerBackgroundProcessor[StandardEngine[Catalina]]" #13 daemon prio=5 os_prio=0 tid=0x00007fabe82ce000 nid=0x8cc waiting on condition [0x00007fabd12d2000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1513)
at java.lang.Thread.run(Thread.java:748)
"GC Daemon" #10 daemon prio=2 os_prio=0 tid=0x00007fabe83b4000 nid=0x8b3 in Object.wait() [0x00007fabd1c2f000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e315c2d0> (a sun.misc.GC$LatencyLock)
at sun.misc.GC$Daemon.run(GC.java:117)
- locked <0x00000000e315c2d0> (a sun.misc.GC$LatencyLock)
"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fabe80c3800 nid=0x8a5 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fabe80b6800 nid=0x8a4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fabe80b3800 nid=0x8a3 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fabe80b2000 nid=0x8a2 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fabe807f000 nid=0x8a1 in Object.wait() [0x00007fabd2a67000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e3162918> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x00000000e3162918> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fabe807a800 nid=0x8a0 in Object.wait() [0x00007fabd2b68000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e3162958> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000e3162958> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"main" #1 prio=5 os_prio=0 tid=0x00007fabe8009000 nid=0x89c runnable [0x00007fabed210000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at org.apache.catalina.core.StandardServer.await(StandardServer.java:453)
at org.apache.catalina.startup.Catalina.await(Catalina.java:777)
at org.apache.catalina.startup.Catalina.start(Catalina.java:723)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:321)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:455)
"VM Thread" os_prio=0 tid=0x00007fabe8073000 nid=0x89f runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fabe801e000 nid=0x89d runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fabe8020000 nid=0x89e runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007fabe80d6800 nid=0x8a6 waiting on condition
JNI global references: 43
6.1拔妥、線程的狀態(tài)
在Java中線程的狀態(tài)一共被分成6種:
-
初始態(tài)(NEW)
- 創(chuàng)建一個(gè)Thread對(duì)象,但還未調(diào)用start()啟動(dòng)線程時(shí)达箍,線程處于初始態(tài)没龙。
-
運(yùn)行態(tài)(RUNNABLE),在Java中,運(yùn)行態(tài)包括 就緒態(tài) 和 運(yùn)行態(tài)兜畸。
-
就緒態(tài)
- 該狀態(tài)下的線程已經(jīng)獲得執(zhí)行所需的所有資源努释,只要CPU分配執(zhí)行權(quán)就能運(yùn)行碘梢。
- 所有就緒態(tài)的線程存放在就緒隊(duì)列中咬摇。
-
運(yùn)行態(tài)
- 獲得CPU執(zhí)行權(quán),正在執(zhí)行的線程煞躬。
- 由于一個(gè)CPU同一時(shí)刻只能執(zhí)行一條線程肛鹏,因此每個(gè)CPU每個(gè)時(shí)刻只有一條運(yùn)行態(tài)的線程。
-
-
阻塞態(tài)(BLOCKED)
- 當(dāng)一條正在執(zhí)行的線程請(qǐng)求某一資源失敗時(shí)恩沛,就會(huì)進(jìn)入阻塞態(tài)在扰。
- 而在Java中,阻塞態(tài)專指請(qǐng)求鎖失敗時(shí)進(jìn)入的狀態(tài)雷客。
- 由一個(gè)阻塞隊(duì)列存放所有阻塞態(tài)的線程芒珠。
- 處于阻塞態(tài)的線程會(huì)不斷請(qǐng)求資源,一旦請(qǐng)求成功搅裙,就會(huì)進(jìn)入就緒隊(duì)列皱卓,等待執(zhí)行。
-
等待態(tài)(WAITING)
- 當(dāng)前線程中調(diào)用wait部逮、join娜汁、park函數(shù)時(shí),當(dāng)前線程就會(huì)進(jìn)入等待態(tài)兄朋。
- 也有一個(gè)等待隊(duì)列存放所有等待態(tài)的線程掐禁。
- 線程處于等待態(tài)表示它需要等待其他線程的指示才能繼續(xù)運(yùn)行。
- 進(jìn)入等待態(tài)的線程會(huì)釋放CPU執(zhí)行權(quán)颅和,并釋放資源(如:鎖)
-
超時(shí)等待態(tài)(TIMED_WAITING)
- 當(dāng)運(yùn)行中的線程調(diào)用sleep(time)傅事、wait、join峡扩、parkNanos享完、parkUntil時(shí),就會(huì)進(jìn)入該狀態(tài)有额;
- 它和等待態(tài)一樣般又,并不是因?yàn)檎?qǐng)求不到資源,而是主動(dòng)進(jìn)入巍佑,并且進(jìn)入后需要其他線程喚醒茴迁;
- 進(jìn)入該狀態(tài)后釋放CPU執(zhí)行權(quán) 和 占有的資源。
- 與等待態(tài)的區(qū)別:到了超時(shí)時(shí)間后自動(dòng)進(jìn)入阻塞隊(duì)列萤衰,開始競爭鎖堕义。
-
終止態(tài)(TERMINATED)
- 線程執(zhí)行結(jié)束后的狀態(tài)。
6.2、實(shí)戰(zhàn):死鎖問題
如果在生產(chǎn)環(huán)境發(fā)生了死鎖倦卖,我們將看到的是部署的程序沒有任何反應(yīng)了洒擦,這個(gè)時(shí)候我們可以借助jstack進(jìn)行分析,下面我們實(shí)戰(zhàn)下查找死鎖的原因怕膛。
6.2.1熟嫩、構(gòu)造死鎖
編寫代碼,啟動(dòng)2個(gè)線程褐捻,Thread1拿到了obj1鎖掸茅,準(zhǔn)備去拿obj2鎖時(shí),obj2已經(jīng)被Thread2鎖定柠逞,所以發(fā)送了死鎖昧狮。
public class TestDeadLock {
private static Object obj1 = new Object();
private static Object obj2 = new Object();
public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
}
private static class Thread1 implements Runnable{
@Override
public void run() {
synchronized (obj1){
System.out.println("Thread1 拿到了 obj1 的鎖!");
try {
// 停頓2秒的意義在于板壮,讓Thread2線程拿到obj2的鎖
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2){
System.out.println("Thread1 拿到了 obj2 的鎖逗鸣!");
}
}
}
}
private static class Thread2 implements Runnable{
@Override
public void run() {
synchronized (obj2){
System.out.println("Thread2 拿到了 obj2 的鎖!");
try {
// 停頓2秒的意義在于绰精,讓Thread1線程拿到obj1的鎖
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1){
System.out.println("Thread2 拿到了 obj1 的鎖撒璧!");
}
}
}
}
}
6.2.2、在linux上運(yùn)行
[root@node01 test]# javac TestDeadLock.java
[root@node01 test]# ll
總用量 28
-rw-r--r--. 1 root root 184 9月 11 10:39 TestDeadLock$1.class
-rw-r--r--. 1 root root 843 9月 11 10:39 TestDeadLock.class
-rw-r--r--. 1 root root 1567 9月 11 10:39 TestDeadLock.java
-rw-r--r--. 1 root root 1078 9月 11 10:39 TestDeadLock$Thread1.class
-rw-r--r--. 1 root root 1078 9月 11 10:39 TestDeadLock$Thread2.class
-rw-r--r--. 1 root root 573 9月 9 10:21 TestJVM.class
-rw-r--r--. 1 root root 261 9月 9 10:21 TestJVM.java
[root@node01 test]# java TestDeadLock
Thread1 拿到了 obj1 的鎖茬底!
Thread2 拿到了 obj2 的鎖沪悲!
#這里發(fā)生了死鎖,程序一直將等待下去
6.2.3阱表、使用jstack進(jìn)行分析
[root@node01 ~]# jstack 3256
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.141-b15 mixed mode):
"Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007f5bfc001000 nid=0xcff waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007f5c2c008800 nid=0xcb9 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" #9 prio=5 os_prio=0 tid=0x00007f5c2c0e9000 nid=0xcc5 waiting for monitor entry [0x00007f5c1c7f6000]
java.lang.Thread.State: BLOCKED (on object monitor)
at TestDeadLock$Thread2.run(TestDeadLock.java:47)
- waiting to lock <0x00000000f655dc40> (a java.lang.Object)
- locked <0x00000000f655dc50> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Thread-0" #8 prio=5 os_prio=0 tid=0x00007f5c2c0e7000 nid=0xcc4 waiting for monitor entry [0x00007f5c1c8f7000]
java.lang.Thread.State: BLOCKED (on object monitor)
at TestDeadLock$Thread1.run(TestDeadLock.java:27)
- waiting to lock <0x00000000f655dc50> (a java.lang.Object)
- locked <0x00000000f655dc40> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007f5c2c0d3000 nid=0xcc2 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f5c2c0b6000 nid=0xcc1 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f5c2c0b3000 nid=0xcc0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f5c2c0b1800 nid=0xcbf runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f5c2c07e800 nid=0xcbe in Object.wait() [0x00007f5c1cdfc000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000f6508ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x00000000f6508ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f5c2c07a000 nid=0xcbd in Object.wait() [0x00007f5c1cefd000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000f6506b68> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000f6506b68> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=0 tid=0x00007f5c2c072800 nid=0xcbc runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f5c2c01d800 nid=0xcba runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f5c2c01f800 nid=0xcbb runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007f5c2c0d6800 nid=0xcc3 waiting on condition
JNI global references: 6
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f5c080062c8 (object 0x00000000f655dc40, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f5c08004e28 (object 0x00000000f655dc50, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at TestDeadLock$Thread2.run(TestDeadLock.java:47)
- waiting to lock <0x00000000f655dc40> (a java.lang.Object)
- locked <0x00000000f655dc50> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at TestDeadLock$Thread1.run(TestDeadLock.java:27)
- waiting to lock <0x00000000f655dc50> (a java.lang.Object)
- locked <0x00000000f655dc40> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
在輸出的信息中殿如,已經(jīng)看到,發(fā)現(xiàn)了1個(gè)死鎖最爬,關(guān)鍵看這個(gè):
"Thread-1":
at TestDeadLock$Thread2.run(TestDeadLock.java:47)
- waiting to lock <0x00000000f655dc40> (a java.lang.Object)
- locked <0x00000000f655dc50> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at TestDeadLock$Thread1.run(TestDeadLock.java:27)
- waiting to lock <0x00000000f655dc50> (a java.lang.Object)
- locked <0x00000000f655dc40> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:748)
可以清晰的看到:
- Thread2獲取了 <0x00000000f655dc50> 的鎖涉馁,等待獲取 <0x00000000f655dc40> 這個(gè)鎖
- Thread1獲取了 <0x00000000f655dc40> 的鎖,等待獲取 <0x00000000f655dc50> 這個(gè)鎖
- 由此可見爱致,發(fā)生了死鎖烤送。
7、VisualVM工具的使用
VisualVM糠悯,能夠監(jiān)控線程帮坚,內(nèi)存情況,查看方法的CPU時(shí)間和內(nèi)存中的對(duì) 象互艾,已被GC的對(duì)象试和,反向查看分配的堆棧(如100個(gè)String對(duì)象分別由哪幾個(gè)對(duì)象分配出來的)。
VisualVM使用簡單纫普,幾乎0配置阅悍,功能還是比較豐富的,幾乎囊括了其它JDK自帶命令的所有功能。
- 內(nèi)存信息
- 線程信息
- Dump堆(本地進(jìn)程)
- Dump線程(本地進(jìn)程)
- 打開堆Dump节视。堆Dump可以用jmap來生成拳锚。
- 打開線程Dump
- 生成應(yīng)用快照(包含內(nèi)存信息、線程信息等等)
- 性能分析寻行。CPU分析(各個(gè)方法調(diào)用時(shí)間霍掺,檢查哪些方法耗時(shí)多),內(nèi)存分析(各類對(duì)象占用的內(nèi)存寡痰,檢查哪些類占用內(nèi)存多)
- ……
7.1抗楔、啟動(dòng)
在jdk的安裝目錄的bin目錄下棋凳,找到j(luò)visualvm.exe拦坠,雙擊打開即可。
7.2剩岳、查看本地進(jìn)程
7.3贞滨、查看CPU、內(nèi)存拍棕、類晓铆、線程運(yùn)行信息
7.4、查看線程詳情
也可以點(diǎn)擊右上角Dump按鈕绰播,將線程的信息導(dǎo)出骄噪,其實(shí)就是執(zhí)行的jstack命令。
發(fā)現(xiàn)蠢箩,顯示的內(nèi)容是一樣的链蕊。
7.5、抽樣器
抽樣器可以對(duì)CPU谬泌、內(nèi)存在一段時(shí)間內(nèi)進(jìn)行抽樣滔韵,以供分析。
7.6掌实、監(jiān)控遠(yuǎn)程的jvm
VisualJVM不僅是可以監(jiān)控本地jvm進(jìn)程陪蜻,還可以監(jiān)控遠(yuǎn)程的jvm進(jìn)程,需要借助于JMX技術(shù)實(shí)現(xiàn)贱鼻。
7.6.1宴卖、什么是JMX?
JMX(Java Management Extensions邻悬,即Java管理擴(kuò)展)是一個(gè)為應(yīng)用程序症昏、設(shè)備、系統(tǒng)等植入管理功能的框架拘悦。JMX可以跨越一系列異構(gòu)操作系統(tǒng)平臺(tái)齿兔、系統(tǒng)體系結(jié)構(gòu)和網(wǎng)絡(luò)傳輸協(xié)議,靈活的開發(fā)無縫集成的系統(tǒng)、網(wǎng)絡(luò)和服務(wù)管理應(yīng)用分苇。
7.6.2添诉、監(jiān)控遠(yuǎn)程的tomcat
想要監(jiān)控遠(yuǎn)程的tomcat,就需要在遠(yuǎn)程的tomcat進(jìn)行對(duì)JMX配置医寿,方法如下:
#在tomcat的bin目錄下栏赴,修改catalina.sh,添加如下的參數(shù)
JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
#這幾個(gè)參數(shù)的意思是:
#-Dcom.sun.management.jmxremote :允許使用JMX遠(yuǎn)程管理
#-Dcom.sun.management.jmxremote.port=9999 :JMX遠(yuǎn)程連接端口
#-Dcom.sun.management.jmxremote.authenticate=false :不進(jìn)行身份認(rèn)證靖秩,任何用戶都可以連接
#-Dcom.sun.management.jmxremote.ssl=false :不使用ssl
保存退出须眷,重啟tomcat。
7.6.3沟突、使用VisualJVM連接遠(yuǎn)程tomcat
添加遠(yuǎn)程主機(jī):
在一個(gè)主機(jī)下可能會(huì)有很多的jvm需要監(jiān)控花颗,所以接下來要在該主機(jī)上添加需要監(jiān)控的jvm:
連接成功。使用方法和前面就一樣了惠拭,就可以和監(jiān)控本地jvm進(jìn)程一樣扩劝,監(jiān)控遠(yuǎn)程的tomcat進(jìn)程。