JVM内存分配与回收策略

JVM内存分配与回收策略


新生代划分为三个部分:分别为Eden、Survivor from、Survivor to,大小比例为8:1:1(为了防止复制收集算法的浪费内存过大)。每次只使用Eden和其中的一块Survivor,回收时将存活的对象复制到另一块Survivor中,这样就只有10%的内存被浪费,但是如果存活的对象总大小超过了Survivor的大小,那么就把多出的对象放入老年代中。

内存分配策略

  1. 对象优先在 Eden 分配
    大多数情况下,对象在新生代 Eden 区分配,当 Eden 区空间不够时,发起 MinorGC。

  2. 大对象直接进入老年代
    大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。
    经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。 -XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配,避免在 Eden区和 Survivor 区之间的大量内存复制。

  3. 长期存活的对象进入老年代
    为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁,增加到一定年龄则移动到老年代中。
    -XX:MaxTenuringThreshold 用来定义年龄的阈值。

  4. 动态对象年龄判定
    虚拟机并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。

  5. 空间分配担保
    在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。
    如果不成立的话虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败, 如果允许就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 设置不允许冒险,就要进行一次 Full GC。

Minor GC 和 Full GC

Minor GC:发生在新生代上,因为新生代对象存活时间很短,因此 Minor GC会频繁执行,执行的速度一般也会比较快。

Full GC:发生在老年代上,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。

Full GC 的触发条件

对于 Minor GC,其触发条件非常简单,当 Eden 空间满时,就将触发一次 MinoGC。而 Full GC 则相对复杂,有以下条件:

  1. 调用 System.gc()
    只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,应该让虚拟机自己管理内存。

  2. 老年代空间不足
    老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。
    为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 -Xmn 虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。

  3. 空间分配担保失败
    使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC。

  4. Concurrent Mode Failure
    执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。

posted @ 2021-03-31 12:50  当康  阅读(113)  评论(0编辑  收藏  举报