JVM-GC-算法

75次阅读

共计 3898 个字符,预计需要花费 10 分钟才能阅读完成。

GC 

垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于 1960 年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。
jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的.

对象存活判断 

引用计数

每个对象有一个引用计数属性,新增一个引用时计数加 1,引用释放时计数减 1,计数为 0 时可以回收。此方法简单,无法解决对象相互循环引用的问题。

可达性分析(Reachability Analysis)

从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的。不可达对象。

在 Java 语言中,GC Roots 包括:

1、虚拟机栈中引用的对象。

2、方法区中类静态属性实体引用的对象。

3、方法区中常量引用的对象。

4、本地方法栈中 JNI 引用的对象。

垃圾收集算法  

标记 - 清除算法

标记 - 清除(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
它的主要缺点有两个:一个是效率问题,标记和清除过程的效率都不高;另外一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法

复制(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,持续复制长生存期的对象则导致效率降低。

标记 - 压缩算法

标记 - 整理(Mark-Compact)算法,标记过程仍然与“标记 - 清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

分代收集算法

分代收集(Generational Collection)算法,把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记 - 清理”或“标记 - 整理”算法来进行回收。

垃圾收集器

串行垃圾回收器 Serial        

串行回收器是指使用单线程进行垃圾回收的回收器,每次回收时串行回收器只有一个工作线程,对于并发能力较弱的计算机来说,串行回收器的专注性和独占性往往有更好的表现。串行回收器可以在新生代和老年代使用,根据作用的堆空间不同,分为新生代串行回收器和老年代串行回收器。
Serial 收集器是最古老的收集器,它的缺点是当 Serial 收集器想进行垃圾回收的时候,必须暂停用户的所有进程,即 stopthe world(服务暂停)。到现在为止,它依然是虚拟机运行在 client 模式下的默认新生代收集器。
参数控制:-XX:+UseSerialGC 使用串行收集器    

并行垃圾回收器 

并行垃圾回收器

并行回收器是在串行回收器的基础上做了改进,它可以使用多个线程同时进行垃圾回收,对于计算能力强的计算机来说,可以有效的缩短垃圾回收所需的实际时间        

并行垃圾回收器——ParNew

ParNew 收集器是一个工作在新生代的垃圾收集器,它只是简单的将串行收集器多线程化,它的回收策略和算法和串行回收器一样。新生代并行,老年代串行;新生代复制算法、老年代标记 - 压缩。
参数控制:-XX:+UseParNewGC 使用 ParNew 收集器     -XX:ParallelGCThreads 限制线程数量

并行垃圾回收器——Parallel

Parallel 是采用复制算法的多线程新生代垃圾回收器,Parallel 收集器更关注系统的吞吐量。所谓吞吐量就是 CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值,即吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能够提升用户的体验;而高吞吐量则可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
可以通过参数来打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量;也可以通过参数控制 GC 的时间不大于多少毫秒或者比例;新生代复制算法、老年代标记 - 压缩
参数控制:-XX:MaxGCPauseMillis 设置最大垃圾收集停顿时间    -XX:GCTimeRatio 设置吞吐量的大小(默认是 99)    -XX:+UseAdaptiveSeizPolicy 打开自适应模式

并行垃圾回收器——Parallel Old

Parallel Old 收集器是 Parallel Scavenge 收集器的老年代版本,采用多线程和”标记-整理”算法,也是比较关注吞吐量。在注重吞吐量及 CPU 资源敏感的场合,都可以优先考虑 Parallel Scavenge 加 Parallel Old 收集器。
参数控制:-XX:+UseParallelOldGC 使用 ParallelOld 收集器    -XX:ParallelGCThreads 限制线程数量

CMS 垃圾回收器

CMS(Concurrent Mark Sweep)并发标记请除,它使用的是标记请除法,工作在老年代,主要关注系统的停顿时间。
CMS 并不是独占的回收器,也就是说,CMS 回收的过程中应用程序仍然在不停的工作,又会有新的垃圾不断的产生,所以在使用 CMS 的过程中应该确保应用程序的内存足够可用,CMS 不会等到应用程序饱和的时候才去回收垃圾,而是在某一阀值 (默认为 68) 的时候开始回收,也就是说当老年代的空间使用率达到 68% 的时候回执行 CMS。如果内存使用率增长很快,在 CMS 执行过程中,已经出现了内存不足的情况,此时,CMS 回收就会失败,虚拟机将启动老年代串行回收器进行垃圾回收,这会导致应用程序中断,直到垃圾回收完成后才会正常工作,这个过程 GC 的停顿时间可能较长,所以阀值的设置要根据实际情况设置。
标记清除法的缺点是内存碎片问题,CMS 提供提供了一些优化设置,可以设置完成 CMS 之后进行一次碎片整理,也可以设置进行多少次 CMS 回收后进行碎片整理
参数控制:
-XX:CMSInitatingPermOccupancyFraction 设置阀值
-XX:+UserConcMarkSweepGC 使用 cms 垃圾清理器
-XX:ConcGCThreads 限制线程数量   
-XX:+UseCMSCompactAtFullCollection 设置完成 CMS 之后进行一次碎片整理   
-XX:CMSFullGCsBeforeCompaction 设置进行多少次 CMS 回收后进行碎片整理

G1(Garbage First)垃圾回收器

G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一。早在 JDK7 就已加入 JVM 的收集器大家庭中,成为 HotSpot 重点发展的垃圾回收技术。同优秀的 CMS 垃圾回收器一样,G1 也是关注最小时延的垃圾回收器,也同样适合大尺寸堆内存的垃圾收集,官方也推荐使用 G1 来代替选择 CMS。G1 最大的特点是引入分区的思路,弱化了分代的概念,合理利用垃圾收集各个周期的资源,解决了其他收集器甚至 CMS 的众多缺陷。
并行与并发:G1 能充分利用多 CPU、多核环境下的硬件优势,使用多个 CPU(CPU 或者 CPU 核心)来缩短 Stop-The-World 停顿的时间,部分其他收集器原本需要停顿 Java 线程执行的 GC 动作,G1 收集器仍然可以通过并发的方式让 Java 程序继续执行。
分代收集:与其他收集器一样,分代概念在 G1 中依然得以保留。虽然 G1 可以不需其他收集器配合就能独立管理整个 GC 堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次 GC 的旧对象以获取更好的收集效果。
空间整合:与 CMS 的“标记 - 清理”算法不同,G1 从整体看来是基于“标记 - 整理”算法实现的收集器,从局部(两个 Region 之间)上看是基于“复制”算法实现,无论如何,这两种算法都意味着 G1 运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次 GC。
可预测的停顿:这是 G1 相对于 CMS 的另外一大优势,降低停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在垃圾收集上的时间不得超过 N 毫秒,这几乎已经是实时 Java(RTSJ)的垃圾收集器特征了。
参数控制:
-XX:+UseG1GC 使用 G1 垃圾收集器   
-XX:ParallelGCThreads 限制线程数量   
-XX:MaxGCPauseMillis 指定最大停顿时间

参考资料:《深入了解 Java 虚拟机》

正文完
 0