java垃圾回收之各种GC

新生代

回收算法

通过复制算法实现垃圾的回收,将标记被使用的对象复制到一个未存放对象的空间,然后清空当前空间中的剩余对象。因此复制清除算法需要一块单独的空闲空间用于对象的复制。

可用GC

  • 串行GC
  • parallel scavenge(PS):并行回收
    • 采用复制算法
    • 扫描和复制都是多线程执行
    • 根据新生代回收情况,动态调整eden、s0、s1的大小
    • 适合于多核CPU的机器,server级别默认采用的回收方式(cpu核数超过2、内存大小超过2G)
  • parnew:并行
    • 跟上面的PS都是采用多线程的方式对新生代进行回收
    • 跟上面的PS的区别在于必须配合老年代的CMS GC使用,因为CMS进行垃圾回收的时候,有写过程需要并发执行,如果此时发生新生代回收(对象需要移动到老年代),需要新生代做一些特殊的处理,但是上面的PS新生代回收没有这部分的特殊处理,也正是因为parnew的这部分特殊处理,因此parnew不能与老年代的并行GC一起使用

进入老年代

  • 年轻代每次垃圾回收都会给未回收的对象的年龄+1,对象的年龄超过一定限制之后,会被移动到老年代
  • 新生代进行垃圾回收的时候,需要复制到s区的对象的大小如果超过了s区的空间大小,那么超出的对象会被移动到老年代
  • 对象创建的时候,如果对象的大小过大,会直接在老年代中进行创建

老年代

回收算法

  • 标记清除
  • 标记整理

可用GC

  • 串行GC:生产环境基本不太会使用
  • 并行GC:采用标记整理算法
  • CMS:并发标记清除
    • 回收过程
      • 初始标记:暂停整个应用
      • 并发标记
      • 重新标记:暂停整个应用
      • 并发清除
    • 采用free list的方式进行内存的分配,因此在分配的时候不能像bump-the-point方式那样从某个位置开始分配对象即可,需要查找free list找到能放得下该对象的空间,因此一定程度上会减慢新生代的垃圾回收速度(新生代回收之后,如果s区域放不下,需要将对象放入老年代)
    • 并发回收过程中可能出现的并发问题,==这个还不是特别明白==
      • 并发标记过程中,新生代回收可能造成老年代对象引用关系的变化,避免这种问题是通过Mod Union Table来记录新生代回收造成的引用关系变化
      • 并发标记过程中,可能应用修改了老年代中对象之间的引用关系,采用Card Table的方式进行了记录
    • 触发时机
      • 老年代使用空间超过设定的百分比,默认是68%,CMSInitiatingOccupancyFraction参数
      • JVM自动触发,根据之前GC的频率,以及老年代的增长趋势来评估下次GC的时间,UseCMSInitiatingOccupancyOnly=true

FullGC

正常情况下,JVM运行过程中,会经常进行年轻代的GC,生命周期较长的对象会被移动到老年代,当老年代占用的空间超过一定比例,会进行老年代的GC,由于老年代一般是用CMS等并发垃圾回收器,所以老年代的GC过程中,大部分时间是跟程序并发执行的,造成的程序的停顿时间很短,整个GC过程,基本上对程序的影响很少。

但是有几种情况会造成FullGC的执行,FullGC会触发年轻代的GC和老年代的GC,由于年轻代的GC很快,所以几乎可以忽略,所以FullGC几乎等价于老年代的GC。

  • HotSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集old gen的GC都会同时收集整个GC堆,包括young gen,所以对于CMS之外的老年代回收和FullGC实际上是等价的;至于为什么回收老年代的时候一起进行新生代的回收,因为young GC会尽量清理了young gen的死对象,减少了full GC的工作量
  • Old GC:只收集old gen的GC。只有CMS的concurrent collection是这个模式,正常情况下,老年代占用空间超过一定的比例就回触发CMS,所以正常情况下,老年代的空间是够用的,不会触发FullGC,一旦出现FullGC,说明老年代的的CMS的回收速度跟不上了,需要排查原因
  • Mixed GC:收集整个young gen以及部分old gen的GC。只有G1有这个模式

触发FullGC的情况主要有:

  • 老年代空间不足
  • 方法区空间不足
  • 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
  • 由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小,此时CMS GC会出现promotion failed和concurrent mode failure
  • 调用System.gc时,系统建议执行Full GC,但是不必然执行,或者说不一定什么时候执行

参考