1. 程式人生 > >JVM 觸發Full gc條件

JVM 觸發Full gc條件

除直接呼叫System.gc外,觸發Full GC執行的情況有如下四種。

1. 舊生代空間不足

舊生代空間只有在新生代物件轉入及建立為大物件、大陣列時才會出現不足的現象,當執行Full GC後空間仍然不足,則丟擲如下錯誤:

java.lang.OutOfMemoryError: Java heap space 

為避免以上兩種狀況引起的Full GC,調優時應儘量做到讓物件在Minor GC階段被回收、讓物件在新生代多存活一段時間及不要建立過大的物件及陣列。

2. Permanet Generation空間滿

Permanet Generation中存放的為一些class的資訊等,當系統中要載入的類、反射的類和呼叫的方法較多時,

Permanet Generation可能會被佔滿,在未配置為採用CMS GC的情況下會執行Full GC。如果經過Full GC仍然回收不了,那麼JVM會丟擲如下錯誤資訊:

java.lang.OutOfMemoryError: PermGen space 

為避免Perm Gen佔滿造成Full GC現象,可採用的方法為增大Perm Gen空間或轉為使用CMS GC

3. CMS GC時出現promotion failedconcurrent mode failure

對於採用CMS進行舊生代GC的程式而言,尤其要注意GC日誌中是否有promotion failed

concurrent mode failure兩種狀況,當這兩種狀況出現時可能會觸發Full GC

promotion failed是在進行Minor GC時,survivor space放不下、物件只能放入舊生代,而此時舊生代也放不下造成的;concurrent mode failure是在執行CMS GC的過程中同時有物件要放入舊生代,而此時舊生代空間不足造成的。

應對措施為:增大survivor space、舊生代空間或調低觸發併發GC的比率,但在JDK 5.0+6.0+的版本中有可能會由於JDKbug29導致CMSremark完畢後很久才觸發sweeping動作。對於這種狀況,可通過設定

-XX: CMSMaxAbortablePrecleanTime=5(單位為ms)來避免。

4. 統計得到的Minor GC晉升到舊生代的平均大小大於舊生代的剩餘空間

這是一個較為複雜的觸發情況,Hotspot為了避免由於新生代物件晉升到舊生代導致舊生代空間不足的現象,在進行Minor GC時,做了一個判斷,如果之前統計所得到的Minor GC晉升到舊生代的平均大小大於舊生代的剩餘空間,那麼就直接觸發Full GC

例如程式第一次觸發Minor GC後,有6MB的物件晉升到舊生代,那麼當下一次Minor GC發生時,首先檢查舊生代的剩餘空間是否大於6MB,如果小於6MB,則執行Full GC

當新生代採用PS GC時,方式稍有不同,PS GC是在Minor GC後也會檢查,例如上面的例子中第一次Minor GC後,PS GC會檢查此時舊生代的剩餘空間是否大於6MB,如小於,則觸發對舊生代的回收。

除了以上4種狀況外,對於使用RMI來進行RPC或管理的Sun JDK應用而言,預設情況下會一小時執行一次Full GC。可通過在啟動時通過- java -Dsun.rmi.dgc.client.gcInterval=3600000來設定Full GC執行的間隔時間或通過-XX:+ DisableExplicitGC來禁止RMI呼叫System.gc

*物件分配規則

1.物件優先分配在Eden區,如果Eden區沒有足夠的空間時,虛擬機器執行一次Minor GC

2.大物件直接進入老年代(大物件是指需要大量連續記憶體空間的物件)。這樣做的目的是避免在Eden區和兩個Survivor區之間發生大量的記憶體拷貝(新生代採用複製演算法收集記憶體)。

3.長期存活的物件進入老年代。虛擬機器為每個物件定義了一個年齡計數器,如果物件經過了1Minor GC那麼物件會進入Survivor區,之後每經過一次Minor GC那麼物件的年齡加1,知道達到閥值物件進入老年區。

4.動態判斷物件的年齡。如果Survivor區中相同年齡的所有物件大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的物件可以直接進入老年代。

