JVM之GC算法的实现(垃圾回收器)

上一节:《JVM之GC算法》 知道GC算法的理论基础,我们来看看具体的实现。只有落地的理论,才是真理。

一、JVM垃圾回收器的结构

JVM虚拟机规范对垃圾收集器应该如何实现没有规定,因为没有最好的垃圾收集器,只有最适合的场景。

图中展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,则说明它们可以搭配使用。虚拟机所处的区域则表示它是属于新生代还是老年代收集器。

7种:serial收集器、parnew收集器、parallel scavenge收集器、serial  old 收集器、parallel old收集器、cms收集器、g1收集器(整堆收集器)、

串行收集:单垃圾收集线程,进行收集工作,用户进程需要等待

并行收集:工作原理与串行一样,只是在收集垃圾时是多条线程同时进行,收集的效率在一般情况下自然高于单线程。

并发收集:指用户线程与垃圾收集线程同时工作(并发:同一时间间隔)。用户程序在继续运行,而垃圾收集程序运行在另一个CPU上。

吞吐量:吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值(吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间))

1、Serial收集器
Serial(串行)收集器:最基本,最古老的收集器,只有一个线程进行垃圾收集器的工作,并且在进行垃圾收集工作时需要暂停其他工作线程(stop the word),直到他工作结束;
Serial收集器简单高效,工作时没有线程交互的开销,所以可以获得很高的单线程收集效率,对于运行在Client模式下的虚拟机来说很适合。

"-XX:+UseSerialGC":添加该参数来显式的使用Serial垃圾收集器。

2、Serial Old收集器
  Serial Old收集器是Seria收集器的老年代版本,他同样是一个单线程收集器,使用" 标记-整理" 算法。
  Serial Old收集器主要用于Client模式下的虚拟机使用。
  Server模式下的两大用途:

    • 在JDK1.5及之前的版本与Parallel Scavenge收集器搭配使用;
    • 作为CMS收集器的后备方案,在并发收集发生Conturrent Mode Failure时使用。

3、ParNew 收集器
ParNew(并行)收集器就是Serial收集器的多线程版本,除了在收集垃圾时是启用多线程并行执行,其他行为(控制参数、收集算法、回收策略/Stop The Word、对象分配规则)完全一样

应用场景:ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器,因为它是除了Serial收集器外,唯一一个能与CMS收集器配合工作的。

"-XX:+UseConcMarkSweepGC":指定使用CMS后,会默认使用ParNew作为新生代收集器。
"-XX:+UseParNewGC":强制指定使用ParNew。
"-XX:ParallelGCThreads":指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同。

4、Parallel Scavenge收集器
Parallel Scavenge收集器 类似于 ParNew 收集器, Parallel Scavenge收集器 更加关注吞吐量(高效的CPU利用率)。CMS等垃圾收集器关注更多的是用户线程的停顿时间(提搞用户体验);Parallel Scavenge 收集器提供很多参数供我们找到最合适的停顿时间或者最大吞吐量。JDK1.8 默认的方式;

Parallel Scavenge收集器提供了两个参数来用于精确控制吞吐量,一是控制最大垃圾收集停顿时间的 -XX:MaxGCPauseMillis参数,二是控制吞吐量大小的 -XX:GCTimeRatio参数;

  • “ -XX:MaxGCPauseMillis” 参数允许的值是一个大于0的毫秒数,收集器将尽可能的保证内存垃圾回收花费的时间不超过设定的值(但是,并不是越小越好,GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的,如果设置的值太小,将会导致频繁GC,这样虽然GC停顿时间下来了,但是吞吐量也下来了)。
  • “ -XX:GCTimeRatio”参数的值是一个大于0且小于100的整数,也就是垃圾收集时间占总时间的比率,默认值是99,就是允许最大1%(即1/(1+99))的垃圾收集时间。
  • “-XX:UseAdaptiveSizePolicy”参数是一个开发,如果这个参数打开之后,虚拟机会根据当前系统运行情况收集监控信息,动态调整新生代的比例、老年大大小等细节参数,以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应的调节策略。

