目录

垃圾回收

可达性分析算法

GC Roots

Stop The World

安全点

安全区域

能够找到 Reference Chain 的对象,就一定会存活么?

垃圾回收算法

标记-清除算法

复制算法

标记-整理算法

分代收集算法

垃圾收集器

Serial

ParNew

Serial Old

Parallel Old

CMS

G1

ZGC


垃圾回收

垃圾回收(Garbage Collection,GC)的主要目的是清除不再使用的对象,自动释放内存。GC主要管理的是堆内存。


可达性分析算法

通过一系列称为GC Roots 的对象作为起始点,从这些节点开始向下搜索,搜索所走的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,该对象将被回收。


GC Roots

GC Roots一般为:

  • 类静态属性中引用的对象;
  • 常量引用的对象;
  • 虚拟机栈中引用的对象;
  • 本地方法栈中引用的对象。

Stop The World

可达性分析的执行需要一个确保一致性的快照下进行,即对象引用关系达到稳定,因此必须停顿所有java线程,这种操作称为Stop The World,简称 STW。


安全点

Stop The World 需要java线程到达安全点后执行。安全点(Safe Point)可以是方法调用、循环跳转、异常跳转等。


安全区域

安全区域指一段代码片段中,引用关系不会发生变化,在这个区域任意地方开始GC都是安全的。


能够找到 Reference Chain 的对象,就一定会存活么?

Java四种引用类型


垃圾回收算法

标记-清除算法

“标记-清除”(Mark-Sweep)算法,算法分为“标记”和“清除”两个阶段。首先根据GC Roots标记出所有可达对象,然后统一回收掉所有不可达对象。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。

它的主要缺点有两个:

一、效率问题。标记和清除过程的效率都不高;

二、空间碎片问题。标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。


复制算法

“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。

缺点:这种算法的代价是将内存缩小为原来的一半,持续复制长生存期的对象则导致效率降低。


标记-整理算法

复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。

根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。


分代收集算法

“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

具体回收过程:一个对象的一生


垃圾收集器

垃圾收集器的关注点:

  1. 停顿时间:停顿时间越短就适合需要与用户交互的程序,提升用户体验。
  2. 吞吐量:高吞吐量则可以高效率地利用CPU时间,尽快完成运算的任务,主要适合在后台计算而不需要太多交互的任务。

吞吐量 = 运行用户代码时间 /(运行用户代码时间+垃圾收集时间)


Serial

特点:

  1. 针对新生代。
  2. 采用复制算法。
  3. 单线程收集。

使用场景:

依然是HotSpot在Client模式下默认的新生代收集器。对于限定单个CPU的环境中,Serial收集没有线程切换开销,简单高效。


ParNew

ParNew垃圾收集器是Serial收集器的多线程版本。

在Server模式下,ParNew收集器是一个非常重要的收集器,因为除Serial外,目前只有它能与CMS收集器配合工作。 但在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销。

特点:

  1. 新生代收集器。
  2. 采用复制算法。
  3. 多线程收集。
  4. CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达一个可控制的吞吐量。

使用场景:

以高吞吐量为目标,即减少垃圾收集时间,让用户代码获得更长的运行时间。当应用程序运行在具有多个CPU上,对暂停时间没有特别高的要求时,即程序主要在后台进行计算,而不需要与用户进行太多交互。

例如批量处理、订单处理、工资支付、科学计算等应用程序。


Serial Old

特点:

  1. 针对老年代。
  2. 采用标记-整理算法。
  3. 单线程收集。

使用场景:

  1. 主要用于Client模式。
  2. Server模式作为CMS收集器的后备预案。

ParNew/Serial Old收集器运行示意图:


Parallel Old

特点:

  1. 针对老年代。
  2. 采用标记-整理算法。
  3. 多线程收集。
  4. 是Parallel Scavenge收集器的老年代版本,JDK1.6开始提供。

使用场景:

Server模式,多CPU的情况下,注重吞吐量以及CPU资源敏感的场景,就有了Parallel Scavenge加Parallel Old收集器的"给力"应用组合。

Parallel Scavenge/Parallel Old收集器运行示意图:


CMS

特点:

1. 针对老年代。

2. 采用标记清除算法(为了节省时间,不进行整理,会产生内存碎片,可配置成周期性整理)。

3. 以获得最短停顿时间为目标。

4. 并发收集,低停顿。

5. 需要更多的内存。

使用场景:

