Jvm的内存分配与回收策略

Java技术体系的自动内存管理,最根本的目标是自动化地解决两个问题:自动给对象分配内存和自动回收分配给对象的内存。下面就会介绍一下关于分配内存的一些原则等等。(后续会补充相关参数的使用和堆的情况)

对象优先在Eden区分配

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

大对象直接进入

大对象就是指需要大量连续内存空间的Java对象,最典型的大对象便是那种很长的字符串,或者元素数量很庞大的数组!

之所以大对象要在老年代中分配是因为大对象如果在新生代分配空间时,它容易导致内存明明还有不少空间时提前触发垃圾收集,以获取足够的空间来安置他们,新生代使用复制算法,当复制时候大对象也意味着高额的内存复制开销。HotSpot虚拟机提供了-XX:PretenureSizeThreshold参数,指定大于该设置值的对象直接在老年代分配,这样做的目的就是避免在Eden区及两个Survivor区之间来回复制,产生大量的内存复制操作。

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

我们之前在介绍GC算法标记-复制的时候,在新生代长期存活的对象,就会进入老年代。为做到一点,虚拟机给每个对象定义了一个对象年龄(Age)计数器,存储在对象头。在新生代第一次被收集的时候,对象会从Eden区复制到Survivor区,后面会继续在两个Survivor区里面来回复制。每在Survivor区里面熬过一次收集,Age就会加1。当Age达到默认的15的时候,就会晋升到老年代!

对象晋升老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。

动态对象年龄判断

为了适应不同内存状况,虚拟机不要求对象年龄达到阈值才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 的一半,年龄不小于该年龄的对象就可以直接进入老年代。

空间分配担保

我们的空间分配担保是指在发生Minor GC之前,虚拟机必须先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么这一次Minor GC可以确保是安全的!

如果条件不成立,虚拟机会查看 -XX:HandlePromotionFailure 参数设置的是否允许担保失败,如果允许会继续检查老年代最大可用连续空间是否大于历次晋升老年代对象的平均大小,如果满足将冒险尝试一次 Minor GC,否则改成一次 FullGC。

为什么需要空间分配担保呢?

这是因为要考虑到我们的新生代是使用一个Survivor空间来作为轮换备份,在经历多次Minor GC后,存在极端情况就是内存回收后新生代中所有对象都存活。我们是需要保证老年代有足够大的空间来容纳这些Survivor区无法容纳的存活的对象,如果无法容纳就会触发Full GC!而设置允许冒险其实就是一种赌概率的方式,赌老年代可以容纳!

参考资料

深入理解Java虚拟机(第三版)

你可能感兴趣的:(Jvm的内存分配与回收策略)