Java虚拟机:常见JVM参数配置和GC性能优化
一、常見的JVM參數(shù)配置:
1、垃圾回收統(tǒng)計(jì)信息:
-XX:+PrintGC?????打印GC簡(jiǎn)要信息
-XX:+PrintGCDetails打印GC的詳細(xì)信息
-XX:+PrintGCTimeStamps打印CG發(fā)生的時(shí)間戳
-Xloggc:log/gc.log?指定GC log的位置,以文件輸出
-XX:+PrintHeapAtGC?每一次GC前和GC后,都打印堆信息。
2、堆設(shè)置:
-Xms:初始堆大,最小堆
-Xmx:最大堆大小
-Xmn:設(shè)置新生代的大小
-XX:NewRatio新生代和年老代的比值,如為3,表示年輕代與年老代比值為1:3,年輕代占整個(gè)年輕代年老代之和的1/4
-XX:SurvivorRatio設(shè)置兩個(gè)Survivor區(qū)和eden的比值。注意Survivor區(qū)有兩個(gè)。如:8,表示Eden:Survivor=8:2,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/10
-XX:PermSize:設(shè)置永久區(qū)的初始空間
-XX:MaxPermSize:設(shè)置永久區(qū)的最大空間。
-XX:+MaxTenuringThreshold=10:新生代垃圾的最大年齡,代表對(duì)象在Survivor區(qū)經(jīng)過(guò)10次復(fù)制以后才進(jìn)入老年代。如果設(shè)置為0,則年輕代對(duì)象不經(jīng)過(guò)Survivor區(qū),直接進(jìn)入老年代。
-XX:+PretenureSizeThreshold:設(shè)置大對(duì)象直接進(jìn)入老年代的閾值。當(dāng)對(duì)象的大小超過(guò)這個(gè)值時(shí),將直接在老年代分配。?
3、棧的分配參數(shù):
-Xss:設(shè)置??臻g的大小。
4、垃圾收集器設(shè)置:
(1)串行收集器的設(shè)置:
-XX:+UseSerialGC:設(shè)置串行收集器,一般適用于小型應(yīng)用和單處理器,算法比較簡(jiǎn)單,GC效率也較高,但可能會(huì)給應(yīng)用帶來(lái)停頓。
(2)并行回收收集器設(shè)置:(ParallelGC收集器的目標(biāo)是達(dá)到一個(gè)可控制的吞吐量)
-XX:+UseParNewGC:設(shè)置年輕代為并行收集。
-XX:+UseParallelGC:設(shè)置年輕代使用并行回收收集器。多個(gè)線程并行執(zhí)行GC,一般適用于多處理器系統(tǒng)中,可以提高GC的效率,但算法復(fù)雜,系統(tǒng)消耗較大。
-XX:+UseParalledlOldGC:設(shè)置老年代為并行回收收集器,Java1.6之后才出現(xiàn)。
-XX:ParallelGCThreads=n:設(shè)置并行收集器收集時(shí)使用的線程數(shù),最好與CPU數(shù)目相等。
-XX:MaxGCPauseMillis=n:設(shè)置年輕代每次并行垃圾回收的最大暫停時(shí)間。
-XX:GCTimeRatio=n:設(shè)置垃圾回收時(shí)間占程序運(yùn)行時(shí)間的百分比。公式為1/(1+n)
-XX:+UseAdaptiveSizePolicy:自適應(yīng)策略,自動(dòng)選擇年輕代區(qū)大小和相應(yīng)的Survivor區(qū)比例。
(3)CMS并發(fā)收集器:(以最短停頓為目標(biāo))
-XX:+UseConcMarkSweepGC:使用CMS內(nèi)存收集。
-XX:+ParallelCMSThreads: 設(shè)定 CMS 的線程數(shù)量。?
-XX:CMSFullGCsBeforeCompaction:CMS多少次后進(jìn)行內(nèi)存壓縮,由于并發(fā)收集器不對(duì)內(nèi)存空間進(jìn)行壓縮整理,所以運(yùn)行一段時(shí)間以后會(huì)產(chǎn)生"碎片",使得運(yùn)行效率降低。
-XX:+UseCMSCompactAtFullCollection:在FULL GC的時(shí)候,對(duì)年老代的壓縮。CMS是不會(huì)移動(dòng)內(nèi)存的,因此,這個(gè)非常容易產(chǎn)生碎片,導(dǎo)致內(nèi)存不夠用,因此,內(nèi)存的壓縮這個(gè)時(shí)候就會(huì)被啟用。可能會(huì)影響性能,但是可以消除碎片。
-XX:+CMSInitiatingOccupancyFraction:設(shè)置 CMS 收集器在老年代空間被使用多少后觸發(fā),默認(rèn)為 68%。?
-XX:+CMSClassUnloadingEnabled:允許對(duì)類元數(shù)據(jù)進(jìn)行回收。
-XX:+CMSParallelRemarkEndable:啟用并行重標(biāo)記。?
-XX:CMSInitatingPermOccupancyFraction:當(dāng)永久區(qū)占用率達(dá)到這一百分比后,啟動(dòng) CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled 激活了)。?
-XX:UseCMSInitatingOccupancyOnly:表示只在到達(dá)閾值的時(shí)候,才進(jìn)行 CMS 回收。?
-XX:+CMSIncrementalMode:使用增量模式,比較適合單 CPU。?
(4)G1收集器:
-XX:+UseG1GC:使用 G1 回收器。?
-XX:+UnlockExperimentalVMOptions:允許使用實(shí)驗(yàn)性參數(shù)。?
-XX:+MaxGCPauseMills:設(shè)置最大垃圾收集停頓時(shí)間。?
-XX:+GCPauseIntervalMills:設(shè)置停頓間隔時(shí)間。?
更多參數(shù)的詳細(xì)介紹和設(shè)置可以參考這篇博客:https://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html
二、JVM的GC性能優(yōu)化:
對(duì)于GC的性能主要有2個(gè)方面的指標(biāo):吞吐量(工作時(shí)間不算,gc的時(shí)間占總的時(shí)間比)和暫停時(shí)間。
1. 堆大小:
?????? 默認(rèn)情況下,vm會(huì)增加/減少heap大小以維持free space在整個(gè)vm中占的比例,這個(gè)比例由MinHeapFreeRatio和MaxHeapFreeRatio指定。
一般而言,server端的app會(huì)有以下規(guī)則:
(1)對(duì)vm分配盡可能多的內(nèi)存;
(2)將Xms和Xmx設(shè)為一樣的值。如果虛擬機(jī)啟動(dòng)時(shí)設(shè)置使用的內(nèi)存比較小,這個(gè)時(shí)候又需要初始化很多對(duì)象,虛擬機(jī)就必須重復(fù)地增加內(nèi)存。
(3)處理器核數(shù)增加,內(nèi)存也跟著增大。
2. 年輕代:
?????? (1)對(duì)于程序流暢性運(yùn)行影響的因素是新生代的大小。新生代越大,minor collection越少;但是在堆大小固定情況下,新生代越大就意味著越小的老年代,就意味著更多的major collection。
?????? (2)NewRatio反映的是新生代和老年代的大小比例。NewSize和MaxNewSize反映的是young generation大小的下限和上限,將這兩個(gè)值設(shè)為一樣就固定了young generation的大小(同Xms和Xmx設(shè)為一樣)。
??????(3)SurvivorRatio也可以優(yōu)化survivor的大小,不過(guò)這對(duì)于性能的影響不是很大。SurvivorRatio是Eden和Survior大小比例。
一般而言,server端的app會(huì)有以下規(guī)則:
(1)首先決定能分配給vm的最大的堆大小,然后設(shè)定最佳的young generation的大小;
(2)如果堆大小固定后,增加新生代的大小意味著減小老年代大小。讓老年代在任何時(shí)候夠大,能夠容納所有存活的對(duì)象(留10%-20%的空余)。
3、年輕代大小選擇:
(1)響應(yīng)時(shí)間優(yōu)先的應(yīng)用:盡可能設(shè)大,直到接近系統(tǒng)的最低響應(yīng)時(shí)間限制,在此種情況下,年輕代收集發(fā)生的頻率也是最小的,同時(shí),減少到達(dá)年老代的對(duì)象。
(2)吞吐量?jī)?yōu)先的應(yīng)用:盡可能的設(shè)置大,可能到達(dá)Gbit的程度,因?yàn)閷?duì)響應(yīng)時(shí)間沒有要求,垃圾收集可以并行進(jìn)行,一般適合8CPU以上的應(yīng)用。
(3)避免設(shè)置過(guò)小。當(dāng)新生代設(shè)置過(guò)小時(shí)會(huì)導(dǎo)致:①YGC次數(shù)更加頻繁;②可能導(dǎo)致YGC對(duì)象直接進(jìn)入舊生代,如果此時(shí)舊生代滿了,會(huì)觸發(fā)FGC。
4、年老代大小選擇:
(1)響應(yīng)時(shí)間優(yōu)先的應(yīng)用:年老代使用并發(fā)收集器。如果堆設(shè)置小了,可以會(huì)造成內(nèi)存碎片、高回收頻率以及應(yīng)用暫停而使用傳統(tǒng)的標(biāo)記清除方式;如果堆大了,則需要較長(zhǎng)的收集時(shí)間。一般需要參考以下數(shù)據(jù):
并發(fā)垃圾收集信息、持久代并發(fā)收集次數(shù)、傳統(tǒng)GC信息、花在年輕代和年老代回收上的時(shí)間比例。
(2)吞吐量?jī)?yōu)先的應(yīng)用:一般吞吐量?jī)?yōu)先的應(yīng)用都有一個(gè)很大的年輕代和一個(gè)較小的年老代,這樣可以盡可能回收掉大部分短期對(duì)象,減少中期的對(duì)象,而年老代盡存放長(zhǎng)期存活對(duì)象。
5、較小堆引起的碎片問(wèn)題:
因?yàn)镃MS年老代的并發(fā)收集器使用標(biāo)記清除算法,所以不會(huì)對(duì)堆進(jìn)行壓縮。當(dāng)收集器回收時(shí),他會(huì)把相鄰的空間進(jìn)行合并,這樣可以分配給較大的對(duì)象。但是,當(dāng)堆空間較小時(shí),運(yùn)行一段時(shí)間以后,就會(huì)出現(xiàn)"碎片",如果并發(fā)收集器找不到足夠的空間,那么并發(fā)收集器將會(huì)停止,可能需要進(jìn)行如下配置:
-XX:+UseCMSCompactAtFullCollection:使用并發(fā)收集器時(shí),開啟對(duì)年老代的壓縮。
-XX:CMSFullGCsBeforeCompaction=0:上面配置開啟的情況下,這里設(shè)置多少次Full GC后,對(duì)年老代進(jìn)行壓縮。
4、用64位操作系統(tǒng),Linux下64位的jdk比32位jdk要慢一些,但是吃得內(nèi)存更多,吞吐量更大。
5、XMX和XMS設(shè)置一樣大,MaxPermSize和MinPermSize設(shè)置一樣大,這樣可以減輕伸縮堆大小帶來(lái)的壓力
6、CMS的目標(biāo)是最短的GC停頓時(shí)間,使用CMS的好處是用盡量少的新生代,然后老生代利用CMS并行收集,這樣能保證系統(tǒng)低延遲的吞吐效率。
7、系統(tǒng)停頓的時(shí)候可能是GC的問(wèn)題也可能是程序的問(wèn)題,多用jmap和jstack查看,或者killall -3 java,然后查看java控制臺(tái)日志,能看出很多問(wèn)題。
8、如果用了緩存,那么年老代應(yīng)該大一些,緩存的HashMap不應(yīng)該無(wú)限制長(zhǎng),建議采用LRU算法的Map做緩存,LRUMap的最大長(zhǎng)度也要根據(jù)實(shí)際情況設(shè)定。
9、采用并發(fā)回收時(shí),年輕代小一點(diǎn),年老代要大,因?yàn)槟昀洗玫氖遣l(fā)回收,即使時(shí)間長(zhǎng)點(diǎn)也不會(huì)影響其他程序繼續(xù)運(yùn)行,網(wǎng)站不會(huì)停頓。
10、JVM參數(shù)的設(shè)置(特別是 –Xmx –Xms –Xmn -XX:SurvivorRatio? -XX:MaxTenuringThreshold等參數(shù)的設(shè)置)沒有一個(gè)固定的公式,需要根據(jù)PV old區(qū)實(shí)際數(shù)據(jù)、YGC次數(shù)等多方面來(lái)衡量。為了避免promotion faild可能會(huì)導(dǎo)致xmn設(shè)置偏小,也意味著YGC的次數(shù)會(huì)增多,處理并發(fā)訪問(wèn)的能力下降等問(wèn)題。每個(gè)參數(shù)的調(diào)整都需要經(jīng)過(guò)詳細(xì)的性能測(cè)試,才能找到特定應(yīng)用的最佳配置。
promotion failed:(晉升失敗)
垃圾回收時(shí)promotion failed,一般可能是兩種原因產(chǎn)生,第一個(gè)原因是To survivor救助空間不夠,救助空間里的對(duì)象還不應(yīng)該被移動(dòng)到年老代,但年輕代又有很多對(duì)象需要放入救助空間;第二個(gè)原因是年老代沒有足夠的空間接納來(lái)自年輕代的對(duì)象;這兩種情況都會(huì)轉(zhuǎn)向Full GC,網(wǎng)站停頓時(shí)間較長(zhǎng)。
解決方案:
第一個(gè)原因解決辦法是去掉救助空間,設(shè)置-XX:SurvivorRatio=65536?-XX:MaxTenuringThreshold=0即可,但是因?yàn)闆]有用到救助空間,所以年老代容易滿,Full GC執(zhí)行會(huì)比較頻繁,所以可以把救助空間加大,這樣也不會(huì)有promotion failed。
第二個(gè)原因我的解決辦法是設(shè)置CMSInitiatingOccupancyFraction為某個(gè)值(假設(shè)70),這樣年老代空間到70%時(shí)就開始執(zhí)行CMS,年老代有足夠的空間接納來(lái)自年輕代的對(duì)象。
11、實(shí)際編程中的性能優(yōu)化:
下面是一些在實(shí)際寫程序的過(guò)程中應(yīng)該注意的點(diǎn):養(yǎng)成這些習(xí)慣可以在一定程度上減少內(nèi)存的無(wú)謂消耗,進(jìn)一步就可以減少因?yàn)閮?nèi)存不足導(dǎo)致GC不斷。參考自:https://blog.csdn.net/antony9118/article/details/51375662
(1)減少new對(duì)象。每次new對(duì)象之后,都要開辟新的內(nèi)存空間。這些對(duì)象不被引用之后,還要回收掉。因此,如果最大限度地合理重用對(duì)象,或者使用基本數(shù)據(jù)類型替代對(duì)象,都有助于節(jié)省內(nèi)存;
(2)多使用局部變量,減少使用靜態(tài)變量。局部變量被創(chuàng)建在棧中,存取速度快。靜態(tài)變量則是在堆內(nèi)存;
(3)避免使用finalize,該方法會(huì)給GC增添很大的負(fù)擔(dān);
(4)如果是單線程,盡量使用非多線程安全的,因?yàn)榫€程安全來(lái)自于同步機(jī)制,同步機(jī)制會(huì)降低性能。例如,單線程程序,能使用HashMap,就不要用HashTable。同理,盡量減少使用synchronized
(5)用移位符號(hào)替代乘除號(hào)。eg:a*8應(yīng)該寫作a<<3
(6)對(duì)于經(jīng)常反復(fù)使用的對(duì)象使用緩存;
(7)盡量使用基本類型而不是包裝類型,盡量使用一維數(shù)組而不是二維數(shù)組;
(8)盡量使用final修飾符,final表示不可修改,訪問(wèn)效率高;
(9)單線程情況下(或者是針對(duì)于局部變量),字符串盡量使用StringBuilder,比StringBuffer要快;
(10)String為什么慢?因?yàn)镾tring 是不可變的對(duì)象, 因此在每次對(duì) String 類型進(jìn)行改變的時(shí)候其實(shí)都等同于生成了一個(gè)新的 String 對(duì)象,然后將指針指向新的 String 對(duì)象。如果不能保證線程安全,盡量使用StringBuffer來(lái)連接字符串。這里需要注意的是,StringBuffer的默認(rèn)緩存容量是16個(gè)字符,如果超過(guò)16,apend方法調(diào)用私有的expandCapacity()方法,來(lái)保證足夠的緩存容量。因此,如果可以預(yù)設(shè)StringBuffer的容量,避免append再去擴(kuò)展容量。如果可以保證線程安全,就是用StringBuilder。
?
總結(jié)
以上是生活随笔為你收集整理的Java虚拟机:常见JVM参数配置和GC性能优化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 十大排序算法小结
- 下一篇: Java虚拟机:Java中堆和栈的详细区