JVM垃圾回收机制及算法
JVM垃圾回收机制
- 在Java中,程序员是不需要像C++那样显示的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
什么是垃圾
- 没有任何引用指向的一个对象或者多个对象(循环引用)
如何定位垃圾
- 引用计数(ReferenceCount)
为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数-1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题。 - 根可达算法(RootSearching)
从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
有哪些可以作为GC Roots的对象
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
常见JVM 垃圾回收算法
标记清除(mark sweep)
优点:实现简单,不需要对象进行移动。
缺点:标记、清除过程效率低,产生大量不连续的内存碎片,提高了垃圾回收的频率。复制算法 (copying)
优点:按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片。
缺点:可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制。标记压缩(mark compact)
优点:解决了标记-清理算法存在的内存碎片问题。
缺点:仍需要进行局部对象移动,一定程度上降低了效率分代算法
根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。而永久代(1.7)空间不足会触发一次Full GC,正确的永久代大小对避免Full GC是非常重要。1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使⽤本地内存。
新生代、老年代、永久代(元空间)的区别
除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型
G1是逻辑分代,物理不分代
除此之外不仅逻辑分代,而且物理分代
- 新生代 + 老年代 + 永久代(1.7)Perm Generation/ 元数据区(1.8) Metaspace
- 永久代 元数据 - 存放Class对象相关信息
- 永久代必须指定大小限制 ,元数据可以设置,也可以不设置,无上限(受限于物理内存)
- 字符串常量 1.7 - 永久代,1.8 - 堆
- MethodArea逻辑概念 - 永久代、元数据是实现
- 新生代和老年代空间默认比例为1:2
- 新生代 = Eden + 2个suvivor区
- YGC回收之后,大多数的对象会被回收,活着的进入s0
- 再次YGC,活着的对象eden + s0 -> s1
- 再次YGC,eden + s1 -> s0
- 年龄足够 -> 老年代 (默认15 CMS 6)
- s区装不下 -> 老年代
- 老年代
- 顽固分子
- 老年代满了Full GC
- 永久代就是JVM的方法区。更不容易被回收
Minor GC、Major GC、Full GC、Mixed GC
- Minor GC是新生代GC,指的是发生在新生代的垃圾收集动作。由于java对象大都是朝生夕死的,所以Minor GC非常频繁,一般回收速度也比较快。(一般采用复制算法回收垃圾)
- Major GC是老年代GC,指的是发生在老年代的GC,通常执行Major GC会连着Minor GC一起执行。Major GC的速度要比Minor GC慢的多。(可采用标记清楚法和标记整理法)
- Full GC是清理整个堆空间,包括年轻代和老年代
- Mixed GC: 清理整个新生代以及部分老年代的GC,只有G1有这个模式
常见的垃圾回收器
Serial
Serial是新生代的垃圾回收器,收集工作是单线程的,并且在垃圾收集过程中,其他线程阻塞,进入SWT状态。新生代使用的 Serial 垃圾回收器,是基于复制算法的。
Serial Old
Serial Old 是在 老年代上实现收集的,Serial Old 收集器所使用的垃圾回收算法是标记压缩或标记清理算法。
优缺点:
Serial和Serial Old垃圾回收器是单线程回收垃圾的,没有线程交互的开销,所以可以获得很高的单线程收集效率,适用于单机场景。同时也适合内存资源受限的环境,因为Serial和Serial Old垃圾回收器是所有垃圾回收器中额外内存消耗最少的。因为只有一个单独线程来清理垃圾所以停顿时间就会比较长,对于现代处理器来说,已经不适用。适用于几十兆的内存。
Parallel Scavenge
Parallel Scavenge 收集器是一个新生代收集器,采用复制算法,并且是多线程垃圾回收,他更加关注吞吐量而不是更短的响应时间。高吞吐量可以最高效率的利用 CPU 时间。尽快完成程序的运算任务。
值得关注的是 Parallel Scavenge 收集器有个自适应调节参数-XX:UseAdaptiveSizePolic,开启后虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整Eden区大小,比例,晋升老年代对象大小等参数以提供最合适的停顿时间或者最大的吞吐量。
Parallel Old
Parallel Old收集器是老年代的垃圾回收器,使用多线程和标记整理算法。在注重吞吐量以及CPU资源敏感的场合,可以优先考虑Parallel Scavenge 加 Parallel Old 收集器。
优缺点:
Parallel Scavenge和Parallel Old是多线程回收垃圾的,着重关心吞吐量。是jdk8 当前默认的垃圾回收器,适用于上G的内存。
ParNew
ParNew 就是一个 Serial 的多线程版本,适合多核服务器,通常与 CMS 收集器搭配使用。CMS 作为老年代收集器时,新生代收集器默认就是 ParNew。
CMS(Concurrent Mark Sweep) 收集器
CMS垃圾回收器分为四个阶段
1. 初始标记:根节点枚举,枚举全部的GC Roots对象。此阶段会STW,但是耗时极短。
2. 并发标记:从标记的GC Roots节点开始根据引用遍历,访问到的对象说明是可达的,访问不到的对象说明是不可达的可以被GC。
3. 重新标记:用户线程和CMS线程是并发的,可能会导致某些对象改变,因此重新标记改变的对象.采用三色标记算法。(SWT)
4. 并发清除:清理标记的垃圾
这是一款承上启下的垃圾回收器,虽然没有任何一个版本的jdk将它作为默认的垃圾回收器,但是不可以否认他的重要性。他是首款并发垃圾回收器。可以做到并发收集、低停顿等优点,但是因为对老年代采用了并发清除的算法,所以会产生大量空间碎片,导致提前的full GC。
G1(Garbage First) 垃圾回收器
G1 本质上是一个分带垃圾回收器,相对 CMS 垃圾回收器,有两个改进:
1. 基于标记-整理 算法,不产生内存碎片。
2. 可以准确的控制停顿时间,在不牺牲吞吐的情况下实现低停顿的垃圾回收。(-XX:MaxGCPauseMillis)
G1的YGC采用复制算法,MIXGC:
1. 初始标记:仅标记GC roots能直接关联到的对象以及所在的region,该步骤需要STW,但是时间极短。
2. 查看old区region的所有Rset在,是否有RootRegion的引用。
3. 并发标记:从GC roots使用可达性算法,对堆内存中对象进行标记(三色标记法)。该步骤耗时过长,是与用户业务程序并发执行。(比CMS时间短,因为只要遍历RootRegion有引用的区而不是整个old区)
4. 重新标记:对用户线程短暂的STW,用于处理并发标记中对象的一些变更情况的最终确定。(SATB)
5. 筛选回收(只选垃圾较多的region):G1会根据用户规定的停顿时间,最大的回收region,将需要被回收的region复制到空白region中,再将原region全部回收。该步骤也需要STW,因为涉及到了对象的移动(一个region到另一个region)。
Rset
每个region区都会有一个区域专门存放Rset,他是用来存储其他region区中引用当前region区中对象的记录。
Cset
本期清理需要清理的region区集合
card table
hotspot对Rset的具体实现
ZGC(分区模型)
GC中使用了复制算法和标记整理算法,使用的场景不同,复制算法如果在小页面中旁边有个空白的内存块,就可以使用复制算法进行整理。如果没有多余的内存块,就只能使用标记整理算法。
ZGC 有 3 个重要特性:
1. 暂停时间不会超过 10 ms - STW
2. 最大支持 16TB 内存,最小支持 8MB 的内存
3. 跟 G1 相比,对应用程序吞吐量的影响较小
与其他垃圾回收算法不同的是,ZGC采用读屏障的机制,来保证垃圾回收的正确性。
ZGC垃圾回收过程
1. 初始标记:从 GC Roots 出发,找出 GC Roots 直接引用的对象,这个过程需要 STW,耗时比较短。
2. 并发标记(三色标记+读屏障):GC 线程标记线程访问对象时,如果对象标志位是 Remapped,就把对象地址视图切换到 Marked0,如果对象地址视图已经是 Marked0,说明已经被其他标记线程访问过了,跳过不处理。标记过程中业务线程新创建的对象会直接进入 Marked0 视图,业务线程访问对象时,如果对象的地址视图是 Remapped,就把对象地址视图切换到 Marked0(读屏障),标记结束后,如果对象地址视图是 Marked0,那就是活跃的,如果对象地址视图是 Remapped,那就是不活跃的。(这里采用两个视图是为了区分前一次标记和这一次标记。如果这次标记的视图是 Marked0,那下一次并发标记就会把视图切换到 Marked1
3. 再标记(处理三色标记的漏标):并发标记阶段 GC 线程和 业务线程并发执行,标记过程中可能会有引用关系发生变化而导致的漏标记问题。再标记阶段重新标记并发标记阶段发生变化的对象,还会对非强引用(软应用,虚引用等)进行并行标记。这个阶段需要 STW,但是需要标记的对象少,耗时很短。
4. 并发转移准备:进行分区选择,选择性回收。
5. 初始转移:把活跃对象复制到新的内存,之前的内存空间可以被回收。初始转移需要扫描 GC Roots 直接引用的对象并进行转移,这个过程需要 STW,STW 时间跟 GC Roots 成正比。
6. 并发转移:并发转移过程 GC 线程和业务线程是并发进行的,转移过程中对象视图会被切回 Remapped 。如果 GC 线程访问对象是 Marked0,则转移对象,并把对象设置成 Remapped。如果 GC 线程访问对象是 Remapped,说明被其他 GC 线程处理过,跳过不再处理。并发转移过程中业务线程创建的新对象地址视图是 Remapped。如果 Java 应用线程访问的对象被标记为活跃并且是 Marked0,则转移对象,并把对象设置成 Remapped。
6. 重定位:转移过程对象的地址发生了变化,在这个阶段,把所有指向对象旧地址的指针调整到对象的新地址上。(下次GC)
转移过程中使用转发表记录新旧地址对应关系。
ZGC 也有一个缺点,就是浮动垃圾。因为 ZGC 没有分代概念,在这个过程中 用户线程可能会创建大量的新对象,这些对象会成为浮动垃圾,只能等下次 GC 的时候进行回收。
三色标记算法
在并发标记过程中,我们采用三色标记算法来标记对象,把每个对象,按照是否访问过标记成三种颜色:
- 白色:还没有被垃圾回收器访问。
- 黑色:已经被垃圾回收器访问,并且这个对象的所有引用也都被垃圾回收器访问过。
- 灰色:已经被垃圾回收器访问,但是这个对象至少还有一个引用没有被垃圾回收器访问。
三色标记算法会产生两个问题
- 在标记过程中,用户线程还会继续执行。如果一个对象标记为灰色,用户线程删除了所有指向此对象的引用,那么这个对象已经是垃圾了,但本次GC却不会回收这个垃圾,这样的垃圾被称作浮动垃圾,只能在下一次GC时清理。这种产生的问题补是很大。
- 如果一个对象标记为不可达后,用户线程却让这个对象变得可达,那么会把原本存活的垃圾回收,这样的话访问这个对象肯定会发生错误。这种错误将会是严重的,会造成空指针异常等。
问题产生原因
同时满足以下两个才会产生问题
1. 用户线程将黑色对象指向白色对象的引用
2. 用户线程删除了灰色对象到这个白色对象的引用
所以只需要破坏其中一个条件就可以将问题解决:
1. CMS采用的是增量更新(Incremental Update):破坏条件一,当黑色对象插入指向白色对象的引用后,采用写屏障(write barrier)把黑色对象重新标记为灰色,等待下一轮重新标记。
2. G1采用的是原始快照(SATB):破坏条件二,灰色对象删除白色对象的引用后,采用写屏障把白色对象记录下来,等待下一轮重新标记。但是会产生浮动垃圾,但无关紧要,下次GC会清除该垃圾。
SATB
三色标记算法,为了解决漏标问题采用了增量更新的方法。但是还是有一种情况的漏标是增量更新解决不了的。就是在并发的情况下,当一个线程扫描对象A,对象A有索引:A->B,A->C,其中线程GC线程1,扫描完B在扫描C的状态中,此时有个业务线程2把索引B改动,改成A->D,把A设置为灰色,此时GC线程1把C扫描完了,把A设置为黑色。这时我们就发现黑色对象A,下面就会有一个白色对象D未扫描。那么这样的漏标如何解决呢?
SATB(原始快照)的意思是当B删除了N引用之后,B->N 的关系仍然被记录,这个动作通过一个写屏障来维护Rset。扫描结束之后再以B为根(被记录的灰色对象为根)重新扫描一次,此时的扫描的B->N的引用已经被重新记录了,即使他实际已经被删除但在这次扫描中它仍然存在。
着色指针
在ZGC 出现之前, GC 信息保存在对象头的 Mark Word 中,如下图所示:
ZGC将GC信息存放在染色指针上,实际上是对寄存器进行操作,这比先前的内存操作要快了不少。
在这个 64 位的指针上,高 16 位都是 0,暂时不用来寻址。剩下高 4 位保存了四个标志位,这样 ZGC 可以管理的最大内存可以达到 16 TB(2 ^ 44)。通过这四个标志位,JVM 可以从指针上直接看到对象的三色标记状态(Marked0、Marked1)、是否进入了重分配集(Remapped)、是否需要通过 finalize 方法来访问到(Finalizable)。
JVM垃圾回收机制及算法相关推荐
- JVM垃圾回收机制和算法
一.垃圾回收机制 1.为什么需要垃圾回收 Java 程序在虚拟机中运行,是会占用内存资源的,比如创建的对象.加载的类型数据等,而且内存资源都是有限的.当创建的对象不再被引用时,就需要被回收掉,释放内存 ...
- JVM架构、JVM垃圾回收机制、垃圾回收算法、垃圾回收器、JMM(内存模型)
0 JVM和Java的关系 JDK = JRE + Java开发工具(java,javac,javadoc,javap-) JRE = JVM + Java核心类库 即: JDK = JVM + Ja ...
- 【JVM】垃圾回收机制及算法
垃圾回收机制及算法 一.垃圾回收概述 二.对象是否存活 1. 判断对象是否存活 - 引用计数算法 2.判断对象是否存活-可达性分析算法 1.可达性分析算法 2.JVM之判断对象是否存活 3.关于引用 ...
- 4、JVM垃圾回收机制、新生代的GC、GC(Minor GC、FullGC)、GC日志、JVM参数选项、元空间(笔记)
4.JVM垃圾回收机制 4.1.新生代的GC 4.1.1.串行GC(SerialGC) 4.1.2.并行回收GC(Parallel Scavenge) 4.1.3.并行GC(ParNew) 4.2.G ...
- jvm垃圾回收机制_JVM 垃圾回收机制之堆的分代回收
JVM垃圾回收机制之堆的分代回收 前言 前文我们了解了Java的GC机制,对于堆中的对象,JVM采用引用计数和可达性分析两种算法来标记对象是否可以清除,本文中我们还会了解到JVM将对分成了不同的区域, ...
- GC:垃圾回收机制及算法
GC:垃圾回收机制及算法 关键词 算法:标记(清除/复制/整理).分代收集 收集器(Serial[串行].ParNew[并行].Parallel Scavenge[并行].Serial Old[串行] ...
- jvm对象从新生代到老年代_JVM内存管理、JVM垃圾回收机制、新生代、老年代以及永久代...
内存模型 JVM运行时数据区由程序计数器.堆.虚拟机栈.本地方法栈.方法区部分组成,结构图如下所示. JVM内存结构由程序计数器.堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 1)程序计数器 ...
- JVM内存区域(Java内存区域)、JVM垃圾回收机制(GC)初探
一.JVM内存区域(Java内存区域) 首先区分一下JVM内存区域(Java内存区域)和Java内存模型(JMM)的概念.Java线程之间的通信采用的是共享内存模型,这里提到的共享内存模型指的就是Ja ...
- 2.JVM垃圾回收机制-什么时候回收内存
在前面的文章中,我们介绍过JVM垃圾回收机制负责的是堆和方法区的内存. 参考:http://blog.csdn.net/u011983531/article/details/49227013 在本篇中 ...
最新文章
- 机器学习中的数学意义
- RxJava使用(四)变换
- 解决 IndyFTp 乱码问题 10.6.0
- mongo mysql 条件查询效率_mongodb查询条件对查询效率的影响
- C++ 虚函数和纯虚函数
- 我们再来玩游戏(博弈)
- Linux文件预读对系统的影响
- postgresql 不同数据库不同模式下的数据迁移
- Android面试收集录4 Fragment详解
- 复星旅文冲刺港交所:上半年营收66亿 核心收入来自度假村
- 性能测试中问题反思和心得
- pic单片机c语言計數,单片机教程:PIC单片机C语言程序设计(三)
- 【转载】Unity 项目管理与优化
- 盲盒是怎么赚钱的(拆解盲盒App背后的盈利逻辑)
- 和开源硬件相关的几个词,免费、山寨、创客教育,以及未来 | COSCon'18
- CSS3字体样式及高级特效
- 51单片机DS1302实时时钟
- 苹果虚拟home键_苹果手机几个实用小技巧,相见恨晚,快试试你的手机!
- CISP证书专栏 — CISP-IRE证书
- opencv教程CV2模块——图片处理,HSV、色调、亮度调节
热门文章
- java 异步上传一张图片_java异步上传图片
- 第三阶段---python基础---抽签点名案例
- 编写c语言的开篇——Hello World
- 编程时拼音输入法的设置
- android变身蓝牙键鼠,使用BluetoothHidDevice将安卓手机同时模拟成鼠标和键盘
- C语言 before string,c语言中expected expression before是什么意思?
- 二进制LDPC码的构造及译码算法
- 《看聊天记录都学不会C语言?太菜了吧》(1)我在大佬群里问基础问题没人理?
- html如何查看json数据,浏览器如何查看json格式的数据?查看方法分享
- linux上项目运行日志导致磁盘空间不够的问题