Java虚拟机——堆内存的分配

Java堆内存分配机制

Java堆内存的分配整体可以概述为“自适应的,分代的,停止-复制,标记-清除”式的垃圾回收器。

     分代指Java将堆内存划分为年轻代(Young Generation),年老代(Old Generation),永久代(Permannet Generation)三块大区域。新生代又再分为Eden区和Survivor区两部分,本文根据java堆的划分,描述hotspot的内存分配策略。

Java堆内存分配分配规则

  1. 对象主要分配在新⽣代的 Eden 区上
  2. 如果启动了本地线程分配缓冲,将按线程优先在 TLAB 上分配
  3. 少数情况下也可能会直接分配在⽼年代中

 年轻代:

  • 对象在被创建时,内存分配首先发生在年轻代(有一部分大对象可以直接被创建在年老代)。大部分对象在被创建后很快不再被使用。于是会被年轻代的GC机制清理掉,叫做 Minor GC 或者 Young GC。
  • 年轻代还可以划分为三个区域:Eden区,Survivor0(也可以理解为From Survivor),Survivor1(也可以理解为To Survivor)
  • 年轻代使用“停止-复制”清理方法,具体步骤如下:
  1. 对象被创建在Eden区。
  2. 当Eden区满时,则触发一次 Minor GC。会清理消亡的对象,并将存活的对象复制到 Survivor0 区。(此时 Survivor1区是空的)
  3. 在Eden区执行几次GC后,Survivor0 区也会变满。此时对Survivor0 区进行GC清理动作,然后将Eden区本次未消亡的对象和 Survivor0 区存活的对象一起复制到 Survivor1 区。(此后一段时间内 Survivor0区是空的)
  4. 当两个 Survivor 区相互切换固定次数(可以理解为互换角色)后(HotSpot虚拟机默认是15次,可手动更改【对象的复制次数达到-XX:MaxTenuringThreshold设置的值(默认-XX:MaxTenuringThreshold=15)时,将被移至老年代】)。将仍然存活的对象复制到年老代。此时仍存货的对象很少,比如我们自定义的类。

   黄色区域堆满了整个Eden,说明需要 Minor GC, Minor GC结束后,Eden将会清空,颜色由原来的黄色变为白色(因为垃圾回收清除了大部分对象),将Eden存活对象复制到 Survivor0 区(也就是from Survivor0区)

 在Eden区执行几次GC后,Survivor0 区也会变满。此时对Survivor0 区进行GC清理动作,然后将Eden区本次未消亡的对象和 Survivor0 区存活的对象一起复制到 Survivor1 区(也就是第二张图片的蓝色区域)

   当对象的复制次数达到-XX:MaxTenuringThreshold设置的值(默认-XX:MaxTenuringThreshold=15)时,将通过复制算法被移至老年代(绿色的区域)。 这个区域包括eden和两个Survivor的存活对象。还有一点要注意因为两个Survivor相互切换,意思是from Survivor为空,to Survivor区域有从eden过来的存活对象,那么下一个Minor GC之前就会把eden和to Survivor区域存活对象复制到from Survivor;反之from Survivor有从eden过来的存活对象,to Survivor区域为空,那么下一个Minor GC之前就会把eden和from Survivor区域存活对象复制到to Survivor,直到对象的复制次数达到-XX:MaxTenuringThreshold设置的值,将通过复制算法被移至老年代。所以不要认为只有to Survivor的存活对象到老年代,from Survivor也可以。

老年代: 

  • 在年老代满了的时候,会触发GC机制。被称为 Major GC 或 Full GC。年老代使用的GC方式为“标记-清理”。
  • 当创建较大的对象时,如长字符串,大数组。Young空间不足,则会直接分配至年老代。大对象会提前出发GC动作。所以应尽少使用。更应该避免使用短命的大对象。
  • 会存在年老代引用新生代对象的情况。此时如果 Young GC时,可能要查询年老代,已确定是否可以回收,这十分低效。所以在年老代中会维护一个 512byte 的“ card table ”,存储多有对年轻代的引用记录。只需要查询此处即可。

永久代

永久代经常只会发生两种清理动作:

常量池中的常量。此时只需要确定没有该常量存活的引用即可。
无用的类信息,此时需要确定以下三点,才可以回收。 
   (1.):类的所有实例已经被回收; 
   (2.):类的ClassLoader被回收; 
   (3.):没有通过反射引用该类的地方;

 堆的内存模型

 默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。
老年代 ( Old ) = 2/3 的堆空间大小。其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。
默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。
因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。

新⽣代 GC (Minor GC):指发⽣在新⽣代的垃圾收集动作,因为 Java 对象⼤多都具备朝⽣ 夕灭的特性,所以 Minor GC ⾮常频繁,⼀般回收速度快。

⽼年代 GC (Major GC/ Full GC):指 发⽣在⽼年代的 GC,出现了 Major GC,经常会伴随⾄少⼀次的 Minor GC(但⾮绝对的, 在 Parallel Scavenge 收集器的收集策略⾥就有直接进⾏ Major GC 的策略选择过程)。 Major GC 的速度⼀般会⽐ Minor GC 10 倍以上。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值