JVM 調優(學習篇)

weixin_42073629發表於2020-12-13

概述


公司的江南白衣寫了一篇關鍵業務系統的JVM引數推薦(2016熱冬版)的文章,大牛的文章總是需要細細品讀。這篇文章介紹大量的JVM調優引數,內容也比較多,本文只是列出我自己能理解的一些引數,暫時理解不了的引數就只能等以後自己實力到家了,再慢慢補充上來。

 

效能調優引數


-XX:AutoBoxCacheMax

JAVA程式啟動的時候,會載入rt.jar這個核心包,rt.jar包裡的Integer自然也會被載入到JVM中,Integer裡面有一個IntegerCache快取,如下:

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
 
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;
 
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }
 
        private IntegerCache() {}
}

IntegerCache有一個靜態程式碼塊,JVM在載入Integer這個類時,會優先載入靜態的程式碼。當JVM程式啟動完畢後, -128 ~ +127 範圍的數字會被快取起來,呼叫valueOf方法的時候,如果是這個範圍內的數字,則直接從快取取出。
超過這個範圍的,就只能構造新的Integer物件了。 

public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}

因此可以根據實際情況把AutoBoxCacheMax的值設定的大些,比如江南白衣推薦的 

-XX:AutoBoxCacheMax=20000

-XX:+AlwaysPreTouch

JAVA程式啟動的時候,雖然我們可以為JVM指定合適的記憶體大小,但是這些記憶體作業系統並沒有真正的分配給JVM,而是等JVM訪問這些記憶體的時候,才真正分配,這樣會造成以下問題。
1、GC的時候,新生代的物件要晉升到老年代的時候,需要記憶體,這個時候作業系統才真正分配記憶體,這樣就會加大young gc的停頓時間;
2、可能存在記憶體碎片的問題。

可以在JVM啟動的時候,配置

-XX:+AlwaysPreTouch

引數,這樣JVM就會先訪問所有分配給它的記憶體,讓作業系統把記憶體真正的分配給JVM,後續JVM就可以順暢的訪問記憶體了。

 

GC引數


JAVA 1.7用的垃圾收集演算法還是CMS,下文提到的引數都是針對CMS的。

CMSInitiatingOccupancyFraction

之前寫過一篇java垃圾回收演算法之-CMS(併發標記清除),裡面提到垃圾收集執行緒會跟應用的執行緒一起並行的工作,萬一垃圾收集執行緒在工作的時候,老年代記憶體不足怎麼辦?因此最好還是提前啟動CMS來收集垃圾(CMS GC)。
可以通過設定

CMSInitiatingOccupancyFraction=75

那麼當老年代堆空間的使用率達到75%的時候就開始執行垃圾回收,CMSInitiatingOccupancyFraction預設值是92%,這個就太大了。
CMSInitiatingOccupancyFraction引數必須跟下面兩個引數一起使用才能生效的。 

-XX:+UseConcMarkSweepGC
-XX:+UseCMSInitiatingOccupancyOnly

MaxTenuringThreshold

新生代是使用copy演算法來進行垃圾回收的,可以參看

java垃圾回收演算法之-coping複製

預設情況下,當新生代執行了15次young gc後,如果還有物件存活在Survivor區中,那麼就可以直接將這些物件晉升到老年代,但是由於新生代使用copy演算法,如果Survivor區存活的物件太久的話,Survivor區存活的物件就越多,這個就會影響copy演算法的效能,使得young gc停頓的時間加長,建議設定成6。

-XX:MaxTenuringThreshold=6

ExplicitGCInvokesConcurrent

如果系統使用堆外記憶體,比如用到了Netty的DirectByteBuffer類,那麼當想回收堆外記憶體的時候,需要呼叫

System.gc()

而這個方法將進行full gc,整個應用將會停頓,如果是使用CMS垃圾收集器,那麼可以設定 

-XX:+ExplicitGCInvokesConcurrent

這個引數來改變System.gc()的行為,讓其從full gc --> CMS GC,CMS GC是併發收集的,且中間執行的過程中,只有部分階段需要stop the world。

注意:設定了ExplicitGCInvokesConcurrent,那就不要設定DisableExplicitGC引數來禁掉System.gc()

記憶體引數


-Xmx, -Xms

這兩個一般都是設定4個g

 

NewRatio

GC最多的還是發生在新生代的young gc,所以可以提高一下新生代在整個堆的佔用比例,建議設定為對半分,儘量避免young gc

-XX:NewRatio=1

 

相關文章