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,高性能
JVM之GC算法的实现(垃圾回收器)的更多相关文章
- java虚拟机入门(五)- 常见垃圾回收器及jvm实现
上节讲完了垃圾回收的基础,包括java的垃圾是什么,如何寻找以及常用的垃圾回收算法,那么那么多的理论知识讲完了,具体是什么样的东西在做着回收垃圾的事情呢?我们接下来就好好聊聊jvm中常用的垃圾回收器. ...
- JVM学习——G1垃圾回收器(学习过程)
JVM学习--G1垃圾回收器 把这个跨时代的垃圾回收器的笔记独立出来. 新生代:适用复制算法 老年代:适用标记清除.标记整理算法 二娃本来看G1的时候觉得比较枯燥,但是后来总结完之后告诉我说,一定要慢 ...
- Java基础知识强化83:System类之gc()方法(垃圾回收)以及和finalize()区别
1. System概述: System类包含一些有用的类字段和方法.它不能被实例化. 2. gc()方法:垃圾回收器 public static void gc() 调用gc方法暗示着Ja ...
- [译]Java垃圾回收器的类型
说明:这篇文章来翻译来自于Javapapers 的Types of Java Garbage Collectors 在这部分的教程中我们将讲到可使用的四种不同类型的Java垃圾回收器.垃圾回收是Jav ...
- G1垃圾回收器在并发场景调优
一.序言 目前企业级主流使用的Java版本是8,垃圾回收器支持手动修改为G1,G1垃圾回收器是Java 11的默认设置,因此G1垃圾回收器可以用很长时间,现阶段垃圾回收器优化意味着针对G1垃圾回收器优 ...
- jvm垃圾回收器介绍
上篇文章中我们讨论了jvm的内存区域,这篇文章我们来讨论针对的内存区域的垃圾回收机制. 其实针对垃圾回收我们通常考虑三个问题:1.哪些内存需要回收?2.什么时候回收?3.如何回收?下面我们针对这三个问 ...
- 深入探究JVM之垃圾回收器
@ 目录 前言 正文 一.垃圾收集算法 标记-复制 标记-清除 标记-整理 分代回收 二.常用的垃圾回收器 Serial/SerialOld ParNew Parallel Scavenge/Para ...
- JVM有哪些垃圾回收器
JVM 的垃圾回收器 目录 JVM 的垃圾回收器 经典垃圾收集器 Serial 收集器 ParNew 收集器 Parallel Scavenge 收集器 Serial Old 收集器 Parallel ...
- JVM垃圾回收器(三)
垃圾回收知识点 引用计数 给对象添加一个引用计数器,每当一个地方引用这个对象,这个计算器就加1.如果引用失效,那计算器就减1.如果计算器数量为0,那这个对象就是失效的. 但是如果2个对象虽然不用了,但 ...
随机推荐
- PHP中 spl_autoload_register() 函数用法
这篇文章主要介绍了PHP中spl_autoload_register()函数用法,结合实例形式分析了__autoload函数及spl_autoload_register函数的相关使用技巧,需要的朋友可 ...
- jieba完整文档
jieba “结巴”中文分词:做最好的 Python 中文分词组件 "Jieba" (Chinese for "to stutter") Chinese tex ...
- H3C 不同匹配顺序导致结果不同
- [转]vue router基本使用
第一步:安装 cnpm install vue-router --save 路由配置基本语法 router下index.js引入 import Vue from "vue"; im ...
- MySQL——修改视图
修改视图是指修改数据库中存在的视图,当基本表的某些字段发生变化时,可以通过修改视图来保持与基本表的一致性. 1. 用 CREATE OR REPLACE VIEW 语句修改视图 语法格式 ...
- Linux 内核pci添加一个驱动
一个 PCI 驱动被添加到 PCI 核心, 当它调用 pci_register_driver 函数时. 这个函数 只初始化 struct device_driver 结构, 这个结构包含在 struc ...
- Mybatis 框架
在之前的内容中,我写了Java的基础知识.Java Web的相关知识.有这些内容就可以编写各种各样丰富的程序.但是如果纯粹手写所有代码,工作量仍然很大.为了简化开发,隐藏一些不必要的细节,专心处理业务 ...
- Cortex-A8/A76
Cortex-A8 关于Cortex-A8的微处理架构参考<ARM_Cortex-A8微处理器的架构和实现> 其中关于NEON有两段话摘录如下: NEON媒体引擎拥有自己的10段流水线,它 ...
- 第二阶段:1.流程图:12.AXURE绘制页面流程图
注意的事项: 完整的页面流程图
- 程序员必须掌握的性能调优 X Y Z
热评博文:<如何设计出优美的Web API?>,现阅读量超 2500,小伙伴们不要错过哦! 2003 ~ 2008 年,这五年老兵哥我在通信行业做实习生和开发岗,主要用 C / C++ / ...