JVM垃圾回收机制总结(4) :新一代的垃圾回收算法

G1垃圾回收算法解析
本文深入探讨了G1垃圾回收算法,旨在解决传统分代垃圾回收方式带来的问题,如FullGC导致的应用暂停。通过将堆空间划分为多个等大小的区域(region),并在这些区域内进行垃圾回收,G1算法实现了高吞吐量、支持多CPU和垃圾回收线程等特点。
 

JVM垃圾回收机制总结(4) :新一代的垃圾回收算法

全文转载: http://pengjiaheng.iteye.com/blog/548472

作者: 和你在一起 (from JavaEye)

垃圾回收的瓶颈

传统分代垃圾回收方式,已经在一定程度上把垃圾回收给应用带来的负担降到了最小,把应用的吞吐量推到了一个极限。但是他无法解决的一个问题,就是Full GC所带来的应用暂停。在一些对实时性要求很高的应用场景下,GC暂停所带来的请求堆积和请求失败是无法接受的。这类应用可能要求请求的返回时间在几百甚至几十毫秒以内,如果分代垃圾回收方式要达到这个指标,只能把最大堆的设置限制在一个相对较小范围内,但是这样有限制了应用本身的处理能力,同样也是不可接收的。

分代垃圾回收方式确实也考虑了实时性要求而提供了并发回收器,支持最大暂停时间的设置,但是受限于分代垃圾回收的内存划分模型,其效果也不是很理想。

为了达到实时性的要求(其实Java语言最初的设计也是在嵌入式系统上的),一种新垃圾回收方式呼之欲出,它既支持短的暂停时间,又支持大的内存空间分配。可以很好的解决传统分代方式带来的问题。

增量收集的演进

增量收集的方式在理论上可以解决传统分代方式带来的问题。增量收集把对堆空间划分成一系列内存块,使用时,先使用其中一部分(不会全部用完),垃圾收集时把之前用掉的部分中的存活对象再放到后面没有用的空间中,这样可以实现一直边使用边收集的效果,避免了传统分代方式整个使用完了再暂停的回收的情况。

当然,传统分代收集方式也提供了并发收集,但是他有一个很致命的地方,就是把整个堆做为一个内存块,这样一方面会造成碎片(无法压缩),另一方面他的每次收集都是对整个堆的收集,无法进行选择,在暂停时间的控制上还是很弱。而增量方式,通过内存空间的分块,恰恰可以解决上面问题。

Garbage Firest(G1)

这部分的内容主要参考这里 ,这篇文章算是对G1算法论文的解读。我也没加什么东西了。

目标

从设计目标看G1完全是为了大型应用而准备的。

支持很大的堆

高吞吐量

--支持多CPU和垃圾回收线程

--在主线程暂停的情况下,使用并行收集

--在主线程运行的情况下,使用并发收集

实时目标: 可配置在N毫秒内最多只占用M毫秒的时间进行垃圾回收

当然G1要达到实时性的要求,相对传统的分代回收算法,在性能上会有一些损失。

算法详解

G1可谓博采众家之长,力求到达一种完美。他吸取了增量收集优点,把整个堆划分为一个一个等大小的区域(region)。内存的回收和划分都以 region为单位;同时,他也吸取了CMS的特点,把这个垃圾回收过程分为几个阶段,分散一个垃圾回收过程;而且,G1也认同分代垃圾回收的思想,认为不同对象的生命周期不同,可以采取不同收集方式,因此,它也支持分代的垃圾回收。为了达到对回收时间的可预计性,G1在扫描了region以后,对其中的活跃对象的大小进行排序,首先会收集那些活跃对象小的region,以便快速回收空间(要复制的活跃对象少了),因为活跃对象小,里面可以认为多数都是垃圾,所以这种方式被称为Garbage First(G1)的垃圾回收算法,即:垃圾优先的回收。

回收步骤:

初始标记(Initial Marking)

G1对于每个region都保存了两个标识用的bitmap,一个为previous marking bitmap,一个为next marking bitmap,bitmap中包含了一个bit的地址信息来指向对象的起始点。

开始Initial Marking之前,首先并发的清空next marking bitmap,然后停止所有应用线程,并扫描标识出每个region中root可直接访问到的对象,将region中top的值放入next top at mark start(TAMS)中,之后恢复所有应用线程。

触发这个步骤执行的条件为:

G1定义了一个JVM Heap大小的百分比的阀值,称为h,另外还有一个H,H的值为(1-h)*Heap Size,目前这个h的值是固定的,后续G1也许会将其改为动态的,根据jvm的运行情况来动态的调整,在分代方式下,G1还定义了一个u以及soft limit,soft limit的值为H-u*Heap Size,当Heap中使用的内存超过了soft limit值时,就会在一次clean up执行完毕后在应用允许的GC暂停时间范围内尽快的执行此步骤;

在pure方式下,G1将marking与clean up组成一个环,以便clean up能充分的使用marking的信息,当clean up开始回收时,首先回收能够带来最多内存空间的regions,当经过多次的clean up,回收到没多少空间的regions时,G1重新初始化一个新的marking与clean up构成的环。

