内存分配与回收策略

Minor GC和Full GC

Minor GC:回收新生代,因为新生代对象存活时间短,因此Minor会频繁执行,执行速度较快。

Full GC:回收老年代和新生代,老年代存活时间长,因此Full GC较少执行,且执行速度较慢。

内存分配策略

  1. 对象优先在 Eden 分配
    大多数情况下,对象在新生代 Eden 上分配,当 Eden 空间不够时,发起 Minor GC。
  2. 大对象直接进入老年代
    大对象是指需要连续内存空间的对象,典型的大对象是那种很长的字符串以及数组。经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。
  3. 长期存活的对象进入老年代
    为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁, 增加到一定年龄则移动到老年代中。
    -XX:MaxTenuringThreshold 用来定义年龄的阈值。
  4. 动态对象年龄判定
    虚拟机并不是永远要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄 所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。
  5. 空间分配担保
    如果Eden空间不够的话,可以使用老年代的空间。在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的 话,那么 Minor GC 可以确认是安全的。

Full GC 的触发条件

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

  1. 调用 System.gc()
    只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存
  2. 老年代空间不足
    老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。
    为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 -Xmn 虚拟机参数 调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对 象进入老年代的年龄,让对象在新生代多存活一段时间。
  3. 空间分配担保失败
    使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC。具体内容请参考上面 的第 5 小节。
  4. JDK 1.7 及以前的永久代空间不足
    当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也 会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。 为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。
  5. Concurrent Mode Failure 执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时 性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。