天天看点

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保

1.示例采用的垃圾收集器

本篇使用Serial加Serial Old客户端默认收集组合。

对象分配的规则并不是确定的,取决于虚拟机当前使用的是哪一款垃圾收集器。

2.对象优先在Eden分配

相关博客:垃圾回收:标记复制算法

大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。

代码

public class test1 {
    public static void main(String[] args) {
        testAllocation();
    }
    public static void testAllocation(){
        /*
        -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:SurvivorRatio=8
        新生代10M,老年代10M
        新生代Eden区与一个Survivor区的空间比例是8:1,Survivor一个1MB
         */
        byte[] allocation1,allocation2,allocation3,allocation4;
        allocation1=new byte[2 * 1024*1024];
        allocation2=new byte[2 * 1024*1024];
        allocation3=new byte[2 * 1024*1024];
        /*
        Eden已被占用6MB,剩余空间已不足以分匹配allocation4需要的4MB,因此发生Minor GC
        又发现三个2M对象不能全部放入Survivor空间(Survivor只有1MB),所以只好通过分配
        担保机制提前转移到老年代去
         */
        allocation4=new byte[4 * 1024*1024];
    }
}
           

输出

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保

书上的输出:这段主要是看我对这些输出数据的标注

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保

3.大对象直接进入老年代

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保

代码

public class test2 {
    public static void main(String[] args) {
        testPretenureSizeThreshold();
    }
    public static void testPretenureSizeThreshold(){
        /*
        -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728
         */
        byte[] allocation;
        allocation=new byte[4*1024*1024];
    }
}
           

输出

可以看到直接进入了老年代。

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保

4.长期存活的对象进入老年代

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保

代码

public class test3 {
    public static void main(String[] args) {
        testTenuringThreshold();
    }
    public static void testTenuringThreshold(){
        /*
        -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1
         */
        byte[] allocation1,allocation2,allocation3;
        allocation1=new byte[1024*1024/4];
        allocation2=new byte[4*1024*1024];
        allocation3=new byte[4*1024*1024];
        allocation3=null;
        allocation3=new byte[4*1024*1024];
    }
}
           

-XX:MaxTenuringThreshold=1 时输出

from space 1024K, 0% used

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保

-XX:MaxTenuringThreshold=15 时输出

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保

5.动态对象年龄判定

如果在Survivor空间中低于或等于某年龄的所有对象的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到-XX:MaxTenuringThreshold中要求的年龄。

代码

public class test4 {
    /*
    -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution
     */
    public static void main(String[] args) {
        testTenuringThreshold2();
    }
    /*
    alloction1,2都直接进入了老年代,并没等到15岁的临界年龄。
    因为这两个对象加起来已经超过了512KB,并且它们是同年龄的,满足低于或等于某年龄的对象达到Survivor空间一半的规则。
    我们只需要注释掉其中一个对象的new操作,就会发现另外一个就不会晋升到老年代。
     */
    public static void testTenuringThreshold2(){
        byte[] allocation1,allocation2,allocation3,allocation4;
        allocation1=new byte[1024*1024/4];
        allocation2=new byte[1024*1024/4];
        allocation3=new byte[4*1024*1024];
        allocation4=new byte[4*1024*1024];
        allocation4=null;
        allocation4=new byte[4*1024*1024];
    }
}

           

输出

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保

6.空间分配担保

JDK6 Update24之后,-XX:HandlePromotionFailure不会影响虚拟机的空间分配担保策略。

只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小,就会进行Minor GC,否则将进行Full GC。

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保

代码

public class test5 {
    private static final int MB=1024*1024;
    public static void main(String[] args) {
        testHandlePromotion();
    }
    /*
    -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:SurvivorRatio=8
     */
    public static void testHandlePromotion(){
        byte[] allocation1,allocation2,allocation3,allocation4,allocation5,allocation6,allocation7;
        allocation1=new byte[2*MB];
        allocation2=new byte[2*MB];
        allocation3=new byte[2*MB];
        allocation1=null;
        allocation4=new byte[2*MB];
        allocation5=new byte[2*MB];
        allocation6=new byte[2*MB];
        allocation4=null;
        allocation5=null;
        allocation6=null;
        allocation7=new byte[2*MB];
    }
}

           

输出

【深入理解Java虚拟机】第3章:内存分配与回收策略1.示例采用的垃圾收集器2.对象优先在Eden分配3.大对象直接进入老年代4.长期存活的对象进入老年代5.动态对象年龄判定6.空间分配担保