应用场景:注重高吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge+Parallel Old 收集器。

5、Paraller Old收集器
  Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。
  在JDK1.6中才出现。

6、CMS(Conturrent Mark Sweep)收集器

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。CMS收集器是基于“标记-清除”算法实现,它的整个运行过程可以分为:

  • 初始标记:标记一下GC Roots能直接关联到的对象,这个过程速度很快,但是会暂停其他用户线程(Stop the word)
  • 并发标记:进行GCRoots Tracing的过程,同时开启GC和用户线程,用一个闭包的结构去记录可达对象,但是在这个阶段结束,该闭包不能保证其包含当前所有的可达对象。因为用户进程可能会不断的更新引用域,所以GC线程无法保证可达性分析的实时性。所以这个算法会跟踪记录这些发生引用更新的地方。
  • 重新标记:修正并发标记期间因用户线程继续运作而导致标记产生变动的那一部分对象的标记记录,该阶段会GC停顿,停顿时间比初始标记时间稍长,单远比并发标记时间短。
  • 并发清除:开启用户线程,同事GC线程清除死亡的对象

CMS收集器运行的整个过程中,最耗费时间的是并发标记和并发清除,GC收集器线程和用户线程是一起工作的,所以总体来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。

优点:并发收集、低停顿。
缺点:

  • 1、CMS收集器对CPU资源非常敏感。虽然在两个并发阶段不会导致用户线程停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量下降。CMS默认启动的回收线程数是(CPU数量+3)/4。
  • 2、:CMS收集器无法处理浮动垃圾,可能出现“Conturrent Mode Failure”失败而导致另一次Full GC产生。由于CMS并发清除阶段用户线程还在运行,伴随着程序还在产生新的垃圾,这一部分垃圾出现在标记之后,CMS无法在当次收集中处理掉它们,只能留到下次再清理,这一部分垃圾称为“浮动垃圾”。也正是由于在垃圾收集阶段用户线程还在运行,那么也就需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等待老年代填满之后再进行收集,需要预留一部分空间给并发收集时用户程序使用。可以通过“-XX:CMSInitiatingOccupancyFraction”参数设置老年代内存使用达到多少时启动收集。
  • 3、:由于CMS收集器是一个基于“标记-清除”算法的收集器,那么意味着收集结束会产生大量碎片,有时候往往还有很多内存未使用,但是没有一块连续的空间来分配这个大对象,导致不得不提前触发一次Full GC。CMS收集器提供了一个“-XX:UseCMSCompactAtFullCollection”参数(默认是开启的)用于在CMS收集器顶不住要FullGC时开启内存碎片整理(内存碎片整理意味着无法并发执行不得不停顿用户线程)。参数“-XX:CMSFullGCsBeforeCompaction”来设置执行多少次不压缩的Full GC后,跟着来一次带压缩的(默认值是0,意味着每次进入Full GC时都进行碎片整理)。

7、G1(Garbage-First)收集器

G1的内存模型

G1收集器没有新生代和老年代的概念,而是将Java堆划分为一块块独立的大小相等的Region。当要进行垃圾收集时,首先估计每个Region中的垃圾数量,每次都从垃圾回收价值最大的Region开始回收,因此可以获得最大的回收效率

Humongous是特殊的Old类型,专门放置大型对象.这样的划分方式意味着不需要一个连续的内存空间管理对象.G1将空间分为多个区域,优先回收垃圾最多的区域.
G1采用的是Mark-Copy ,有非常好的空间整合能力,不会产生大量的空间碎片
G1的一大优势在于可预测的停顿时间,能够尽可能快地在指定时间内完成垃圾回收任务,在JDK11中,已经将G1设为默认垃圾回收器,通过jstat命令可以查看垃圾回收情况,在YGC时S0/S1并不会交换.

一个对象和它内部所引用的对象可能不在同一个Region中,那么当垃圾回收时,是否需要扫描整个堆内存才能完整地进行一次可达性分析?
当然不是,每个Region都有一个Remembered Set,用于记录本区域中所有对象引用的对象所在的区域,从而在进行可达性分析时,只要在GC Roots中再加上Remembered Set即可防止对所有堆内存的遍历.

