深入理解java虚拟机之自动内存管理机制(四)
内存分配与回收策略
(一)内存分配策略
给谁分配?分配到哪?是内存分配策略必须解答的问题。
java对象是分配的对象,往大方向来说,是分配到堆中,更细一点说,根据对象不同的特点分配到新生代和老年代区域。如果启动了本地线程分配缓冲,就按线程优先在TLAB上分配。
一、新生代区域的分配
新创建出来的对象优先分配到新生代中的Eden区域。当Eden区域没有足够的空间时,会发生一次minor gc。当新生代总可用空间(Eden区+1个Survivor区的总容量)仍然
不够的时候,就通过空间分配担保机制分到老年区。
-Minor GC:发生在新生代的垃圾收集动作,非常频繁,回收速度快。
-Major GC / Full GC: 发生在老年代的垃圾收集动作,经常会伴随至少一次的Minor GC。 速度一般比Minor GC慢10倍以上。
二、老年代区域的分配
进入老年代区域的对象有三种。
1. 大对象直接进入。所谓大对象,是指需要大量连续内存空间的java对象。虚拟机提供了一个 -XX:PretenureSizeThreshold参数,令空间大于这个设置值的对象直接在老年代
分配。这样做的目的是避免在Eden区及两个Survivor之间发生大量的内存复制。
2. 长期存活的对象。老年代,顾名思义,里面应该存放着“老”对象。如何判断一个对象老不老呢?虚拟机给每个对象定义了一个对象年龄计数器。如果对象出生在Eden并经过
第一次Minor GC后任然能够存活,并且能够被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。在Survivor中的对象每熬过一次Minor GC,年龄就增加
1岁。当对象的年龄增加到一定程度(默认15岁),就会晋升到老年代中。这个年龄阈值可以通过-XX:MaxTenuringThreshold设置。
3. 动态对象年龄判定下的对象。如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,那么年龄>=该年龄的对象将直接进入老年代。
三、空间分配担保
在发生Minor GC之前,虚拟机先检查老年代最大可用连续空间是否大于新生代所有对象的总和,是,那Minor GC就是安全的;否,虚拟机就查看HandlePromotionFailure设置
值是否允许担保失败。允许,就继续查看老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,成立,就尝试进行Minor GC;不成立,或者HandlePromotionFailure
不允许,就要改为进行一次Full GC。