技术标签: jvm ZGC cms G1 JVM从小白学成大佬
在开始介绍前,我们可以剧透几点:
CMS和G1作为垃圾收集器里的大杀器,是需要好好弄明白的,而且面试中也经常被问到,但对于ZGC却很少提及。
希望大家带着下面的问题进行阅读,有目标的阅读,收获更多:
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。这是因为CMS收集器工作时,GC工作线程与用户线程可以并发
执行,以此来达到降低收集停顿时间的目的。
CMS收集器仅作用于老年代的收集,是基于标记-清除算法
的,它的运作过程分为4个步骤:
其中,初始标记
、重新标记
这两个步骤仍然需要Stop-the-world。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段就是进行GC Roots Tracing的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始阶段稍长一些,但远比并发标记的时间短。
CMS以流水线方式拆分了收集周期,将耗时长的操作单元保持与应用线程并发执行。只将那些必需STW才能执行的操作单元单独拎出来,控制这些单元在恰当的时机运行,并能保证仅需短暂的时间就可以完成。这样,在整个收集周期内,只有两次短暂的暂停(初始标记和重新标记),达到了近似并发的目的。
CMS收集器优点:并发收集、低停顿。
CMS收集器缺点:
CMS收集器之所以能够做到并发,根本原因在于采用基于“标记-清除”的算法并对算法过程进行了细粒度的分解。前面篇章介绍过标记-清除算法将产生大量的内存碎片这对新生代来说是难以接受的,因此新生代的收集器并未提供CMS版本。
备注:说CMS是老年代收集器,其实不是非常准确。CMS 的各个收集过程其实是一个涉及年轻代和老年代的综合性垃圾回收器,在很多文章和书籍的划分中,都将 CMS 划分为了老年代垃圾回收器,加上它主要作用于老年代,所以一般误认为是。
另外要补充一点,JVM在暂停的时候,需要选准一个时机。由于JVM系统运行期间的复杂性,不可能做到随时暂停,因此引入了安全点的概念。
安全点,即程序执行时并非在所有地方都能停顿下来开始GC,只有在到达安全点时才能暂停。Safepoint的选定既不能太少以至于让GC等待时间太长,也不能过于频繁以致于过分增大运行时的负荷。
安全点的初始目的并不是让其他线程停下,而是找到一个稳定的执行状态。在这个执行状态下,Java虚拟机的堆栈不会发生变化。这么一来,垃圾回收器便能够“安全”地执行可达性分析。只要不离开这个安全点,Java虚拟机便能够在垃圾回收的同时,继续运行这段本地代码。
程序运行时并非在所有地方都能停顿下来开始GC,只有在到达安全点时才能暂停。安全点的选定基本上是以程序“是否具有让程序长时间执行的特征”为标准进行选定的。“长时间执行”的最明显特征就是指令序列复用,例如方法调用、循环跳转、异常跳转等,所以具有这些功能的指令才会产生Safepoint。
对于安全点,另一个需要考虑的问题就是如何在GC发生时让所有线程(这里不包括执行JNI调用的线程)都“跑”到最近的安全点上再停顿下来。
两种解决方案:
抢先式中断(Preemptive Suspension)
抢先式中断不需要线程的执行代码主动去配合,在GC发生时,首先把所有线程全部中断,如果发现有线程中断的地方不在安全点上,就恢复线程,让它“跑”到安全点上。现在几乎没有虚拟机采用这种方式来暂停线程从而响应GC事件。
主动式中断(Voluntary Suspension)
主动式中断的思想是当GC需要中断线程的时候,不直接对线程操作,仅仅简单地设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时就自己中断挂起。轮询标志的地方和安全点是重合的,另外再加上创建对象需要分配内存的地方。
指在一段代码片段中,引用关系不会发生变化。在这个区域中任意地方开始GC都是安全的。也可以把Safe Region看作是被扩展了的Safepoint。
G1重新定义了堆空间,打破了原有的分代模型,将堆划分为一个个区域。这么做的目的是在进行收集时不必在全堆范围内进行,这是它最显著的特点。区域划分的好处就是带来了停顿时间可预测
的收集模型:用户可以指定收集操作在多长时间内完成。即G1提供了接近实时的收集特性。G1 的主要关注点在于达到可控的停顿时间,在这个基础上尽可能提高吞吐量。
G1 使用了停顿预测模型来满足用户指定的停顿时间目标,并基于目标来选择进行垃圾回收的区块数量。G1 采用增量回收
的方式,每次回收一些区块,而不是整堆回收。要清楚 G1 不是一个实时收集器(只是接近实时),它会尽力满足我们的停顿时间要求,但也不是绝对的,它基于之前垃圾收集的数据统计,估计出在用户指定的停顿时间内能收集多少个区块。
G1与CMS的特征对比如下:
特征 | G1 | CMS |
---|---|---|
并发和分代 | 是 | 是 |
最大化释放堆内存 | 是 | 否 |
低延时 | 是 | 是 |
吞吐量 | 高 | 低 |
压实 | 是 | 否 |
可预测性 | 强 | 弱 |
新生代和老年代的物理隔离 | 否 | 是 |
G1具备如下特点:
在G1之前的其他收集器进行收集的范围都是整个新生代或者老年代,而G1不再是这样。在堆的结构设计时,G1打破了以往将收集范围固定在新生代或老年代的模式,G1收集器将整个Java堆划分为多个大小相等的独立区域(Region)。Region是一块地址连续的内存空间,G1模块的组成如下图所示:
虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。Region的大小是一致的,数值是在1M到32M
字节之间的一个2的幂值数,JVM会尽量划分2048
个左右、同等大小的Region,这一点可以参看如下源码。其实这个数字既可以手动调整,G1也会根据堆大小自动进行调整。
#ifndef SHARE_VM_GC_G1_HEAPREGIONBOUNDS_HPP
#define SHARE_VM_GC_G1_HEAPREGIONBOUNDS_HPP
#include "memory/allocation.hpp"
class HeapRegionBounds : public AllStatic {
private:
// Minimum region size; we won't go lower than that.
// We might want to decrease this in the future, to deal with small
// heaps a bit more efficiently.
static const size_t MIN_REGION_SIZE = 1024 * 1024;
// Maximum region size; we don't go higher than that. There's a good
// reason for having an upper bound. We don't want regions to get too
// large, otherwise cleanup's effectiveness would decrease as there
// will be fewer opportunities to find totally empty regions after
// marking.
static const size_t MAX_REGION_SIZE = 32 * 1024 * 1024;
// The automatic region size calculation will try to have around this
// many regions in the heap (based on the min heap size).
static const size_t TARGET_REGION_NUMBER = 2048;
public:
static inline size_t min_size();
static inline size_t max_size();
static inline size_t target_number();
};
#endif // SHARE_VM_GC_G1_HEAPREGIONBOUNDS_HPP
G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。G1会通过一个合理的计算模型,计算出每个Region的收集成本并量化,这样一来,收集器在给定了“停顿”时间限制的情况下,总是能选择一组恰当的Regions作为收集目标,让其收集开销满足这个限制条件,以此达到实时收集的目的。
对于打算从CMS或者ParallelOld收集器迁移过来的应用,按照官方 的建议,如果发现符合如下特征,可以考虑更换成G1收集器以追求更佳性能:
原文如下:
Applications running today with either the CMS or the ParallelOld garbage collector would benefit switching to G1 if the application has one or more of the following traits.
- More than 50% of the Java heap is occupied with live data.
- The rate of object allocation rate or promotion varies significantly.
- Undesired long garbage collection or compaction pauses (longer than 0.5 to 1 second)
G1收集的运作过程大致如下:
停顿线程
,但耗时很短。停顿线程
,但是可并行执行。全局变量和栈中引用的对象是可以列入根集合的,这样在寻找垃圾时,就可以从根集合出发扫描堆空间。在G1中,引入了一种新的能加入根集合的类型,就是记忆集
(Remembered Set)。Remembered Sets(也叫RSets)用来跟踪对象引用。G1的很多开源都是源自Remembered Set,例如,它通常约占Heap大小的20%或更高。并且,我们进行对象复制的时候,因为需要扫描和更改Card Table的信息,这个速度影响了复制的速度,进而影响暂停时间。
G1 比 ParallelOld 和 CMS 会需要更多的内存消耗,那是因为有部分内存消耗于簿记(accounting)上,如以下两个数据结构:
有个场景,老年代的对象可能引用新生代的对象,那标记存活对象的时候,需要扫描老年代中的所有对象。因为该对象拥有对新生代对象的引用,那么这个引用也会被称为GC Roots。那不是得又做全堆扫描?成本太高了吧。
HotSpot给出的解决方案是一项叫做卡表
(Card Table)的技术。该技术将整个堆划分为一个个大小为512字节的卡,并且维护一个卡表,用来存储每张卡的一个标识位。这个标识位代表对应的卡是否可能存有指向新生代对象的引用。如果可能存在,那么我们就认为这张卡是脏的。
在进行Minor GC的时候,我们便可以不用扫描整个老年代,而是在卡表中寻找脏卡,并将脏卡中的对象加入到Minor GC的GC Roots里。当完成所有脏卡的扫描之后,Java虚拟机便会将所有脏卡的标识位清零。
想要保证每个可能有指向新生代对象引用的卡都被标记为脏卡,那么Java虚拟机需要截获每个引用型实例变量的写操作,并作出对应的写标识位操作。
卡表能用于减少老年代的全堆空间扫描,这能很大的提升GC效率。
我们可以看下官方文档对G1的展望(这段英文描述比较简单,我就不翻译了):
Future:
G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS). Comparing G1 with CMS, there are differences that make G1 a better solution. One difference is that G1 is a compacting collector. G1 compacts sufficiently to completely avoid the use of fine-grained free lists for allocation, and instead relies on regions. This considerably simplifies parts of the collector, and mostly eliminates potential fragmentation issues. Also, G1 offers more predictable garbage collection pauses than the CMS collector, and allows users to specify desired pause targets.
ZGC(Z Garbage Collector)作为一种比较新的收集器,目前还没有得到大范围的关注。作为一款低延迟的垃圾收集器,它有如下几个亮点:
在ZGC中,连逻辑上的也是重新定义了堆空间(不区分年轻代和老年代),只分为一块块的page,每次进行GC时,都会对page进行压缩操作,所以没有碎片问题。虽然ZGC属于很新的GC技术, 但优点不一定真的出众,ZGC只在特定情况下具有绝对的优势, 如巨大的堆和极低的暂停需求
。而实际上大多数开发在这两方面都不太成问题(尤其是在服务器端), 而对GC的性能/效率更在意。也有一种观点认为ZGC是为大内存、多cpu而生,它通过分区的思路来降低STW。
ZGC在JDK14前只支持Linux, 从JDK14开始支持Mac和Windows。
可以从官网看下ZGC的Change Log:
<TBD>
可以看出ZGC未来可期,让我们拭目以待吧。
查了下度娘有关G1的文章,绝大部分文章对G1的介绍都是停留在JDK7或更早期的实现很多结论已经存在较大偏差了,甚至一些过去的GC选项已经不再推荐使用。举个例子,JDK9中JVM和GC日志进行了重构,如PrintGCDetails已经被标记为废弃,而PrintGCDateStamps已经被移除,指定它会导致JVM无法启动。
本文对CMS和G1的介绍绝大部分内容也是基于JDK7,新版本中的内容有一点介绍,倒没做过多介绍,后面有机会可以再出专门的文章来重点介绍。通过分析CMS、G1的优缺点,就能更清楚ZGC的由来及优势,特别是停顿时间不超过10ms,是不是已经摩拳擦掌,跃跃欲试了?
《深入理解Java虚拟机》
《HotSpot实战》
https://wiki.openjdk.java.net/display/zgc/Main
https://plumbr.io/handbook/garbage-collection-algorithms-implementations#g1
https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html
翻译结果为没错,gym里没有依赖reward_threshold的代码。它本质上是环境的外部用户可以使用的元数据。尽管如此,它仍然可用于跨不同环境的奖励规范化,或者用于计算“在所有环境中,我的新算法设法解决了多少”的聚合统计信息。这里的Location和上文的不同是因为我在python默认环境和Anaconda中都装有gym,因此在找位置时要。很多教程都是在已有的gym环境文件夹下,为了方便我选择新建一个文件夹来保存我搭建的gym环境。至此,环境的注册就完成了,可以使用自定义的环境了。......_gym环境搭建
苏佳园:二维热传导方程有限差分法的MATLAB实现l所构成,其中xj?j?x?h,?x?h?J;tn?n?t?n?.3.2插值函数的选择选择不同的插值函数对偏微分方程进行估计,可得到不同的差分方程,进而稳定性和精度会有所不同。用Taylor级数展开方法是最常用的方法,下面建立差分格式的同时引入一些基本概念及术语。我们主要从对流方程的初值问题?u??u?a?0,x?R,t?0,? ..._苏佳园:二维热传导方程有限差分法的matlab实现
开门见山 ,初始代码及效果 ↓<!DOCTYPE html><html><head><meta charset="utf-8"> <style> .a{ height:180px; width:100%; background-color:#FFEBCD;}.b{}.c{ height:20px; width:35px; background-color:blue;}</style></head&g
/*** Difficult the first time, easy the second.** @author xulin* @description:* @date :2019/10/12*/public class LxTimeLineView extends AppCompatImageView {private int itemCount;//Item总数量private int it..._android 自定义view画一个0到24时间轴
tcpdump原理是注册一种虚拟协议,使得每个包在遍历当前的协议列表时有机会被处理,如果tcpdump发现包符合要求就会使用skb_clone一份,送到用户态程序去分析。tcpdump还会使网络设备进入混杂模式,是为了让包能过进入网络协议栈,从而有机会被捕捉,这样才能抓到包。_tcpdump混杂模式
我先在一个component里draw了一个image1,是个实体图象;然后用component的createImage(image1.getWidth,image1.getHeight)造了个image2,也在paint(g)draw了然后调用image2.getgraphi...我先在一个component里draw 了一个image1,是个实体图象;然后 用component的createI..._java image.getgraphics()
有关软件工程师职业发展的文章并不少,甚至可以说是泛滥。很多人都能在这个话题上说两句,三五年工作经验的编程老鸟、架构师、技术VP、CTO,都有各自的看法与实践经验。没有哪一套方法是适用于所有人的,这一套软件工程师职业发展纲要,也不过是在你踽踽前行的路上,迷茫时可用来参考借鉴。你的核心竞争力,永远..._软件工程职业发展路径
讲解:COMP 321、fdopen、c/c++、c/c++Java|RCOMP 321April 24, 2019Questions on this exam may refer to the textbook as well as to the manual pages on CLEAR.Therefore, you should not start the exam until you ar..._compto
如果您正在浏览新的台式机或笔记本电脑,它可能会包含最新版本的 Windows。哎呀,你甚至可以使用基于 Windows 的 PC 阅读这篇文章。当然,如果您是专门的 Apple 客户,您可能会使用基于 MacOS 的 PC。它们是目前主导台式机和笔记本电脑市场的两大主要操作系统。Linux 是一个基于 UNIX 的免费开源操作系统(OS),由 Linus Torvalds 于 1991 年创建。用户可以修改和创建计算机和其他设备的源代码变体,称为分发。最常见的用途是作为服务器,但 Linux 也用于_linux未来收费吗
1、窗口界面上添加一个Spin Control控件、添加一个Edit Control控件;如图2、设置Spin Control控件属性如下;a、Alignlent - Right Alient//设置绑定控件时的对齐方式b、Arrow keys - True //设置控件按键是否增减 c、Set Buddy Interger- Tru..._spin control设置关联
虽然我知道你们都在用IDEA,但我还是要发这篇文章~先上github链接:https://github.com/supernova-explosion/translate看源码时,注释里经常遇到不认识的单词,各种复制粘贴查询,不胜其烦。虽然各大翻译软件都有选择查词之类的功能,但是,一来不想装太多软件,二来有时候选择文本只是习惯性操作并不是想查词,所以只能从eclipse插件入手了。但是eclipse插件市场里一个也找不到,github上虽然能搜到几个,可我没找到能用的。没办法,只能自己写了。然后_eclipse 翻译插件
复合索引!有一次设计mysql索引的时候,无意中发现索引名字可以任取,可以选择几个字段进来,这有什么用呢?开始的selectidfromcollectorderbyidlimit90000,10;这么快就是因为走了索引,可是如果加了where就不走索引了。抱着试试看的想法加了search(vtype,id)这样的索引。然后测试selectidfromcollectwherevtype=1limit..._逻辑分页 collet