回收步骤

初始标记:标记与GC Roots直接关联的对象,停止所有用户线程,只启动一条初始标记线程,这个过程很快.
并发标记:进行全面的可达性分析,开启一条并发标记线程与用户线程并行执行.这个过程比较长.
最终标记:标记出并发标记过程中用户线程新产生的垃圾.停止所有用户线程,并使用多条最终标记线程并行执行.
筛选回收:回收废弃的对象.此时也需要停止一切用户线程,并使用多条筛选回收线程并行执行.

G1为什么能建立可预测的停顿时间模型?

因为它有计划的避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。这样就保证了在有限的时间内可以获取尽可能高的收集效率。

G1与其他收集器的区别?

其他收集器的工作范围是整个新生代或者老年代、G1收集器的工作范围是整个Java堆。在使用G1收集器时,它将整个Java堆划分为多个大小相等的独立区域(Region)。虽然也保留了新生代、老年代的概念,但新生代和老年代不再是相互隔离的,他们都是一部分Region(不需要连续)的集合。

二、如何选择垃圾收集器

1、单CPU或者小内存,单机程序 — -XX:+UseSerialGC
2、多CPU,需要大吞吐量,如后台计算型应用,允许工作线程停顿超过1秒  -XX:+UseParallelGC + -XX:+UseParallelOldGC
3、多CPU,追求低停顿时间,快速响应如互联网应用  -XX:+UseParNewGC + -XX:+UseConcMarkSweepGC

4、JVM自己选择

5、官方推荐G1,高性能

原文地址:https://www.cnblogs.com/jalja365/p/12182064.html

时间: 2024-11-05 20:36:57

JVM之GC算法的实现(垃圾回收器)的相关文章

jvm参数的配置、垃圾回收器的配置

(1)jvm也是在启动文件中配 -xms:初始堆大小 -xmx:最大堆大小 -xmn:年轻代大小 -XX:PermSize:持久代大小 -XX:MaxPermSize:持久带最大值 -Xss:每个线程堆栈的大小 (2)垃圾回收器的配置 一般使用用cms垃圾回收器(并发垃圾回收器),尽可能降低用户线程在FGC的暂停时间 -XX:+UseconcMarkSweepGC 一般JVM配置 首先先配堆,初始化堆大小和最大堆大小一样,如果cpu内核大小是10,一般配7-6G.其次配年轻代,一般年轻代占堆内存

垃圾回收器的基本原理

垃圾回收器之所以不能完全保证能够回收内存,是因为是一个级别很低的线程.它通过不定时监测程序使用的内存中被占用的动态分配的内存内的对象是否还存在它的引用来判断是否该回收那个内存单元,如果不存在则回收,否则相反~~并不是只要监测到就会回收的,因为垃圾回收器线程的低级别,所以当另一个级别比它高的线程跟他同时竞争运行时间时,前者优先运行,我们通过Thread或者继承Runnable的线程都级别都比它高,所以你无法知道垃圾回收器何时回收,System.gc()只是建议垃圾回收器进行回收处理,调用它并不能保

JVM中的G1垃圾回收器

我们先回顾一下主流Java的垃圾回收器(HotSpot JVM).本文是针对堆的垃圾回收展开讨论的. 堆被分解为较小的三个部分.具体分为:新生代.老年代.持久代. 绝大部分新生成的对象都放在Eden区,当Eden区将满,JVM会因申请不到内存,而触发Young GC ,进行Eden区+有对象的Survivor区(设为S0区)垃圾回收,把存活的对象用复制算法拷贝到一个空的Survivor(S1)中,此时Eden区被清空,另外一个Survivor S0也为空.下次触发Young GC回收Eden+S

JVM 垃圾回收器工作原理及使用实例介绍(转载自IBM),直接复制粘贴,需要原文戳链接