5.空間分配擔保。每次進行Minor GC時,JVM會計算Survivor區移至老年區的物件的平均大小,如果這個值大於老年區的剩餘值大小則進行一次Full GC,如果小於檢查HandlePromotionFailure設定,如果true則只進行Monitor GC,如果false則進行Full GC。 

1.呼叫System.gc

import java.util.ArrayList;
import java.util.List;

/**
 * 
 * created by: [email protected]
 * created:2016年3月20日
 */

/**
 * -XX:+UseSerialGC -Xms200M -Xmx200M -Xmn32m -XX:SurvivorRatio=8 -XX:+PrintGCDetails
 * @author gxl
 *
 */
public class SimulateFullGc
{
    public static void main(String[] args)
    {
        //模擬fullgc場景
        //場景1 使用System.gc
        List<Object> l = new ArrayList<Object>();
        for (int i =0; i< 100;i++)
        {
            l.add(new byte[1024*1024 ]);
            if (i % 10 ==0)
            {
                System.gc();
            }
        }

    }
}
[Full GC (System) [Tenured: 0K->1495K(172032K), 0.0048354 secs] 2073K->1495K(201536K), [Perm : 2529K->2529K(21248K)], 0.0048900 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System) [Tenured: 1495K->11735K(172032K), 0.0064495 secs] 13310K->11735K(201536K), [Perm : 2532K->2532K(21248K)], 0.0064752 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

由System.gc呼叫的gc標記Full GC (System).

2.老年代空間不足

/**
 * 
 * created by: [email protected]
 * created:2016年3月20日
 */

/**
 * -XX:+UseSerialGC -Xms200M -Xmx200M -Xmn32m -XX:SurvivorRatio=8 -XX:+PrintGCDetails
 * @author gxl
 *
 */
public class SimulateFullGc
{
    public static void main(String[] args)
    {
        //模擬fullgc場景
        //老年代空間不足
        //按照上面的引數推算:老年代大小: 200 -32m = 168M


        byte [] MAXOBJ = new byte [1024 * 1024 * 100]; // 100M

        byte [] MAXOBJ2 = new byte [1024 * 1024 * 70]; // 60M
        MAXOBJ = null;

        byte [] MAXOBJ3 = new byte [1024 * 1024 * 100]; // 60M
    }
}