与用户交互较多的场景,希望系统停顿时间最短,注重服务的响应速度。如常见WEB、B/S系统的服务器上的应用。

CMS收集器工作过程:

  1. 初始标记:仅标记一下GC Roots能直接关联到的对象,不用向下追溯,速度很快,但需要"Stop The World";
  2. 并发标记:进行GC Roots Tracing的过程, 这个过程相对耗时,但却可以和用户线程并行。在这个阶段的执行过程中,有些对象可能又产生了变化,它们会被标记为dirty,用于后续重新标记处理,这些变化可能是:
    1. 有些对象,从新生代晋升到了老年代;
    2. 有些对象,直接分配到了老年代;
    3. 老年代或者新生代的对象引用发生了变化。
  3. 重新标记:为了修正并发标记期间因用户程序继续运作而导致标记变动的dirty记录,需要"Stop The World"进行重新标记,且停顿时间比初始标记稍长,但远比并发标记耗时短, 采用多线程并行执行来提升效率。
  4. 并发清除:回收所有的垃圾对象,整个过程中耗时最长的并发标记和并发清除都可以与用户线程一起工作。

CMS收集器运行示意图:

 缺点:

  1. 并发收集虽然不会暂停用户线程,但因为占用一部分CPU资源,还是会导致应用程序变慢,总吞吐量降低。
  2. 无法处理浮动垃圾: 在并发清除时,用户线程新产生的垃圾,称为浮动垃圾,这使得并发清除时需要预留一定的内存空间,不能像其他收集器在老年代几乎填满再进行收集,也要可以认为CMS所需要的空间比其他垃圾收集器大。无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败。
  3. Concurrent Mode Failure时,JVM启用后备预案:临时启用Serail Old收集器,而导致另一次Full GC的产生,这样的代价是很大的。
  4. 产生大量内存碎片。由于CMS基于"标记-清除"算法,清除后不进行压缩操作,但CMS提供了参数CMSFullGCsBeforeCompaction,表示每隔多少次不压缩的 Full GC 后,执行一次带压缩的 Full GC,默认是0,表示表示每次进入 Full GC 时都进行碎片整理。

CMS与Parallel Old的比较:

总体来看,与Parallel Old垃圾收集器相比,CMS减少了执行老年代垃圾收集时应用暂停的时间, 但却增加了新生代垃圾收集时应用暂停的时间、降低了吞吐量而且需要占用更大的堆空间。


G1

特点:

  1. 并行与并发:能充分利用多CPU、多核环境下的硬件优势, 可以并行来缩短"Stop The World"停顿时间,也可以并发让垃圾收集与用户程序同时进行。
  2. 分代收集,收集范围包括新生代和老年代 。能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配,能够采用不同方式处理不同时期的对象。将整个堆划分为多个大小相等的独立区域(Region),新生代和老年代不再是物理隔离,它们都是一部分Region(不需要连续)的集合。
  3. 结合多种垃圾收集算法,空间整合,不产生碎片。从整体看,是基于标记-整理算法,从局部(两个Region间)看,是基于复制算法。这是一种类似火车算法的实现,都不会产生内存碎片,有利于长时间运行。
  4. 可预测的停顿:低停顿的同时实现高吞吐量。可以明确指定M毫秒时间片内,垃圾收集消耗的时间不超过N毫秒。
  5. 垃圾最多的Region,会被优先收集,这也是 G1 名字的由来。

使用场景:

面向服务端应用,针对具有大内存、多处理器的机器。最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案。如果现在采用的收集器没有出现问题,不用急着去选择G1。

为什么G1收集器可以实现可预测的停顿?

可以有计划地避免在Java堆的进行全区域的垃圾收集。G1跟踪各个Region获得其收集价值大小,在后台维护一个优先列表。每次根据允许的收集时间,优先回收价值最大的Region(名称Garbage-First的由来)。这就保证了在有限的时间内可以获取尽可能高的收集效率。

一个对象被不同区域引用的问题:

  • 无论G1还是其他分代收集器,JVM都是使用Remembered Set来避免全局扫描。
  • 每个Region都有一个对应的Remembered Set。
  • 每次Reference类型数据写操作时,都会检查将要写入的引用指向的对象是否和该Reference类型数据在不同的Region。
  • 如果不同,就把相关引用信息记录到引用指向对象的所在Region对应的Remembered Set中。
  • 当进行垃圾收集时,在GC根节点的枚举范围加入Remembered Set,就可以保证不进行全局扫描,也不会有遗漏。