原文 https://www.ibm.com/developerworks/cn/java/j-lo-JVMGarbageCollection/ 再插一个关于线程和进程上下文,待判断 http://blog.sina.com.cn/s/blog_75e9551f01016cm3.html 垃圾收集基础 Java 语言的一大特点就是可以进行自动垃圾回收处理,而无需开发人员过于关注系统资源,例如内存资源的释放情况.自动垃圾收集虽然大大减轻了开发人员的工作量,但是也增加了软件系统的负担. 拥有垃圾收集

JVM总结(二):垃圾回收器

这一节我们来总结一下JVM垃圾收集器方面的东西. 垃圾回收器 判断对象引用是否失效 对象生存判断算法 引用判断过程 垃圾收集算法简介 垃圾收集器 新生代垃圾收集器 老年代垃圾收集器 新生代和老年代垃圾收集器 垃圾回收器 判断对象引用是否失效 对象生存判断算法 引用计数法 给对象中添加一个引用计数器,每当一个地方引用到这个对象的时候,计数器值就加1,当引用失效时,计数器的值就减1,当计数器值变为0时,便说明该对象不可能再被使用了. 优点:实现简单,判定效率较高. 缺点:当出现对象之间的相互循环引用

JVM垃圾回收器工作原理及使用实例介绍

垃圾收集基础 Java 语言的一大特点就是可以进行自动垃圾回收处理,而无需开发人员过于关注系统资源,例如内存资源的释放情况.自动垃圾收集虽然大大减轻了开发人员的工作量,但是也增加了软件系统的负担. 拥有垃圾收集器可以说是 Java 语言与 C++语言的一项显著区别.在 C++语言中,程序员必须小心谨慎地处理每一项内存分配,且内存使用完后必须手工释放曾经占用的内存空间.当内存释放不够完全时,即存在分配但永不释放的内存块,就会引起内存泄漏,严重时甚至导致程序瘫痪. 以下列举了垃圾回收器常用的算法及实

JVM 垃圾回收器工作原理及使用实例介绍

垃圾收集基础 Java 语言的一大特点就是可以进行自动垃圾回收处理,而无需开发人员过于关注系统资源,例如内存资源的释放情况.自动垃圾收集虽然大大减轻了开发人员的工作量,但是也增加了软件系统的负担. 拥有垃圾收集器可以说是 Java 语言与 C++语言的一项显著区别.在 C++语言中,程序员必须小心谨慎地处理每一项内存分配,且内存使用完后必须手工释放曾经占用的内存空间.当内存释放不够完全时,即存在分配但永不释放的内存块,就会引起内存泄漏,严重时甚至导致程序瘫痪. 以下列举了垃圾回收器常用的算法及实

JVM 垃圾回收器工作原理及使用实例介绍(原文已发表于IBM开发者论坛)

打个广告,本人的<大话Java性能优化>一书已经在亚马逊.当当.京东.天猫出售,感谢大家对致力于技术推广梦想者的支持. 垃圾收集基础 Java 语言的一大特点就是可以进行自动垃圾回收处理,而无需开发人员过于关注系统资源,例如内存资源的释放情况.自动垃圾收集虽然大大减轻了开发人员的工作量,但是也增加了软件系统的负担. 拥有垃圾收集器可以说是 Java 语言与 C++语言的一项显著区别.在 C++语言中,程序员必须小心谨慎地处理每一项内存分配,且内存使用完后必须手工释放曾经占用的内存空间.当内存释

Java GC 垃圾回收器的类型小结

阅读了java paper的垃圾回收器类型文章,在此做一个小结,文章部分翻译自java paper gc collector,部分自己做的总结,图片来自网络,在此仅用作理解表达之用. 一.JVM GC 垃圾回收器类型 JVM的垃圾回收器大致分为四种类型: (图片来自网络) 1.串行垃圾回收器  Serial Garbage Collector 串行垃圾回收器在进行垃圾回收时,它会持有所有应用程序的线程,冻结所有应用程序线程,使用单个垃圾回收线程来进行垃圾回收工作. 串行垃圾回收器是为单线程环境而