怎么理解JVM啟動(dòng)參數(shù),針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
目前創(chuàng)新互聯(lián)建站已為成百上千的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)絡(luò)空間、綿陽(yáng)服務(wù)器托管、企業(yè)網(wǎng)站設(shè)計(jì)、清河網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶(hù)導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶(hù)和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。
因?yàn)镠otspot JDK提供的參數(shù)默認(rèn)值,在小版本之間不斷變化,參數(shù)之間也會(huì)互相影響。而且,服務(wù)器配置不同,都可能影響最后的效果。所以千萬(wàn)不要迷信網(wǎng)上的某篇文章(包括這篇)里面的參數(shù)配置,一切的配置都需要自己親身測(cè)試一番才能用。
針對(duì)于JVM參數(shù)默認(rèn)值不斷變化,可以使用-XX:+PrintFlagsFinal打印當(dāng)前環(huán)境JVM參數(shù)默認(rèn)值,比如:java -XX:PrintFlagsFinal -version,也可以用java [生產(chǎn)環(huán)境參數(shù)] -XX:+PrintFlagsFinal –version | grep [待查證的參數(shù)]查看具體的參數(shù)數(shù)據(jù)。
這里是一個(gè)8G服務(wù)器的參數(shù),JDK版本信息如下:
java version "1.8.0_73"
Java(TM) SE Runtime Environment (build 1.8.0_73-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.73-b02, mixed mode)
堆設(shè)置
堆內(nèi)存設(shè)置應(yīng)該算是一個(gè)Java程序猿的基本素養(yǎng),最少也得修改過(guò)Xms、Xmx、Xmn這三個(gè)參數(shù)了。但是一個(gè)2G堆大小的JVM,可能總共占用多少內(nèi)存的?
堆內(nèi)存 + 線程數(shù) * 線程棧 + 永久代 + 二進(jìn)制代碼 + 堆外內(nèi)存
2G + 1000 * 1M + 256M + 48/240M + (~2G) = 5.5G (3.5G)
- 堆內(nèi)存: 存儲(chǔ)Java對(duì)象,默認(rèn)為物理內(nèi)存的1/64
- 線程棧: 存儲(chǔ)局部變量(原子類(lèi)型,引用)及其他,默認(rèn)為1M
- 永久代: 存儲(chǔ)類(lèi)定義及常量池,注意JDK7/8的區(qū)別
- 二進(jìn)制代碼:JDK7與8,打開(kāi)多層編譯時(shí)的默認(rèn)值不一樣,從48到240M
- 堆外內(nèi)存: 被Netty,堆外緩存等使用,默認(rèn)最大值約為堆內(nèi)存大小
也就是說(shuō),堆內(nèi)存設(shè)置為2G,那一個(gè)有1000個(gè)線程的JVM可能需要占5.5G,在考慮系統(tǒng)占用、IO占用等等各種情況,一臺(tái)8G的服務(wù)器,也就啟動(dòng)一個(gè)服務(wù)了。當(dāng)然,如果線程數(shù)少、并發(fā)不高、壓力不大,還是可以啟動(dòng)多個(gè),而且也可以把堆內(nèi)存降低。
-Xms2g 與 -Xmx2g:堆內(nèi)存大小,第一個(gè)是最小堆內(nèi)存,第二個(gè)是最大堆內(nèi)存,比較合適的數(shù)值是2-4g,再大就得考慮GC時(shí)間
-Xmn1g 或 (-XX:NewSize=1g 和 -XX:MaxNewSize=1g) 或 -XX:NewRatio=1:設(shè)置新生代大小,JDK默認(rèn)新生代占堆內(nèi)存大小的1/3,也就是-XX:NewRatio=2。這里是設(shè)置的1g,也就是-XX:NewRatio=1。可以根據(jù)自己的需要設(shè)置。
-XX:MetaspaceSize=128m 和 -XX:MaxMetaspaceSize=512m,JDK8的永生代幾乎可用完機(jī)器的所有內(nèi)存,為了保護(hù)服務(wù)器不會(huì)因?yàn)閮?nèi)存占用過(guò)大無(wú)法連接,需要設(shè)置一個(gè)128M的初始值,512M的最大值保護(hù)一下。
-XX:SurvivorRatio:新生代中每個(gè)存活區(qū)的大小,默認(rèn)為8,即1/10的新生代, 1/(SurvivorRatio+2),有人喜歡設(shè)小點(diǎn)省點(diǎn)給新生代,但要避免太小使得存活區(qū)放不下臨時(shí)對(duì)象而要晉升到老生代,還是從GC Log里看實(shí)際情況了。
-Xss256k:在堆之外,線程占用棧內(nèi)存,默認(rèn)每條線程為1M。存放方法調(diào)用出參入?yún)⒌臈?、局部變量、?biāo)量替換后的局部變量等,有人喜歡設(shè)小點(diǎn)節(jié)約內(nèi)存開(kāi)更多線程。但反正內(nèi)存夠也就不必要設(shè)小,有人喜歡再設(shè)大點(diǎn),特別是有JSON解析之類(lèi)的遞歸調(diào)用時(shí)不能設(shè)太小。
-XX:MaxDirectMemorySize:堆外內(nèi)存/直接內(nèi)存的大小,默認(rèn)為堆內(nèi)存減去一個(gè)Survivor區(qū)的大小。
-XX:ReservedCodeCacheSize:JIT編譯后二進(jìn)制代碼的存放區(qū),滿(mǎn)了之后就不再編譯。默認(rèn)開(kāi)多層編譯240M,可以在JMX里看看CodeCache的大小。
GC設(shè)置
目前比較主流的GC是CMS和G1,有大神建議以8G為界。(據(jù)說(shuō)JDK 9默認(rèn)的是G1)。因?yàn)閼?yīng)用設(shè)置的內(nèi)存都比較小,所以選擇CMS收集器。下面的參數(shù)也是針對(duì)CMS收集器的,等之后如果有需要,再補(bǔ)充G1收集器的參數(shù)。
CMS設(shè)置
-XX:+UseConcMarkSweepGC:?jiǎn)⒂肅MS垃圾收集器
-XX:CMSInitiatingOccupancyFraction=80 與 -XX:+UseCMSInitiatingOccupancyOnly:兩個(gè)參數(shù)需要配合使用,否則第一個(gè)參數(shù)的75只是一個(gè)參考值,JVM會(huì)重新計(jì)算GC的時(shí)間。
-XX:MaxTenuringThreshold=15:對(duì)象在Survivor區(qū)熬過(guò)多少次Young GC后晉升到年老代,默認(rèn)是15。Young GC是最大的應(yīng)用停頓來(lái)源,而新生代里GC后存活對(duì)象的多少又直接影響停頓的時(shí)間,所以如果清楚Young GC的執(zhí)行頻率和應(yīng)用里大部分臨時(shí)對(duì)象的最長(zhǎng)生命周期,可以把它設(shè)的更短一點(diǎn),讓其實(shí)不是臨時(shí)對(duì)象的新生代長(zhǎng)期對(duì)象趕緊晉升到年老代。
-XX:-DisableExplicitGC:允許使用System.gc()主動(dòng)調(diào)用GC。這里需要說(shuō)明下,有的JVM優(yōu)化建議是設(shè)置-XX:-DisableExplicitGC,關(guān)閉手動(dòng)調(diào)用System.gc()。這是應(yīng)為System.gc()是觸發(fā)Full GC,頻繁的Full GC會(huì)嚴(yán)重影響性能。但是很多NIO框架,比如Netty,會(huì)使用堆外內(nèi)存,如果沒(méi)有Full GC的話,堆外內(nèi)存就無(wú)法回收。如果不主動(dòng)調(diào)用System.gc(),就需要等到JVM自己觸發(fā)Full GC,這個(gè)時(shí)候,就可能引起長(zhǎng)時(shí)間的停頓(STW),而且機(jī)器負(fù)載也會(huì)升高。所以不能夠完全禁止System.gc(),又得縮短Full GC的時(shí)間,那就使用-XX:+ExplicitGCInvokesConcurrent或-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses選項(xiàng),使用CMS收集器來(lái)觸發(fā)Full GC。這兩個(gè)選項(xiàng)需要配合-XX:+UseConcMarkSweepGC使用。
-XX:+ExplicitGCInvokesConcurrent:使用System.gc()時(shí)觸發(fā)CMS GC,而不是Full GC。默認(rèn)是不開(kāi)啟的,只有使用-XX:+UseConcMarkSweepGC選項(xiàng)的時(shí)候才能開(kāi)啟這個(gè)選項(xiàng)。
-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses:使用System.gc()時(shí),永久代也被包括進(jìn)CMS范圍內(nèi)。只有使用-XX:+UseConcMarkSweepGC選項(xiàng)的時(shí)候才能開(kāi)啟這個(gè)選項(xiàng)。
-XX:+ParallelRefProcEnabled:默認(rèn)為false,并行的處理Reference對(duì)象,如WeakReference,除非在GC log里出現(xiàn)Reference處理時(shí)間較長(zhǎng)的日志,否則效果不會(huì)很明顯。
-XX:+ScavengeBeforeFullGC:在Full GC執(zhí)行前先執(zhí)行一次Young GC。
-XX:+UseGCOverheadLimit: 限制GC的運(yùn)行時(shí)間。如果GC耗時(shí)過(guò)長(zhǎng),就拋OOM。
-XX:+UseParallelGC:設(shè)置并行垃圾收集器
-XX:+UseParallelOldGC:設(shè)置老年代使用并行垃圾收集器
-XX:-UseSerialGC:關(guān)閉串行垃圾收集器
-XX:+CMSParallelInitialMarkEnabled 和 -XX:+CMSParallelRemarkEnabled:降低標(biāo)記停頓
-XX:+CMSScavengeBeforeRemark:默認(rèn)為關(guān)閉,在CMS remark前,先執(zhí)行一次minor GC將新生代清掉,這樣從老生代的對(duì)象引用到的新生代對(duì)象的個(gè)數(shù)就少了,停止全世界的CMS remark階段就短一些。如果看到GC日志里remark階段的時(shí)間超長(zhǎng),可以打開(kāi)此項(xiàng)看看有沒(méi)有效果,否則還是不要打開(kāi)了,白白多了次YGC。
-XX:CMSWaitDuration=10000:設(shè)置垃圾收集的最大時(shí)間間隔,默認(rèn)是2000。
-XX:+CMSClassUnloadingEnabled:在CMS中清理永久代中的過(guò)期的Class而不等到Full GC,JDK7默認(rèn)關(guān)閉而JDK8打開(kāi)??醋约呵闆r,比如有沒(méi)有運(yùn)行動(dòng)態(tài)語(yǔ)言腳本如Groovy產(chǎn)生大量的臨時(shí)類(lèi)。它會(huì)增加CMS remark的暫停時(shí)間,所以如果新類(lèi)加載并不頻繁,這個(gè)參數(shù)還是不開(kāi)的好。
GC日志
GC過(guò)程可以通過(guò)GC日志來(lái)提供優(yōu)化依據(jù)。
-XX:+PrintGCDetails:?jiǎn)⒂胓c日志打印功能
-Xloggc:/path/to/gc.log:指定gc日志位置
-XX:+PrintHeapAtGC:打印GC前后的詳細(xì)堆棧信息
-XX:+PrintGCDateStamps:打印可讀的日期而不是時(shí)間戳
-XX:+PrintGCApplicationStoppedTime:打印所有引起JVM停頓時(shí)間,如果真的發(fā)現(xiàn)了一些不知什么的停頓,再臨時(shí)加上-XX:+PrintSafepointStatistics -XX: PrintSafepointStatisticsCount=1找原因。
-XX:+PrintGCApplicationConcurrentTime:打印JVM在兩次停頓之間正常運(yùn)行時(shí)間,與-XX:+PrintGCApplicationStoppedTime一起使用效果更佳。
-XX:+PrintTenuringDistribution:查看每次minor GC后新的存活周期的閾值
-XX:+UseGCLogFileRotation 與 -XX:NumberOfGCLogFiles=10 與 -XX:GCLogFileSize=10M:GC日志在重啟之后會(huì)清空,但是如果一個(gè)應(yīng)用長(zhǎng)時(shí)間不重啟,那GC日志會(huì)增加,所以添加這3個(gè)參數(shù),是GC日志滾動(dòng)寫(xiě)入文件,但是如果重啟,可能名字會(huì)出現(xiàn)混亂。
-XX:PrintFLSStatistics=1:打印每次GC前后內(nèi)存碎片的統(tǒng)計(jì)信息
其他參數(shù)設(shè)置
-ea:?jiǎn)⒂脭嘌?,這個(gè)沒(méi)有什么好說(shuō)的,可以選擇啟用,或這選擇不啟用,沒(méi)有什么大的差異。完全根據(jù)自己的系統(tǒng)進(jìn)行處理。
-XX:+UseThreadPriorities:?jiǎn)⒂镁€程優(yōu)先級(jí),主要是因?yàn)槲覀兛梢越o予周期性任務(wù)更低的優(yōu)先級(jí),以避免干擾客戶(hù)端工作。在我當(dāng)前的環(huán)境中,是默認(rèn)啟用的。
-XX:ThreadPriorityPolicy=42:允許降低線程優(yōu)先級(jí)
-XX:+HeapDumpOnOutOfMemoryError:發(fā)生內(nèi)存溢出是進(jìn)行heap-dump
-XX:HeapDumpPath=/path/to/java_pid.hprof:這個(gè)參數(shù)與-XX:+HeapDumpOnOutOfMemoryError共同作用,設(shè)置heap-dump時(shí)內(nèi)容輸出文件。
-XX:ErrorFile=/path/to/hs_err_pid.log:指定致命錯(cuò)誤日志位置。一般在JVM發(fā)生致命錯(cuò)誤時(shí)會(huì)輸出類(lèi)似hs_err_pid.log的文件,默認(rèn)是在工作目錄中(如果沒(méi)有權(quán)限,會(huì)嘗試在/tmp中創(chuàng)建),不過(guò)還是自己指定位置更好一些,便于收集和查找,避免丟失。
-XX:StringTableSize=1000003:指定字符串常量池大小,默認(rèn)值是60013。對(duì)Java稍微有點(diǎn)常識(shí)的應(yīng)該知道,字符串是常量,創(chuàng)建之后就不可修改了,這些常量所在的地方叫做字符串常量池。如果自己系統(tǒng)中有很多字符串的操作,且這些字符串值比較固定,在允許的情況下,可以適當(dāng)調(diào)大一些池子大小。
-XX:+AlwaysPreTouch:在啟動(dòng)時(shí)把所有參數(shù)定義的內(nèi)存全部捋一遍。使用這個(gè)參數(shù)可能會(huì)使啟動(dòng)變慢,但是在后面內(nèi)存使用過(guò)程中會(huì)更快??梢员WC內(nèi)存頁(yè)面連續(xù)分配,新生代晉升時(shí)不會(huì)因?yàn)樯暾?qǐng)內(nèi)存頁(yè)面使GC停頓加長(zhǎng)。通常只有在內(nèi)存大于32G的時(shí)候才會(huì)有感覺(jué)。
-XX:-UseBiasedLocking:禁用偏向鎖(在存在大量鎖對(duì)象的創(chuàng)建且高度并發(fā)的環(huán)境下(即非多線程高并發(fā)應(yīng)用)禁用偏向鎖能夠帶來(lái)一定的性能優(yōu)化)
-XX:AutoBoxCacheMax=20000:增加數(shù)字對(duì)象自動(dòng)裝箱的范圍,JDK默認(rèn)-128~127的int和long,超出范圍就會(huì)即時(shí)創(chuàng)建對(duì)象,所以,增加范圍可以提高性能,但是也是需要測(cè)試。
-XX:-OmitStackTraceInFastThrow:不忽略重復(fù)異常的棧,這是JDK的優(yōu)化,大量重復(fù)的JDK異常不再打印其StackTrace。但是如果系統(tǒng)是長(zhǎng)時(shí)間不重啟的系統(tǒng),在同一個(gè)地方跑了N多次異常,結(jié)果就被JDK忽略了,那豈不是查看日志的時(shí)候就看不到具體的StackTrace,那還怎么調(diào)試,所以還是關(guān)了的好。
-XX:+PerfDisableSharedMem:?jiǎn)⒂脴?biāo)準(zhǔn)內(nèi)存使用。JVM控制分為標(biāo)準(zhǔn)或共享內(nèi)存,區(qū)別在于一個(gè)是在JVM內(nèi)存中,一個(gè)是生成/tmp/hsperfdata_{userid}/{pid}文件,存儲(chǔ)統(tǒng)計(jì)數(shù)據(jù),通過(guò)mmap映射到內(nèi)存中,別的進(jìn)程可以通過(guò)文件訪問(wèn)內(nèi)容。通過(guò)這個(gè)參數(shù),可以禁止JVM寫(xiě)在文件中寫(xiě)統(tǒng)計(jì)數(shù)據(jù),代價(jià)就是jps、jstat這些命令用不了了,只能通過(guò)jmx獲取數(shù)據(jù)。但是在問(wèn)題排查是,jps、jstat這些小工具是很好用的,比jmx這種很重的東西好用很多,所以需要自己取舍。這里有個(gè)GC停頓的例子。
-Djava.net.preferIPv4Stack=true:這個(gè)參數(shù)是屬于網(wǎng)絡(luò)問(wèn)題的一個(gè)參數(shù),可以根據(jù)需要設(shè)置。在某些開(kāi)啟ipv6的機(jī)器中,通過(guò)InetAddress.getLocalHost().getHostName()可以獲取完整的機(jī)器名,但是在ipv4的機(jī)器中,可能通過(guò)這個(gè)方法獲取的機(jī)器名不完整,可以通過(guò)這個(gè)參數(shù)來(lái)獲取完整機(jī)器名。
大神給出的例子
下面貼上大神給出的例子,可以參考使用,不過(guò)還是建議在自己的環(huán)境中有針對(duì)的驗(yàn)證之后再使用,畢竟大神的環(huán)境和自己的環(huán)境還是不同。
性能相關(guān)
-XX:-UseBiasedLocking -XX:-UseCounterDecay -XX:AutoBoxCacheMax=20000
-XX:+PerfDisableSharedMem(可選) -XX:+AlwaysPreTouch -Djava.security.egd=file:/dev/./urandom
內(nèi)存大小相關(guān)(JDK7)
-Xms4096m -Xmx4096m -Xmn2048m -XX:MaxDirectMemorySize=4096m
-XX:PermSize=128m -XX:MaxPermSize=512m -XX:ReservedCodeCacheSize=240M
如果使用jdk8,就把-XX:PermSize=128m -XX:MaxPermSize=512m換成-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m,正如前面所說(shuō)的,這兩套參數(shù)是為了保證安全的,建議還是加上。
CMS GC相關(guān)
-XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly -XX:MaxTenuringThreshold=6
-XX:+ExplicitGCInvokesConcurrent -XX:+ParallelRefProcEnabled
GC日志相關(guān)
-Xloggc:/dev/shm/app-gc.log -XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCDateStamps -XX:+PrintGCDetails
異常日志相關(guān)
-XX:-OmitStackTraceInFastThrow -XX:ErrorFile=${LOGDIR}/hs_err_%p.log
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${LOGDIR}/
JMX相關(guān)
-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
參考文章
Java性能優(yōu)化指南1.8版,及唯品會(huì)的實(shí)戰(zhàn)
Java中的逃逸分析和TLAB以及Java對(duì)象分配
The Four Month Bug: JVM statistics cause garbage collection pauses
========================================================
Cassandra,在Linux系統(tǒng)中,默認(rèn)的JVM啟動(dòng)參數(shù)
-ea
-Xms4096MB
-Xmx4096MB
-Xss128K
-XX:+UseThreadPriorities
-XX:ThreadPriorityPolicy=42
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=1
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
-ea 從JDK1.4開(kāi)始,支持?jǐn)嘌詸C(jī)制,可以用于診斷運(yùn)行時(shí)問(wèn)題。-ea用來(lái)啟動(dòng)JVM的斷言機(jī)制,如果不加packagename和classname,默認(rèn)運(yùn)行所有包和類(lèi)中的斷言。如果希望進(jìn)行運(yùn)行某些包和類(lèi)的斷言,可以類(lèi)似 -ea:com.foo.util進(jìn)行設(shè)置。
-XX:+UseThreadPriorities 開(kāi)啟JAVA線程優(yōu)先級(jí)的功能
-XX:ThreadPriorityPolicy JAVA線程優(yōu)先級(jí)的側(cè)臉
-XX:+HeapDumpOnOutOfMemoryError 在JVM內(nèi)存不足而引發(fā)程序崩潰時(shí),講內(nèi)存中的數(shù)據(jù)導(dǎo)出
-Xss128K 設(shè)置線程棧的大小,默認(rèn)單位為字節(jié),也可以用KB或MB來(lái)設(shè)置。通常操作系統(tǒng)分配給線程棧的默認(rèn)大小為1MB。另外,也可以在JAVA CODE中創(chuàng)建線程對(duì)象時(shí)設(shè)置棧的大小,Thread( TheadGroup group, Runnable target, String name, long stackSize)
-XX:+UseParNewGC 縮短JVM GC執(zhí)行minor收集的時(shí)間
-XX:+UseConcMarkSweepGC 縮短JVM GC執(zhí)行major收集的時(shí)間
-XX:+CMSParallelRemarkEnabled 縮短JVM GC執(zhí)行remark操作的中斷時(shí)間
-XX:SurvivorRatio 設(shè)置young generation 在 survivor spaces中所占用的大小比例
-XX:MaxTenuringThreshold 表示一個(gè)對(duì)象如果在survivor spaces移動(dòng)多少次還沒(méi)有被回收就放入老年代
-XX:CMSInitiatingOccupancyFraction 表示老年代占到約百分之多少的時(shí)候開(kāi)始執(zhí)行CMS操作
-XX:+UseCMSInitiatingOccupancyOnly 這個(gè)參數(shù)讓JVM只使用CMSInitiatingOccupancyFraction 中定義的值而不用在運(yùn)行中計(jì)算
關(guān)于怎么理解JVM啟動(dòng)參數(shù)問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。