java虚拟机面试干货【玖】_G1 GC的空间划分

之前有看过介绍G1回收器的知识,但是一直没有去完成一个整理。接着今天的功夫,好好把这部分知识总结一下。


什么是G1 GC


通过前面的文章我们知道,在JDK 1.6版本之前,我们一般是使用ParNew+CMS两个垃圾回收器来完成JVM中的垃圾回收的。但是它们的实现并不算特别的高效,详情可见之前的文章总结(链接)。有鉴于此,JDK从1.7版本开始全新引入了一个新的垃圾回收期,就是今天要说的G1 GC。和之前的回收器相比,G1 GC有如下的特点:


并行性:G1在回收期间,可以有多个GC线程同时工作,可以有效利用多核的计算能力。

并发性:G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此,一般来说,不会在整个回收阶段发生完全阻塞应用程序的情况。

分代GC:G1依然是一个分代收集器,但是和之前的各类回收器不同,它同时兼顾了年轻代和老年代。对比其他回收器,它们或者工作在年轻代,或者工作在老年代。

空间整理:G1在回收过程中,会进行适当的对象移动,不像CMS那样只是简单地标记清理对象。在若干次GC后,CMS必须进行一次碎片整理。而G1不同,它每次回收都会有效地复制对象,减少空间碎片,进而提升内部循环速度。

可预见性:由于分区的原因,G1可以只选取部分区域进行内存回收,这样缩小了回收的范围,因此对于全局停顿情况的发生也能得到较好的控制。

扫描二维码关注公众号,回复: 864596 查看本文章


G1 GC的新特性


G1 GC依然采用的分代回收的算法思想,在堆空间划分上,依然保留了新生代(包括一个Eden区和两个Survivor区)和老年代。但在设计层面和上一代回收器相比,有如下的改动:


1.去掉了永久代

我们知道,在HotSpot时代,我们将类加载器对象、类对象以及运行时常量池都保存在永久代进行存储。而且永久代的大小是固定的,当永久代空间不够时会触发Full GC,如果Full GC后空间仍无法满足,则抛出OOM:Perm Gen异常。而随着JDK 8的到来,JVM不再有PermGen。类的元数据信息(metadata)不再存储在连续的堆空间上,而是移动到叫做元空间(Metaspace)的本地内存(Native memory,容量取决于是32/64位操作系统的可用虚拟内存大小)中。

对于元空间的大小我们可以通过置如下参数完成配置:

-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

-XX:MaxMetaspaceSize来限制Metaspace空间的增长。默认情况下,-XX:MaxMetaspaceSize并没有限制,如果没有指定这个参数,元空间会在运行时根据需要动态调整。

-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集。

-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集。


2.增加了大对象区间

大对象(humongous)通常是很大的对象,占据了超过Gl GC中区间(region)大小的50%。大对象没有按照年轻代的回收方式把对象放入老年区,而是放置在老年区以外的大对象区间(humongous regions)里面,即单独分离出来形成一片新的区域,独立管理。

当储存一个比一个区间还大的大对象时,我们需要一个连续的可用分区集合进行存储。横跨的第一个区间叫大对象开始区间(humongous start),后面的区间叫连续的大对象区间(humongous continues)。

因为大对象在堆内存里是物理连续的,所以存在两个缺点:一是移动对象需要拷贝数据,大对象的拷贝性能开销比较大;二是如果没有足够空间分配大对象时会触发full GC,因此当系统中有越多越大的大对象存储,回收可能越频繁。


在JDK8u40之前的版本, 即便大对象区间是完全空闲的,也只会在并行回收循环的清除暂停阶段才会回收大对象。而在 JDK8U40之后,新增了年轻代、Full GC阶段针对大对象区间的回收功能,只要大对象区间不再包含任何引用,这些区间就会被回收并且放入空闲区间队列。即年轻代回收、并行回收循环、 Full GC, 它们都会参与到大对象区间的回收工作。

G1 GC的区间


在上面介绍大对象区间时我们提到的区间(region)的概念,那么区间是什么呢?在G1 GC中,堆空间被平均分成若干个大小相等的区间,我们可以通过-XX:G1HeapRegionSize参数来设置一个区间的空间大小。区间的大小必须为2的倍数,范围为1MB到32MB,即1MB、2MB、4MB、8MB、16MB或32MB。JVM最多可以容纳大概2048个区间。


region支持动态分配,随着每次GC的回收,一个region可以分配给eden、survivor、老年代、大对象区间、空闲区间等,不固定它的作用。说到这里还要说下两个概念,RSet和CSet:


RSet


每个区间都有一个关联的Remembered Set(RSet),RSet的数据结构是hash表,里面的数据是Card Table(堆中每512byte映射在card table 1byte)。说白了,RSet保存的就是region中存活对象的指针。当region数据发生变化时,首先反应到card table中的一个或多个card上,RSet通过扫描内部的card table得知region中内存使用情况和存活对象,目的是让区间可以独立进行GC,而不用遍历整个堆空间。RSet大小不能超过JVM大小的5%。


CSet


即Collection Sets,保存一次GC中需要执行垃圾回收的区间(region),GC中CSet中的所有存活数据被转移。CSet大小大概是JVM大小的1%。


下篇文章说说G1GC中的垃圾回收。

猜你喜欢

转载自blog.csdn.net/xinzun/article/details/80280720
今日推荐