G1垃圾回收算法【Java】

来自智得网
跳转至: 导航、​ 搜索

简介

G1的内存分布

在G1垃圾回收算法之前,Java的内存区域都分为新生代和旧生代,因为对象的生命周期大多比较短暂,存放短生命周期的新生代空间不需要很大,可以更加频繁的进行回收,旧生代的垃圾回收算法往往较为复杂,而且一般和业务线程并发执行。

G1垃圾回收算法依然沿用了新生代和老年代的概念,而且新生代依然分为Eden区和Survive,但是和之前的垃圾回收算法不同的是这些区域从连续的内存空间变为了分块的内存空间(Region),这些大小相同的内存块分别承担Eden区、Survive区或者老年区的职责。Region的大小默认是JVM的内存区域除以2048,如果对象的大小超过了Region的一半空间,这些对象会存放在单独的区域,该区域称为humongous区。

G1的垃圾回收分为三个阶段,Young GC(新生代回收)、Mix GC(混合回收)、Full GC。触发时机分别如下:

  • 当Eden区域填满的时候,会触发Young GC。
  • 当老年代空间填满到一定比例,默认45%的情况,触发Mix GC。
  • 因为Young GC和Mix GC都是采用复制算法进行垃圾回收,所以当没有空间进行复制的时候,G1会转为使用Serial回收算法进行Full GC。

原理

复制算法

垃圾回收的复制算法

G1的Young GC和其他的新生代垃圾回收算法类似,都是采用经典的复制算法进行回收,复制算法将Survive(存活区)分为两部分,每次会保留其中一半的区域为空闲区域,在进行垃圾回收的时候,会将回收区域(包括存活区的另外一半非空闲区域)的存活对象复制到这部分空闲的空间,当下次执行复制回收的时候,另外一半的Survive区域因为在本次垃圾回收中已经清空了所有的对象,所以可以作为复制的目标区域。

在老年代空间达到一定使用量的情况,G1垃圾回收算法会启动Mix模式进行垃圾回收,Mix同样采用复制算法进行回收,但是除了新生代之外,还会回收老年代的对象。

跨代引用

在进行Young GC的时候,也需要采用标记算法将存活的对象进行标记,Young GC因为只需要回收新生代的对象,不需要对老年代等区域进行全量的标记扫描,但是因为存在跨代引用的问题,也就是老年的对象引用了新生代对象的情况,如果不进行老年代对象的引用计算,可能会出现将被引用的对象清除的情况。

CMS采用了Card Table来记录老年代到新生代的引用,每个Card 覆盖一定范围的Heap(默认512Bytes),记录了老年代在该堆内存区域的引用关系,在GC期间会扫描整个Card Table避免把年轻代中被老年代引用的对象回收。

因为老年代空间较大,扫描Card Table的成本较高,而且G1是分块的内存管理机制,所以G1引入了RSet的来解决跨代引用的问题。每个Region都会有一个RSet,RSet记录了其他Region到本Region的引用关系,RSet的存储模型是HashTable,该Hash表的Key是其他的Region,Value是Card Table的索引。

Full GC

因为Young GC和Mix GC都是采用复制算法进行垃圾回收,所以当没有空间进行复制的时候,G1会转为使用Serial回收算法进行Full GC。