JVM04——七个GC垃圾收集器,一个都不能少
了解了JVM内存区域与垃圾回收算法,今天将为各位带来关于垃圾收集器的知识。关注我的公众号「Java面典」了解更多 Java 相关知识点。
Java 堆内存被划分为新生代和老年代两部分,因此 JVM 通常采用分代回收算法。新生代主要使用复制和标记-清除垃圾回收算法 ,老年代主要使用标记-整理垃圾回收算法。JVM 中针对新生代和年老代分别提供了多种不同的垃圾收集器。
根据线程特点,可以将收集器分为三类:
- 串行收集器:Serial 收集器、Serial Old 收集器;
// 串行收集器开启方式
-XX:+UseSerialGC
- 并行收集器:Parallel Scavenge 收集器、Parallel Old 收集器、ParNew 收集器;
// 并行收集器开启方式
-XX:+UseSerialGC
- 并发收集器:CMS 收集器、G1 收集器。
// CMS 收集器开启方式
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
// G1 收集器开启方式
-XX:+UseG1GC
Serial 收集器
- 新生代:Serial 垃圾收集器是 JVM 运行在 Client 模式下默认的新生代垃圾收集器;
- 复制算法 :Serial 是最基本垃圾收集器,使用复制算法,曾经是 JDK1.3.1 之前新生代唯一的垃圾收集器;
- 单线程:Serial 只会使用一个 CPU 或一条线程去完成垃圾收集工作;
- 线程暂停:Serial 在进行垃圾收集的时,必须暂停其他所有的工作线程,直到垃圾收集结束;
- 单线程效率最高:Serial 对于限定单个 CPU 环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率。
ParNew 收集器
- 新生代:ParNew 垃圾收集器是很多 JVM 运行在 Server 模式下新生代的默认垃圾收集器;
- 复制算法 : ParNew 和 Serial 一样使用了复制算法;
- 多线程 :ParNew 垃圾收集器其实是 Serial 收集器的多线程版本;
- 线程暂停:ParNew 和 Serial 一样垃圾收集的同时,必须暂停其他所有的工作线程;
- 默认线程数:ParNew 收集器默认开启和 CPU 数目相同的线程数,可以通过-XX:ParallelGCThreads 参数来限制垃圾收集器的线程数。
Parallel Scavenge 收集器
- 新生代:Parallel Scavenge 收集器也是一个新生代垃圾收集器;
- 复制算法:Parallel Scavenge 收集器同样使用复制算法;
- 多线程 :Parallel Scavenge 收集器也是一个多线程的垃圾收集器;
- 高吞吐量:Parallel Scavenge 重点关注的是程序达到一个可控制的吞吐量(Thoughput,吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)),高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务;
- 适用场景:主要适用于在后台运算而不需要太多交互的任务。自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个重要区别。
新生代 Parallel Scavenge 收集器与 ParNew 收集器工作原理类似:
- 都是多线程的收集器;
- 都使用的是复制算法;
- 在垃圾收集过程中都需要暂停所有的工作线程。
Serial Old 收集器
- 老年代:Serial Old 是 Serial 垃圾收集器老年代的版本;
- 标记-整理算法:Serial Old 使用标记-整理算法;
- 单线程:Serial Old 与 Serial 一样是单线程收集器;
- Client 模式:JVM 运行在 Client 模式下,Serial Old 是默认的老年代垃圾收集器;
- Server 模式:JVM 运行在 Server 模式下,Serial Old 主要有两个用途:
- 在 JDK1.5 之前版本中与新生代的 Parallel Scavenge 收集器搭配使用;
- 作为年老代中使用 CMS 收集器的后备垃圾收集方案。
新生代 Serial 与年老代 Serial Old 搭配垃圾收集过程图:
新生代 Parallel Scavenge 收集器与 ParNew 收集器工作原理类似,都是多线程的收集器,都使用的是复制算法,在垃圾收集过程中都需要暂停所有的工作线程。新生代 Parallel Scavenge/ParNew 与年老代 Serial Old 搭配垃圾收集过程图:
Parallel Old 收集器
- 老年代:Parallel Old 收集器是 Parallel Scavenge 的老年代版本;
- 标记-整理算法:Parallel Old 收集器使用标记-整理算法;
- 多线程:Parallel Old 收集器是多线程收集器;
- JDK1.6:Parallel Old 是 JDK1.6才开始提供的。
在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只能保证新生代的吞吐量优先,无法保证整体的吞吐量,Parallel Old 正是为了在年老代同样提供吞吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,可以优先考虑新生代 Parallel Scavenge和年老代 Parallel Old 收集器的搭配策略。
新生代 Parallel Scavenge 和年老代 Parallel Old 收集器搭配运行过程图:
CMS 收集器
- 老年代:CMS(Concurrent mark sweep)收集器是一种年老代垃圾收集器;
- 标记-清理算法:和其他年老代使用标记-整理算法,CMS 使用标记-清除算法;
- 多线程 :CMS 采用的是多线程的标记-清除算法;
- 停顿时间端: CMS最主要目标是获取最短垃圾回收停顿时间,最短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验。
CMS 运行过程分为以下 4 个阶段:
- 初始标记:标记 GC Roots 能直接关联的对象(速度很快,需要暂停所有的工作线程);
- 并发标记:进行 GC Roots 跟踪(和用户线程一起工作,不需要暂停工作线程);
- 重新标记:修正因用户程序继续运行而导致标记产生变动的那一部分对象的标记(需要暂停所有的工作线程);
- 并发清除:清除 GC Roots 不可达对象(和用户线程一起工作,不需要暂停工作线程)。
由于耗时最长的并发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,所以总体上来看CMS 收集器的内存回收和用户线程是一起并发地执行。
G1 收集器
特点
- 无分代:G1 将新生代,老年代的物理空间划分取消了。这样我们再也不用单独的空间对每个代进行设置了,不用担心每个代内存是否足够;
- 标记-整理算法:G1 收集器采用标记-整理算法,无内存碎片产生;
- 分区回收:G1 虽然没有了新生代与老年代的物理限制,但是 G1 采取内存分区策略,将堆内存划分为大小固定的几个独立区域。在分区中,同时存在新生代与老年代;
分区
新生代区域:G1 收集器中新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者 Survivor 空间;
老年代区域:G1 收集器通过将对象从一个区域复制到另外一个区域,以此来完成老年代的清理工作;
Humongous区域:巨型对象区域。如果一个对象占用的空间超过了分区容量 50% 以上,G1 收集器就认为这是一个巨型对象。这些巨型对象,默认直接会被分配在年老代,但是如果它是一个短期存在的巨型对象,就会对垃圾收集器造成负面影响。为了解决这个问题,G1 划分了一个 Humongous 区,它用来专门存放巨型对象。如果一个 H 区装不下一个巨型对象,那么 G1 会寻找连续的 H 分区来存储。为了能找到连续的 H 区,有时候不得不启动 Full GC。
对象分配策略
说起大对象的分配,我们不得不谈谈对象的分配策略。它分为3个阶段:
- TLAB(Thread Local Allocation Buffer)线程本地分配缓冲区
- Eden区中分配
- Humongous区分配
TLAB为线程本地分配缓冲区,它的目的为了使对象尽可能快的分配出来。如果对象在一个共享的空间中分配,我们需要采用一些同步机制来管理这些空间内的空闲空间指针。在Eden空间中,每一个线程都有一个固定的分区用于分配对象,即一个TLAB。分配对象时,线程之间不再需要进行任何的同步。
对TLAB空间中无法分配的对象,JVM会尝试在Eden空间中进行分配。如果Eden空间无法容纳该对象,就只能在老年代中进行分配空间。
最后,G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW)的。下面我们将分别介绍一下这2种模式。
G1 Young GC
Young GC 主要是对 Eden 区进行 GC ,它在 Eden 空间耗尽时会被触发。在这种情况下,Eden 空间的数据移动到 Survivor 空间中,如果 Survivor 空间不够,Eden 空间的部分数据会直接晋升到年老代空间。Survivor 区的数据移动到新的 Survivor 区中,也有部分数据晋升到老年代空间中。最终 Eden 空间的数据为空,GC 停止工作,应用线程继续执行。
G1 Young GC 阶段:
- 根扫描:静态和本地对象被扫描;
- 更新RS: 处理 Dirty Card 队列更新 RS(Remembered Set,作用是跟踪指向某个 Heap 区内的对象引用);
- 处理RS: 检测从年轻代指向年老代的对象;
- 对象拷贝:拷贝存活的对象到 Survivor/Old 区域;
- 处理引用队列: 软引用,弱引用,虚引用处理。
G1 Mix GC
Mix GC不仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的老年代分区。
G1 Mix GC运行步骤:
全局并发标记(global concurrent marking)
1.1. 初始标记(initial mark,STW):在此阶段,G1 GC 对根进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关;
1.2. 根区域扫描(root region scan):G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 年轻代垃圾回收;
1.3. 并发标记(Concurrent Marking):G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 年轻代垃圾回收中断;
1.4. 最终标记(Remark,STW): 该阶段是 STW 回收,帮助完成标记周期。G1 GC 清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理;
1.5. 清除垃圾(Cleanup,STW):在这个最后阶段,G1 GC 执行统计和 RSet 净化的 STW 操作。在统计期间,G1 GC 会识别完全空闲的区域和可供进行混合垃圾回收的区域。清理阶段在将空白区域重置并返回到空闲列表时为部分并发。拷贝存活对象(evacuation)
G1 收集器与 CMS 收集器相比,G1 收集器两个最突出的改进是:
- 基于标记-整理算法,不产生内存碎片;
- 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。
// G1 收集器参数设置
-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=200
// -XX:+UseG1GC —— 为开启G1垃圾收集器,
// -Xmx32g —— 设计堆内存的最大内存为32G,
// -XX:MaxGCPauseMillis=200 —— 设置GC的最大暂停时间为200ms
JVM系列推荐
JVM04——七个GC垃圾收集器,一个都不能少的更多相关文章
- JAVA GC垃圾收集器的分析
本篇文章主要介绍了"JAVA GC垃圾收集器的分析",主要涉及到JAVA GC垃圾收集器的分析方面的内容,对于JAVA GC垃圾收集器的分析感兴趣的同学可以参考一下. ...
- java - GC垃圾收集器详解(一)
概要 该图标记了在jdk体系中所使用到的垃圾收集器及对应的关系图.图片上方为年轻代的垃圾收集器而图片下方是老年代的垃圾收集器.当选择某一个区域的垃圾收集器时会自动选择另外一个区域的另一个垃圾收集器.例 ...
- JVM学习笔记——GC垃圾收集器
GC 垃圾收集器 Java 堆内存采用分代回收算法,因此 JVM 针对新生代和老年代提供了多种垃圾收集器. 1. Serial 收集器 Serial 收集器是单线程收集器,采用复制算法. 是最基本的垃 ...
- Spark学习之路 (十四)SparkCore的调优之资源调优JVM的GC垃圾收集器
一.概述 垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟机栈.本 ...
- Spark(八)JVM调优以及GC垃圾收集器
一JVM结构 1 Java内存结构 JVM内存结构主要有三大块:堆内存.方法区和栈. 堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间.From Survivo ...
- java - GC垃圾收集器详解(三)
以前收集器的特点 年轻代和老年代是各自独立且连续的内存块 年轻代收集必须使用单个eden+S0+S1进行复制算法 老年代收集扫描整个老年代区域 都是以尽可能少而快速地执行GC为设计原则 G1是什么 G ...
- 第七课 GC资源管理器实验
<GC资源管理器> 要求: 1.定义2个资源消耗组(OLTP事务处理资源消耗组和DSS数据仓库资源消耗组) 2.创建一个日程计划schedule 3.对于OLTP资源消耗组,我们最多不 ...
- 【JVM】-NO.110.JVM.1 -【GC垃圾收集器】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- GC垃圾收集器分类
参考https://blog.csdn.net/tjiyu/article/details/53983650 Java垃圾收集器组合: 新生代收集器:Serial.ParNew.Parallel Sc ...
随机推荐
- centos 中文乱码解决办法
缘由:本人在虚拟机中安装centos 5.3,起初安装时选择了english,后来使用的过程中发现打开网页,会出现中文乱码,无法正常显示.当然,本地文件中的中文更是无法显示. 若是将系统语言langu ...
- deeplearning.ai 人工智能行业大师访谈
Geoffrey Hinton 1. 怀揣着对大脑如何存储记忆的好奇,Hinton本科最开始学习生物学和物理学,然后放弃,转而学习哲学:然后觉得哲学也不靠谱,转而学习心理学:然后觉得心理学在解释大脑运 ...
- python练习题——猜数字游戏
增加了按照对半找数的方法来计算最短几次就可以猜到随机数,决定到游戏结束共猜数的次数: from random import * import numpy as np from numpy import ...
- 使用mybatis的动态sql解析能力生成sql
需求: 计算平台,有很多表,打算提供一个基于sql的服务接口, sql不能完全在配置页面写死, 要能根据参数不同执行不同的语义,防止sql个数爆炸 把mybatis原码down下来, 改造一下测试用例 ...
- pycharm中无法导入pip安装的包
https://blog.csdn.net/mdxiaohu/article/details/82430060 2020.1.20 练习通过python操作数据库的时候需要导入一个包,因为看代码写的是 ...
- JS做深度学习3——数据结构
最近在上海上班了,很久没有写博客了,闲下来继续关注和研究Tensorflow.js 关于深度学习的文章我也已经写了不少,部分早期作品可能包含了不少错误的认识,在后面的博文中会改进或重新审视. 今天聊聊 ...
- Java并发编程入门与高并发面试(三):线程安全性-原子性-CAS(CAS的ABA问题)
摘要:本文介绍线程的安全性,原子性,java.lang.Number包下的类与CAS操作,synchronized锁,和原子性操作各方法间的对比. 线程安全性 线程安全? 线程安全性? 原子性 Ato ...
- unittest(22)- p2p项目实战(8)-test_class_auto_incre
# 8.test_class_auto_incre # 使用ddt import requests import unittest from p2p_project_7.tools.http_requ ...
- Replace into 与Insert into on duplicate key update的区别
前提条件:除非表有一个PRIMARY KEY或UNIQUE索引,否则,使用这2条语句没有意义.该语句会与INSERT相同 1. Replace into (1) 添加相同的主键 操作前 ...
- iPhone X价格下跌!用户依旧冷眼相看为哪般?
X价格下跌!用户依旧冷眼相看为哪般?" title="iPhone X价格下跌!用户依旧冷眼相看为哪般?"> 其实对于刚刚过去的2017年手机市场来说,根本没有一款 ...