并发标记(Concurrent Marking)

按照之前Initial Marking扫描到的对象进行遍历,以识别这些对象的下层对象的活跃状态,对于在此期间应用线程并发修改的对象的以来关系则记录到remembered set logs中,新创建的对象则放入比top值更高的地址区间中,这些新创建的对象默认状态即为活跃的,同时修改top值。

最终标记暂停(Final Marking Pause)

当应用线程的remembered set logs未满时,是不会放入filled RS buffers中的,在这样的情况下,这些remebered set logs中记录的card的修改就会被更新了,因此需要这一步,这一步要做的就是把应用线程中存在的remembered set logs的内容进行处理,并相应的修改remembered sets,这一步需要暂停应用,并行的运行。

存活对象计算及清除(Live Data Counting and Cleanup)

值得注意的是,在G1中,并不是说Final Marking Pause执行完了,就肯定执行Cleanup这步的,由于这步需要暂停应用,G1为了能够达到准实时的要求,需要根据用户指定的最大的GC造成的暂停时间来合理的规划什么时候执行Cleanup,另外还有几种情况也是会触发这个步骤的执行的:

G1采用的是复制方法来进行收集,必须保证每次的”to space”的空间都是够的,因此G1采取的策略是当已经使用的内存空间达到了H时,就执行Cleanup这个步骤;

对于full-young和partially-young的分代模式的G1而言,则还有情况会触发Cleanup的执行,full-young模式下,G1根据应用可接受的暂停时间、回收young regions需要消耗的时间来估算出一个yound regions的数量值,当JVM中分配对象的young regions的数量达到此值时,Cleanup就会执行;partially-young模式下,则会尽量频繁的在应用可接受的暂停时间范围内执行Cleanup,并最大限度的去执行non-young regions的Cleanup。

### JVM垃圾回收机制的工作原理 JVM中的垃圾回收(Garbage Collection, GC)是一种自动化的内存管理技术,其核心目标是通过检测并释放不再使用的对象来优化程序运行时的性能和稳定性。以下是关于JVM垃圾回收机制的核心概念及其工作方式: #### 1. **堆内存区域划分** JVM将堆内存划分为多个逻辑区域,以便更高效地管理和回收内存。这些区域主要包括新生代(Young Generation)、老年代(Old Generation/Tenured Generation),以及永久代/元空间(Permanent Generation/Metaspace)。这种分区设计使得GC可以根据不同区域的特点采取相应的回收策略。 - 新生代通常用于存储短生命周期的对象,频繁执行小型GC操作。 - 老年代则保存长期存活的对象,较少触发大规模GC活动。 - 永久代或元空间主要用于存放类加载器和方法区数据[^1]。 #### 2. **对象引用类型与可达性分析** 为了判断哪些对象可以被安全删除,在实际实现中采用了两种主要的方法——**引用计数法**和**可达性分析法**。然而由于前者存在循环引用等问题而难以广泛应用;后者基于根节点集合(Root Set)遍历整个图结构寻找不可达节点作为候选清理项更为普遍接受。 具体来说: - 当某个对象无法再从任何线程栈顶变量表访问到它时,则认为该实例已经死亡可进入下一步处理流程即标记阶段[^3]。 #### 3. **常见的几种GC算法** ##### (1) 标记-清除算法(Mark-Sweep) 此方案先扫描所有可能成为起点的地方找到活着的数据单元做上标志位记录下来;然后再统一扫一遍把未被打过标签的部分腾出来供后续分配使用。不过缺点在于容易造成碎片化现象影响下次请求连续大块空间效率低下情况发生几率增加[^4]。 ```java // 示例伪代码展示如何模拟简单的标记过程 public class MarkSweepExample { public static void mark(Object obj){ if(obj != null && !obj.isMarked()){ obj.setMark(true); for(Field field : obj.getClass().getDeclaredFields()){ Object fieldValue = ReflectionUtils.getFieldValue(field,obj); mark(fieldValue); // Recursive marking of referenced objects. } } } } ``` ##### (2) 标记-复制算法(Copying) 为了避免上述提到的空间浪费问题提出了另一种思路即将幸存下来的实体迁移到另一片干净区域内从而自然消除掉那些废弃不用的东西所占据的位置。这种方法特别适合于新创建不久就很快消亡的小型临时项目所在之处比如年轻一代里头经常运用这种方式来进行周期性的清扫作业。 ##### (3) 标记-整理算法(Mark-Compact) 综合前两者优点改进而成的新版本既保留了原址不动的优势又解决了因多次移动带来的额外开销困扰。基本做法是在完成常规意义上的标注动作之后不是简单抹去而是向一侧压缩排列紧挨着摆放好剩余的有效成分这样既能保持整体布局紧凑又能减少迁移次数提升速度表现。 ##### (4) 分代收集算法(Generational Collection) 考虑到大多数应用程序都遵循“少数几个长命个体混杂众多短暂伙伴”的规律特性因此引入了按照年龄层次区分对待的原则分别制定差异化的处置措施以达到最佳平衡状态下的效能最大化目的[^2]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值