经过上两篇垃圾回收的文章我们可以了解到垃圾回收的运作原理。
Java垃圾回收机制 Java垃圾收集器

Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决两个问题 :

  • 给对象分配内存。
  • 回收分配给对象的内存。

那么这篇文章就来了解以下内存分配策略。

对象的内存分配,可以说主要就是在堆上分配内存。

对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。

少数情况下也可能会直接分配在老年代,分配的规则细节会取决于当前使用的垃圾收集器组合,还有JVM中与内存相关的参数配置。

总体来说,内存分配有以下5点策略 :

  • 对象优先在Eden分配
    大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。
  • 大对象直接进入老年代
    所谓的大对象是指需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组。
    经常出现大对象容易导致内存还有不少空间时就提前触发垃圾收集以获取足够的连续空间来“安置”它们。
  • 长期存活的对象将进入老年代
    虚拟机给每个对象定义了一个对象年龄(分代年龄),如果对象在Eden初并经过第一次Minor GC后仍然存活,并能被Survivor 容纳的话,将被移动到 Survivor 空间中,并且对象年龄设置为1。对象在两个Survivor 中每次捯饬都会增加一岁,当它的年龄到了一定程度(默认15岁),就会被晋升到老年代。
  • 动态年龄判定
    如果在Survivor 空间中相同年龄所有对象大小的总和大于Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到15岁。
  • 空间分配担保

在发生Minor GC之前,虚拟机会先检查老年代最大可用连续空间是否大于新生代所有对象的总空间,如果条件成立,那么Minor GC可以确保是安全的。如果不成立,则JVM会查看是否有设置允许担保失败。

如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次Minor GC,尽管这次Minor GC是有风险的;
如果小于或者不允许担保失败,那这是要改为进行一次Full GC(即整个堆都GC,Minor GC + Major GC)。

并且如果出现大量对象在Minor GC存活的对象过多,以至于Survivor 无法容纳这么多对象,那么容纳不下的对象将直接进入老年代。



这里我举一个例子,就拿我们生活中的乘坐电梯来说吧:

java eden内存分配过大_内存分配


1.对象优先在Eden分配

比如我们有载人电梯Eden,A(From Survivor),B(To Survivor)。
另外我们还有货梯Old,货梯载的重量是Eden,A,B加起来的2倍,但是货梯不好清理。



java eden内存分配过大_java_02

这时候Eden客梯坐满人了。并且无法容纳下一位乘客对象。



java eden内存分配过大_内存分配_03

由于Eden客梯空间不足,将会发起一次Minor GC。把所有有关系(可达性分析到GC Roots)的乘客对象都移到A客梯。



java eden内存分配过大_java eden内存分配过大_04

Eden客梯清理完毕之后,运营一段时间之后空间又变小了,并且无法容纳下一位新对象时。



java eden内存分配过大_Java_05

由于Eden客梯空间不足,将会发起一次Minor GC。把所有有关系(可达性分析到GC Roots)的对象都移到B客梯。



java eden内存分配过大_老年代_06


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

此时分配了一个300多斤的对象,那么因为默认规定300斤以上(可以修改此规定)的对象可以直接进入货梯。它就被分配到了货梯。

以为如果大对象如果直接分配在Eden,那么不仅容易产生Minor GC,并且大对象在A,B区间的移动成本比较高。



java eden内存分配过大_java_07


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

就这样对象从Eden移动到A,再从A移动到B,再从B移动到A。
对象初始标记为0,每次移动到A或者B(Survivor)中,标记变+1。
直到有次Minor GC,发现有对象的标记为15,那么这些对象便可以直接进入Old货梯。



java eden内存分配过大_java eden内存分配过大_08


4.动态年龄判定

如果在A或者B客梯中标记的对象大小的总和大于A或B空间的一半,标记大于或等于的对象就可以直接进入Old货梯,无需等到15岁。



5.空间分配担保

空间分配担保就自由想象了,当Old货梯容量也满了的时候,便会Major Gc。

那么什么是Full GC呢,因为有空间分配担保,当Old货梯连续可用容量小于Eden时,便会来一次Minor GC。A或者B容量不下的对象将会移动到Old货梯。

但是如果不允许空间分配担保的话,便直接Full GC。

个人理解Full GC便是Minor GC + Major GC。