G1收集器运作过程:

不计算维护Remembered Set的操作,可以分为4个步骤(与CMS较为相似)。

  1. 初始标记:仅标记一下GC Roots能直接关联到的对象,且修改TAMS(Next Top at Mark Start),让下一阶段并发运行时,用户程序能在正确可用的Region中创建新对象。需要"Stop The World",但速度很快。
  2. 并发标记:进行GC Roots Tracing的过程, 刚才产生的集合中标记出存活对象,耗时较长,但应用程序也在运行。并不能保证可以标记出所有的存活对象。
  3. 最终标记:为了修正并发标记期间因用户程序继续运作而导致标记变动的那一部分对象的标记记录。需要"Stop The World",且停顿时间比初始标记稍长,但远比并发标记短。采用多线程并行执行来提升效率。
  4. 筛选回收:首先排序各个Region的回收价值和成本,然后根据用户期望的GC停顿时间来制定回收计划,最后按计划回收一些价值高的Region中垃圾对象。 回收时采用复制算法,从一个或多个Region复制存活对象到堆上的另一个空的Region,并且在此过程中压缩和释放内存。可以并发进行,降低停顿时间,并增加吞吐量。

G1收集器运行示意图:


ZGC

ZGC是JDK11新加入的低延迟收集器,它立下了三个令人期待的flag:

  1. 停顿时间不会超过 10ms;
  2. 停顿时间不会随着堆的增大而增大(不管多大的堆都能保持在 10ms 以下);
  3. 可支持几百 M,甚至几 T 的堆大小(最大支持 4T)。

它具有以下特点:

  1. ZGC 使用了着色指针技术,我们知道 64 位平台上,一个指针的可用位是 64 位,ZGC 限制最大支持 4TB 的堆,这样寻址只需要使用 42 位,那么剩下 22 位就可以用来保存额外的信息,着色指针技术就是利用指针的额外信息位,在指针上对对象做着色标记。
  2. 第二个特点是使用读屏障,ZGC 使用读屏障来解决 GC 线程和应用线程可能并发修改对象状态的问题,而不是简单粗暴的通过 STW 来进行全局的锁定。使用读屏障只会在单个对象的处理上有概率被减速。
  3. 由于读屏障的作用,进行垃圾回收的大部分时候都是不需要 STW 的,因此 ZGC 的大部分时间都是并发处理,也就是 ZGC 的第三个特点。
  4. 第四个特点是基于 Region,这与 G1 算法一样,不过虽然也分了 Region,但是并没有进行分代。ZGC 的 Region 不像 G1 那样是固定大小,而是动态地决定 Region 的大小,Region 可以动态创建和销毁。这样可以更好的对大对象进行分配管理。
  5. 第五个特点是压缩整理。CMS 算法清理对象时原地回收,会存在内存碎片问题。ZGC 和 G1 一样,也会在回收后对 Region 中的对象进行移动合并,解决了碎片问题。

回收过程:

  1. 初始标记:ZGC 首先会进行一个短暂的 STW,来进行 roots 标记。这个步骤非常短,因为 roots 的总数通常比较小。
  2. 并发标记:通过对对象指针进行着色来进行标记,结合读屏障解决单个对象的并发问题。其实,这个阶段在最后还是会有一个非常短的 STW 停顿,用来处理一些边缘情况,这个阶段绝大部分时间是并发进行的,所以没有明显标出这个停顿。
  3. 并发清理:这个阶段会把标记为不在使用的对象进行回收。
  4. 重定位:对 GC 后存活的对象进行移动,来释放大块的内存空间,解决碎片问题。重定位最开始会有一个短暂的 STW,用来重定位集合中的 root 对象。暂停时间取决于 root 的数量、重定位集与对象的总活动集的比率。
  5. 并发重定位:这个过程也是通过读屏障,与应用线程并发进行的。

