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ù)量?
- .......
我們將對(duì)jvm有更深入的學(xué)習(xí)琳猫,我們不僅要讓程序能跑起來(lái)伟叛,而且是可以
跑的更快!可以分析解決在生產(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)定的娃善,在未來(lái)的JVM版本中不會(huì)改變论衍,可以使用java -help
檢索出所有的標(biāo)準(zhǔn)參數(shù)。
[root@smile /]# 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.
-cp <目錄和 zip/jar 文件的類搜索路徑>
-classpath <目錄和 zip/jar 文件的類搜索路徑>
用 : 分隔的目錄, JAR 檔案
和 ZIP 檔案列表, 用于搜索類文件聚磺。
-D<名稱>=<值>
設(shè)置系統(tǒng)屬性
-verbose:[class|gc|jni]
啟用詳細(xì)輸出
-version 輸出產(chǎn)品版本并退出
-version:<值>
警告: 此功能已過時(shí), 將在
未來(lái)發(fā)行版中刪除坯台。
需要指定的版本才能運(yùn)行
-showversion 輸出產(chǎn)品版本并繼續(xù)
-jre-restrict-search | -no-jre-restrict-search
警告: 此功能已過時(shí), 將在
未來(lái)發(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ī)代理庫(kù) <libname>, 例如 -agentlib:hprof
另請(qǐng)參閱 -agentlib:jdwp=help 和 -agentlib:hprof=help
-agentpath:<pathname>[=<選項(xiàng)>]
按完整路徑名加載本機(jī)代理庫(kù)
-javaagent:<jarpath>[=<選項(xiàng)>]
加載 Java 編程語(yǔ)言代理, 請(qǐng)參閱 java.lang.instrument
-splash:<imagepath>
2.2.1瘫寝、實(shí)戰(zhàn)
實(shí)戰(zhàn)1:查看jvm版本
[root@smile /]# java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, 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("null");
} else {
System.out.println(str);
}
}
}
進(jìn)行編譯暮屡、測(cè)試:
#編譯
[root@node01 test]# javac TestJVM.java
#測(cè)試
[root@node01 test]# java TestJVM
null
[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ì)來(lái)講會(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類型针肥。
測(cè)試:
[root@smile test]# java -client -showversion TestJVM
java version "1.8.0_201"
Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)
null
[root@smile test]# java -server -showversion TestJVM
java version "1.8.0_201"
Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)
null
[root@smile test]#
#由于機(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@smile 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ù)期將來(lái)的默認(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é)碼編譯成本地代碼轻绞,從而帶來(lái)最大程度的優(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è)置為解釋模式
[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)
null
#強(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)
null
#注意:編譯模式下眼姐,第一次執(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)
null
2.4众旗、-XX參數(shù)
-XX參數(shù)也是非標(biāo)準(zhǔn)參數(shù),主要用于jvm的調(diào)優(yōu)和debug操作趟畏。
-XX參數(shù)的使用有2種方式贡歧,一種是boolean類型,一種是非boolean類型:
boolean類型
格式:-XX:[+-]
如:-XX:+DisableExplicitGC 表示禁用手動(dòng)調(diào)用gc操作拱镐,也就是說調(diào)用 System.gc()無(wú)效非boolean類型
格式:-XX:
如:-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)
null
2.5艘款、-Xms與-Xmx參數(shù)
屬于-XX參數(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@smile test]# java -Xms64m -Xmx128m TestJVM
null
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}
…………………………略…………………………………………
由上述的信息可以看出响迂,參數(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用于測(cè)試,來(lái)觀察下運(yùn)行的jvm參數(shù)
#查看所有的參數(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)存模型
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ū)主要保存生命周期長(zhǎng)的對(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ì)遇到
java.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ū)?
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)存空間