《深入理解java虚拟机v3》对象优先在Eden分配 > 代码单3-7 新生代Minor GC

对象优先在Eden分配

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

HotSpot虚拟机提供了-XX:+PrintGCDetails这个收集器日志参数,告诉虚拟机在发生垃圾收集行为时打印内存回收日志,并且在进程退出的时候输出当前的内存各区域分配情况。

例子

在代码清单3-7的testAllocation()方法中,尝试分配三个2MB大小和一个4MB大小的对象,在运行时通过-Xms20M、-Xmx20M、-Xmn10M这三个参数限制了Java堆大小为20MB,不可扩展,其中
10MB分配给新生代,剩下的10MB分配给老年代。-XX:Survivor-Ratio=8决定了新生代中Eden区与一个Survivor区的空间比例是8∶1,从输出的结果也清晰地看到“eden space 8192K(8M)、from space 1024K(1M)、tospace 1024K(1M)”的信息,新生代总可用空间为9216KB(9M)(Eden区+1个Survivor区的总容量)。

新生代总大小为10M,2个Survivor各为1M,Eden区8M,那么可用的新生代空间=1个Survivor+Eden区=9M,另一个Survivor为不可用区(用来当粘贴板了)

/**
 * VM参数:-XX:+UseSerialGC
 * @author zzm
 */
public class TestSerialGCAllocation {

    private static final int _1MB = 1024 * 1024;

    /**
     * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
     */
    public static void testAllocation() {
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation4 = new byte[4 * _1MB];  // 出现一次Minor GC
    }
    
   public static void main(String[] args) {
		testAllocation();
	}

注:
-Xms20M 设置初始内存
-Xmx20M 设置最大内存
-Xmn10M 设置新生代内存
-verbose:gc 控制台输出精简的GC情况,是 -XX:+PrintGC的别名,意义不大,当日志详细参数时,会被覆盖。
-XX:+PrintGCDetails 在控制台输出详细的
-XX:+UseSerialGC 使用serial gc收集器
-XX:SurvivorRatio=8 设置新生代中Eden区与一个Survivor区的空间,由于Survivor实际存在2个,因此最终是8:1:1

JDK1.6执行

JDK1.6执行结果,与书上一致:

[GC [DefNew: 6651K->148K(9216K), 0.0061143 secs] 6651K->6292K(19456K), 0.0061421 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 def new generation   total 9216K, used 4490K [0x03810000, 0x04210000, 0x04210000)
  eden space 8192K,  53% used [0x03810000, 0x03c4d7f8, 0x04010000)
  from space 1024K,  14% used [0x04110000, 0x04135348, 0x04210000)
  to   space 1024K,   0% used [0x04010000, 0x04010000, 0x04110000)
 tenured generation   total 10240K, used 6144K [0x04210000, 0x04c10000, 0x04c10000)
   the space 10240K,  60% used [0x04210000, 0x04810030, 0x04810200, 0x04c10000)
 compacting perm gen  total 12288K, used 2103K [0x04c10000, 0x05810000, 0x08c10000)
   the space 12288K,  17% used [0x04c10000, 0x04e1dec0, 0x04e1e000, 0x05810000)
No shared spaces configured.

其含义大概如下:

 [GC       表示Young GC
 [DefNew:  单线程Serial年轻代GC
 6651K->   年轻代垃圾回收前的大小   
 148K      年轻代回收后的大小
 (9216K),  年轻代可用空间总大小  9M
 0.0061143 secs]   回收花费的时间
  6651K->   整个堆回收前的大小
  6292K     整个堆回收后的大小  几乎未回收到什么内存
  (19456K),  堆可用空间总大小  19M
   0.0061421 secs] 
   [Times: user=0.00   用户耗时
   sys=0.00,           系统耗时
   real=0.01 secs]     实际耗时

分析原因:
执行testAllocation()中分配allocation4对象的语句时会发生一次Minor GC,这次回收的结果是新生代6651KB变为148KB,而总内存占用量则几乎没有减少(因为allocation1、2、3三个对象都是存活
的,虚拟机几乎没有找到可回收的对象)。

产生这次垃圾收集的原因是为allocation4分配内存时,发现il
Eden已经被占用了(6MB+148K),剩余空间已不足以分配allocation4所需的4MB内存,因此发生Minor GC。垃圾收集期间虚拟机又发现已有的三个2MB大小的对象全部无法放入Survivor空间(Survivor空间只有1MB大小),所以只好通过分配担保机制提前转移到老年代去。

这次收集结束后,4MB的allocation4对象顺利分配在Eden中。因此程序执行完的结果是Eden占用4MB(被allocation4占用),Survivor空闲,老年代被占用6MB(被allocation1、2、3占用)。通过GC日志可以证实这一点。

148K是什么内存?是我反推的,gc后Survivor遗留148K

Heap
 def new generation   //新生代信息
 total 9216K,         //总可用大小
 used 4490K           //已占用大小
 [0x03810000, 0x04210000, 0x04210000)
  eden space 8192K,   //eden 区使用量 
   53% used           //使用率
  [0x03810000, 0x03c4d7f8, 0x04010000)
  from space 1024K,  14% used  //其中一个Survivor区
  [0x04110000, 0x04135348, 0x04210000)
  to   space 1024K,   0% used  //另一个Survivor区
  [0x04010000, 0x04010000, 0x04110000)
 tenured generation    //老年代信息
   total 10240K,       //总可用大小10M
   used 6144K          //已占用的大小 6M,刚好是allocation1、2、3三个对象
   [0x04210000, 0x04c10000, 0x04c10000)
   the space 10240K,   //总大小10M
   60% used            //使用率60%=6M/10M
   [0x04210000, 0x04810030, 0x04810200, 0x04c10000)
compacting perm gen   //永久代,可以理解成方法区,非堆内存
 total 12288K, used 2103K [0x04b60000, 0x05760000, 0x08b60000)
   the space 12288K,  17% used [0x04b60000, 0x04d6dec0, 0x04d6e000, 0x05760000)

注意:需要执行2次,才能稳定的结果和书上一致,否则为下面,不知道具体原因:

[GC [DefNew: 6487K->147K(9216K), 0.0136419 secs] 6487K->6291K(19456K), 0.0136898 secs] [Times: user=0.00 sys=0.02, real=0.01 secs] 

JDK1.7执行

JDK1.7执行结果:

[GC[DefNew: 7128K->445K(9216K), 0.0068331 secs] 7128K->6589K(19456K), 0.0068977 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 

可以看出初始内存7128K占用变多了一点

参考:
【GC分析】Java GC日志查看
GC日志详情分析

Java GC日志查看,GC日志时间分析 *

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值