[GC [DefNew: 5145K->470K(29504K), 0.0029970 secs][Tenured: 106496K->106966K(172032K), 0.0027630 secs] 107545K->106966K(201536K), [Perm : 2528K->2528K(21248K)], 0.0057990 secs] [Times: user=0.00 sys=0.02, real=0.01 secs] 
[Full GC [Tenured: 106966K->106952K(172032K), 0.0024331 secs] 106966K->106952K(201536K), [Perm : 2528K->2527K(21248K)], 0.0024488 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space 
at SimulateFullGc.main(SimulateFullGc.java:27)

3.永久代空間不足

方式 在jdk6 上:

import java.util.ArrayList;
import java.util.List;

/**
 * 
 * created by: [email protected]
 * created:2016年3月20日
 */

/**
 * -XX:+UseSerialGC -Xms200M -Xmx200M -Xmn32m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:MaxPermSize=10M
 * @author gxl
 *
 */
public class SimulateFullGc
{
    public static void main(String[] args)
    {
        //模擬fullgc場景
        //持久代空間不足
        List<String> list = new ArrayList<String>();
        int i = 0;
        while (true)
        {
            list.add(String.valueOf("ABCD:"  + i ++).intern());
        }
    }

[GC [DefNew: 26240K->937K(29504K), 0.0040883 secs] 26240K->937K(201536K), 0.0041121 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[Full GC [Tenured: 0K->1325K(172032K), 0.0362063 secs] 17898K->1325K(201536K), [Perm : 20479K->20479K(20480K)], 0.0362549 secs] [Times: user=0.03 sys=0.00, real=0.04 secs] 
[Full GC [Tenured: 1325K->1325K(172032K), 0.0326822 secs] 1325K->1325K(201536K), [Perm : 20479K->20479K(20480K)], 0.0327085 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] 
[Full GC [Tenured: 1325K->1325K(172032K), 0.0128924 secs] 1821K->1325K(201536K), [Perm : 20479K->3734K(20480K)], 0.0129210 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Exception in thread “main” java.lang.OutOfMemoryError: PermGen space 
at java.lang.String.intern(Native Method) 
at SimulateFullGc.main(SimulateFullGc.java:25)

String.intern 會拷貝例項到永久代中.

在jdk1.7 不會,所以可以載入class來模擬:

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 
 * created by: [email protected]
 * created:2016年3月20日
 */

/**
 * -XX:+UseSerialGC -Xms200M -Xmx200M -Xmn32m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:MaxPermSize=10M
 * @author gxl
 *
 */
public class SimulateFullGc
{
    public static void main(String[] args)
    {
        //模擬fullgc場景
        //持久代空間不足
        //class 載入資訊
        //需要cglib + asm (http://forge.ow2.org/projects/asm/)
        while (true)
        {
            Enhancer en = new Enhancer();
            en.setSuperclass(OOMObject.class);
            en.setUseCache(false);
            en.setCallback(new MethodInterceptor()
            {

                @Override
                public Object intercept(Object arg0, Method arg1, Object[] arg2,
                        MethodProxy arg3) throws Throwable
                {
                    // TODO Auto-generated method stub
                    return null;
                }
            });
            en.create();
        }
    }
    static class OOMObject
    {

    }
}

[GC [DefNew: 28043K->1735K(29504K), 0.0039443 secs] 33739K->7817K(201536K), 0.0039660 secs] [Times: user=0.03 sys=0.00, real=0.00 secs] 
[Full GC [Tenured: 6082K->7989K(172032K), 0.0322856 secs] 23097K->7989K(201536K), [Perm : 20479K->20479K(20480K)], 0.0323121 secs] [Times: user=0.05 sys=0.00, real=0.03 secs] 
[Full GC [Tenured: 7989K->7989K(172032K), 0.0233015 secs] 7989K->7989K(201536K), [Perm : 20479K->20479K(20480K)], 0.0233266 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
[Full GC [Tenured: 7989K->7989K(172032K), 0.0199921 secs] 8515K->7989K(201536K), [Perm : 20479K->20479K(20480K)], 0.0200187 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
[Full GC [Tenured: 7989K->3354K(172032K), 0.0250219 secs] 7989K->3354K(201536K), [Perm : 20479K->20477K(20480K)], 0.0250530 secs] [Times: user=0.02 sys=0.00, real=0.03 secs] 
Exception in thread “main” [Full GC [Tenured: 3354K->3355K(172032K), 0.0198650 secs] 3880K->3355K(201536K), [Perm : 20479K->20479K(20480K)], 0.0198919 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
[Full GC [Tenured: 3355K->3355K(172032K), 0.0198493 secs] 3355K->3355K(201536K), [Perm : 20479K->20479K(20480K)], 0.0198762 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
[Full GC [Tenured: 3355K->3355K(172032K), 0.0197512 secs] 3880K->3355K(201536K), [Perm : 20479K->20479K(20480K)], 0.0197814 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
[Full GC [Tenured: 3355K->3285K(172032K), 0.0245018 secs] 3355K->3285K(201536K), [Perm : 20479K->20478K(20480K)], 0.0245283 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]

4. gc 擔保失敗

空間分配擔保失敗. 
在發生MinorGC前,檢查老年代是否有連續空間,如果有,則執行,如果沒有,根據設定:-XX:-HandlePromotionFailure 指定,如果開啟,那麼繼續檢查,當前老年代最大可用連續空間大於平均歷次晉升到老年代大小,如果大於,則進行MinorGC,否則進行FullGC,如果HandlePromotionFailure 不設定 直接進行FullGC. 
大致就是這樣: 
flow

程式碼: 
該引數:-XX:-HandlePromotionFailure 在JDK 6U24中移除.後續判斷只要剩餘連續大於當前新生代或者歷次晉升平均大小就會執行minorgc.

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option HandlePromotionFailure; support was removed in 6.0_24

搞了好久終於搞出來了:
import java.lang.reflect.Field;

import sun.misc.Unsafe;

/**
 * 
 * created by: [email protected]
 * created:2016年3月20日
 */

/**
 * -Xms20M -Xmx20M -Xmn10m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:-HandlePromotionFailure -XX:MaxTenuringThreshold=1
 * @author gxl
 *
 */
public class SimulateFullGc
{
    private static final int MB = 1024 * 1024;

    public static void main(String[] args) throws Exception
    {
        // 模擬fullgc場景
        // 提升擔保
        // 提升擔保
        byte[] M6, M3, M4, M5, M7, M8;
        M6 = new byte[6 * MB];
        M6[0] = 0;
        M6[0] = 0;
        // 使用2次保證下次需要的時候可以晉升到老年代 會晉升那麼 晉升經驗值為6M
        M3 = new byte[4 * MB];
        M4 = new byte[2 * MB];
        M4 = null;
        M5 = new byte[2 * MB];
        M5[0] = 0;
        M5[0] = 0;
        M7 = new byte[2 * MB];
        M7[0] = 0;
        M7[0] = 0;
        M7 = null;
        M8 = new byte[3 * MB];
        // 最終如下物件 老年代 M6 + M8 = 9M
        // 年輕代:M3 + M5 = 6M = 6144K
        System.out.println("M6 HEX:0x" + Long.toHexString(addressOf(M6)));
        System.out.println("M5 HEX:0x" + Long.toHexString(addressOf(M5)));
        System.out.println("M3 HEX:0x" + Long.toHexString(addressOf(M3)));
        System.out.println("M8 HEX:0x" + Long.toHexString(addressOf(M8)));

    }

    private static Unsafe unsafe;

    static
    {
        try
        {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public static long addressOf(Object o) throws Exception
    {
        Object[] array = new Object[] { o };

        long baseOffset = unsafe.arrayBaseOffset(Object[].class);
        int addressSize = unsafe.addressSize();
        long objectAddress;
        switch (addressSize)
        {
        case 4:
            objectAddress = unsafe.getInt(array, baseOffset);
            break;
        case 8:
            objectAddress = unsafe.getLong(array, baseOffset);
            break;
        default:
            throw new Error("unsupported address size: " + addressSize);
        }

        return (objectAddress);
    }




}

[GC [DefNew: 6487K->149K(9216K), 0.0027691 secs] 6487K->6293K(19456K), 0.0027839 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [DefNew: 6379K->6379K(9216K), 0.0000060 secs][Tenured: 6144K->6144K(10240K), 0.0048112 secs] 12523K->10389K(19456K), [Perm : 374K->374K(12288K)], 0.0048426 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC [Tenured: 8192K->6144K(10240K), 0.0032886 secs] 14485K->12437K(19456K), [Perm : 374K->374K(12288K)], 0.0033058 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
M6 HEX:0x33990000 
M5 HEX:0x333b5520 
M3 HEX:0x32f90000 
M8 HEX:0x33f90010 
Heap 
def new generation total 9216K, used 6514K [0x32f90000, 0x33990000, 0x33990000) 
eden space 8192K, 79% used [0x32f90000, 0x335ec808, 0x33790000) 
from space 1024K, 0% used [0x33890000, 0x33890000, 0x33990000) 
to space 1024K, 0% used [0x33790000, 0x33790000, 0x33890000) 
tenured generation total 10240K, used 9216K [0x33990000, 0x34390000, 0x34390000) 
the space 10240K, 90% used [0x33990000, 0x34290020, 0x34290200, 0x34390000) 
compacting perm gen total 12288K, used 374K [0x34390000, 0x34f90000, 0x38390000) 
the space 12288K, 3% used [0x34390000, 0x343ed960, 0x343eda00, 0x34f90000) 
ro space 10240K, 54% used [0x38390000, 0x3890c510, 0x3890c600, 0x38d90000) 
rw space 12288K, 55% used [0x38d90000, 0x3942fb78, 0x3942fc00, 0x39990000)

注意物件位置.

5. Cocurrent mode failure

發生在cms的清理sweep階段,發現有新的垃圾產生,而且老年代沒有足夠空間導致的.
關於cms:
初始標記(STW) - >併發標記 ->重新標記(STW) ->併發清除.
STW = stop the world.