JVM 垃圾回收简介相关推荐

  1. Java GC系列(1):Java垃圾回收简介

    转载自  Java GC系列(1):Java垃圾回收简介 这篇教程是系列第一部分.首先会解释基本的术语,比如JDK.JVM.JRE和HotSpotVM.接着会介绍JVM结构和Java 堆内存结构.理解 ...

  2. JVM垃圾回收系列--垃圾回收器的详解/对比

    原文网址:JVM垃圾回收系列--Java垃圾回收器的详解/对比_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Java的垃圾回收器,包括:JDK默认的垃圾回收器.CMS与G1的区别.年轻代回收器.老 ...

  3. 深入理解JVM虚拟机2:JVM垃圾回收基本原理和算法

    JVM GC基本原理与GC算法 Java的内存分配与回收全部由JVM垃圾回收进程自动完成.与C语言不同,Java开发者不需要自己编写代码实现垃圾回收.这是Java深受大家欢迎的众多特性之一,能够帮助程 ...

  4. JVM垃圾回收系列--内存模型/垃圾回收流程

    原文网址:JVM垃圾回收系列--内存模型/垃圾回收流程_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Java各个代的关系(内存模型)及垃圾收集流程. 内存模型 JDK8的内存模型 在Java中所有 ...

  5. 老李分享:jvm垃圾回收

    老李分享:jvm垃圾回收 1.垃圾收集算法核心思想 java语言建立了垃圾回收机制,用于跟踪正在被使用(引用)的对象和没有被使用(引用)的对象,该机制可以有效防范动态内存分配中可能发生的两个危险:因垃 ...

  6. JVM垃圾回收算法 总结及汇总

    先看一眼JVM虚拟机运行时的内存模型: 1.方法区 Perm(永久代.非堆) 2.虚拟机栈 3.本地方法栈 (Native方法) 4.堆 5.程序计数器 1 首先的问题是:jvm如何知道那些对象需要回 ...

  7. JVM 垃圾回收算法 -可达性分析算法!!!高频面试!!!

    前言:学习JVM,那么不可避免的要去了解JVM相关的垃圾回收算法,本文只是讲了讲了可达性分析算法,至于标记-清除.标记-复制,标记-整理,分代收集等等算法,会在近两天的文章中陆续更新出来. 很喜欢一句 ...

  8. java学习笔记-4 JVM垃圾回收(GC)

    引言 jvm垃圾回收相关的问题是老生常谈的问题了,相信大家都有所了解,这里再进行相关的探讨,以加深理解.若文中有不正之言,望不吝指正. 本文将围绕以下几个点展开 1.为什么要进行垃圾回收 我们知道jv ...

  9. jvm - 垃圾回收 gc

    2019独角兽企业重金招聘Python工程师标准>>> jvm - 垃圾回收 注意 : 本系列文章为学习系列,部分内容会取自相关书籍或者网络资源,在文章中间和末尾处会有标注 垃圾回收 ...

最新文章

  1. 从源码分析DEARGUI之动态绘图的两种方法
  2. doctype的三种类型
  3. 【百战GAN】新手如何开始你的第一个生成对抗网络(GAN)任务
  4. java------线程同步方法
  5. 微信小程序-当用户拒绝了位置信息的请求时,提醒用户去设置地理位置授权
  6. SAP CRM Long text unit test preparation
  7. SQL语言基础:数据库语言概念介绍
  8. 2字节取值范围_高中数学:构造不等式,解析几何范围题的有效解法
  9. 计算机组成原理 唐朔飞 思维导图
  10. ucore_os_lab lab1 report
  11. TZOJ--5447: Irrational Division (博弈)
  12. 如何批量获取高德地图的商家信息
  13. jQuery-动画效果(图片抽奖案例)
  14. 卫青和霍去病:汉匈战争史最天才的两名战将
  15. 接着外挂教程 VB 从零开始编外挂
  16. jsp 实验室管理系统Myeclipse开发mysql数据库web结构jsp编程计算机网页项目
  17. 用Python读红楼梦之——一、词云绘制
  18. 快速搭建一个MyBatis项目
  19. Rpgmakermv(16) YEP MainmenuManager
  20. 儿童餐椅CPC认证亚马逊要求ASTM F404测试所需资料流程

热门文章

  1. ie input兼容 vue_IE浏览器兼容问题(基于vue)
  2. MySql 高级查询强化学习
  3. 网站增加百度收录最有效的方法!!!!!!
  4. win10无法连接windows服务器,无法连接SENS服务
  5. 计算机主机箱工作电流,电脑使用常识
  6. hrbust 1313 火影忍者之~静音【优先队列STL+模拟】
  7. JS异步:执行原理与回调
  8. css3的媒体查询(Media Queries)
  9. WireShark的过滤语法
  10. 编写一个程序,提示用户输入以兆位每秒(Mb/s)为单位的下载速度和以兆字节(MB)为单位的文件大小。程序中应计算文件的下载时间。