内存分配规则

对象优先在Eden分配

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

package com.wjz.demo;

public class HeapOOM {
    static class OOMObject {}
    /**
     * 使用 Serial / Serial Old 组合收集器
     * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
     * @param args
     */
    private static final int _1MB = 1024*1024;
    public static void main(String[] args) {
        byte[] b1, b2, b3, b4;
        b1 = new byte[2*_1MB];
        b2 = new byte[2*_1MB];
        b3 = new byte[2*_1MB];
        b4 = new byte[4*_1MB]; // 此处会出现一次Minor GC
    }
}

运行结果

[GC [DefNew: 6993K->554K(9216K), 0.0212177 secs] 6993K->6698K(19456K), 0.0318550 secs] [Times: user=0.00 sys=0.02, real=0.03 secs] 
Heap
 def new generation   total 9216K, used 4978K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  56% used [0x00000000f9a00000, 0x00000000f9e51fa0, 0x00000000fa200000)
  from space 1024K,  54% used [0x00000000fa300000, 0x00000000fa38a8b0, 0x00000000fa400000)
  to   space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
 tenured generation   total 10240K, used 6144K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,  60% used [0x00000000fa400000, 0x00000000faa00030, 0x00000000faa00200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2556K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  12% used [0x00000000fae00000, 0x00000000fb07f258, 0x00000000fb07f400, 0x00000000fc2c0000)
No shared spaces configured.

  eden space 8192K:新生代Eden区

  from space 1024K:新生代from survivor区

  to space 1024K:新生代to survivor区

  the space 10240K:老年代区

结果分析

  新生代内存占用量由6993K降到554K,总内存占用量由6993K升到6998K几乎没有变化。3个2M对象分配内存时放入了新生代Eden区,此时Eden区还剩2M左右的内存空间, 又来了一个4M对象想放进Eden区当时显然不可能,触发了一次Minor GC,复制算法开始将Eden区的对象复制到from survivor区,但是from survivor区只有1M空间,所以 只能将这3个2M对象通过分配担保转移到老年代中。4M对象被分配到了Eden区。

大对象直接进入老年代

  大对象是指需要大量连续空间的对象如字符串和数组。

package com.wjz.demo;

public class HeapOOM {
    static class OOMObject {}
    /**
     * 使用 Serial / Serial Old 收集器
     * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC 
     * -XX:PretenureSizeThreshold=3145728(仅Serial、PreNew有效)
     * @param args
     */
    private static final int _1MB = 1024*1024;
    public static void main(String[] args) {
        byte[] b1;
        b1 = new byte[4*_1MB];
    }
}

运行结果

Heap
 def new generation   total 9216K, used 1013K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  12% used [0x00000000f9a00000, 0x00000000f9afd518, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 4096K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,  40% used [0x00000000fa400000, 0x00000000fa800010, 0x00000000fa800200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2553K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  12% used [0x00000000fae00000, 0x00000000fb07e5d8, 0x00000000fb07e600, 0x00000000fc2c0000)
No shared spaces configured.

结果分析

  新生代Eden和survivor区几乎没有使用,老年区占用了40%,因为大于3M的对象直接分配到了老年代中。

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

  设置-XX:MaxTenuringThreshold参数设置对象进入老年代的年龄阈值。对象在Eden区中经过一次GC的对象年龄增加1,对象成功转移到Survivor中每熬过“”一次GC,年龄就增加1。  

package com.wjz.demo;

public class LongLifeObject2OldSpace {/**
     * 使用 Serial / Serial Old 收集器
     * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC 
     * -XX:MaxTenuringThreshold=1 -XX:+PrintTenuringDistribution
     * @param args
     */
    private static final int _1MB = 1024*1024;
    public static void main(String[] args) {
        byte[] b1, b2, b3, b4;
        b1 = new byte[_1MB/4];
        b2 = new byte[2*_1MB];
        b3 = new byte[6*_1MB];
        b3 = null;
        b4 = new byte[4*_1MB];
    }
}

运行结果

[GC [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     829632 bytes,     829632 total
: 3153K->810K(9216K), 0.0043913 secs] 3153K->2858K(19456K), 0.0044574 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:        256 bytes,        256 total
: 7282K->0K(9216K), 0.0013956 secs] 9330K->2858K(19456K), 0.0014221 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4260K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  52% used [0x00000000f9a00000, 0x00000000f9e28fd0, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200100, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 2857K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,  27% used [0x00000000fa400000, 0x00000000fa6ca7c0, 0x00000000fa6ca800, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2556K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  12% used [0x00000000fae00000, 0x00000000fb07f268, 0x00000000fb07f400, 0x00000000fc2c0000)
No shared spaces configured.

 结果分析

  先是b1,b2进入Eden区中,b3处发生GC,b1进入Survivor区,b2无法进入只能是进入老年区,b3进入Eden区,b4进入Eden区时再次发生GC,b1达到老年年龄进入老年区,b3设置为了null被垃圾回收,b4进入Eden区。

动态对象年龄判断

  虚拟机并不是永远的要求对象年龄必须达到MaxTenuringThreshold年龄阈值才能进入老年代,如果Survivor空间中相同年龄所有对象的大小总和大于Survivor空间的一半时,年龄大于或等于该年龄的对象将进入老年代。  

package com.wjz.demo;

public class AgeObject {/**
     * 使用 Serial / Serial Old 收集器
     * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC 
     * -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution
     * @param args
     */
    private static final int _1MB = 1024*1024;
    public static void main(String[] args) {
        byte[] b1, b2, b3, b4;
        b1 = new byte[_1MB/4];
        b2 = new byte[_1MB/4];
        b3 = new byte[4*_1MB]; 
        b4 = new byte[4*_1MB];
        b4 = null;
        b4 = new byte[4*_1MB];
    }
}

运行结果

[GC [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age   1:    1048576 bytes,    1048576 total
: 5457K->1024K(9216K), 0.0045888 secs] 5457K->5162K(19456K), 0.0046869 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC [DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age   1:        120 bytes,        120 total
: 5283K->0K(9216K), 0.0016055 secs] 9422K->5162K(19456K), 0.0016282 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4424K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  54% used [0x00000000f9a00000, 0x00000000f9e51f90, 0x00000000fa200000)
  from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200078, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 5162K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,  50% used [0x00000000fa400000, 0x00000000fa90a8e0, 0x00000000fa90aa00, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2556K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  12% used [0x00000000fae00000, 0x00000000fb07f260, 0x00000000fb07f400, 0x00000000fc2c0000)
No shared spaces configured.

结果分析

  Survivor空间被清空,老年区比预期增加了,说明b1,b2没有存活在Survivor区而是进入了老年区。

空间分配担保

  jdk6之后的版本分配担保规则为只要老年代的连续空间大于新生代对象总大小或历次晋升老年代对象的平均大小就进行Minor GC,否则进行Full GC。

 

posted @ 2017-10-05 08:26  BINGJJFLY  阅读(405)  评论(0编辑  收藏  举报