共计 5656 个字符,预计需要花费 15 分钟才能阅读完成。
摘要
前一节,咱们解说了内存回收的方法论,从根本的回收对象是否存活引入了: 间接援用计数法(对象增加援用计数器)、可达性分析法(援用对象从 GC Root 登程。通过援用链查找),正因为间接援用计数法无奈解决循环援用问题,引入可达性分析法。而后引入垃圾对象回收的算法方法论:最简略的是“标记 - 革除法”: 应用可达性分析法将内存对象标记为垃圾对象,而后革除垃圾对象,标记 - 革除法在对象存活工夫比拟长的内存区域效率低下: 会耗费大量工夫标记革除大量内存对象,标记革除法革除垃圾对象的时候会导致不间断的内存空间,引出空间碎片化问题。所以为了解决下面问题引出了标记 - 复制算法: 将空间分为两局部: 正在应用的其中一块空间,当对象标记为垃圾对象,须要登程 GC 时候,将存对象一次性移到另一块内存, 并且应用挪动内存指针形式让另一块内存空间间断话,而后 gc 掉第一块所有内存, 解决了内存碎片化问题,在内存对象存活周期短的区域,只须要挪动大量的存活对象到另一块区域,效率高,所以适宜在堆内存外面的新生代;对于老年代;咱们须要引出:标记 - 整顿算法:将垃圾对象进行标记, 而后 gc 时候通过指针将内存对象间断化。这一讲咱们来钻研具体的内存回收实现: 垃圾回收器。
咱们次要剖析罕用的虚拟机 (HotSpot) 的垃圾回收器。
展现了七种作用于不同分代的收集器,如果两个收集器之间存在连线,就阐明它们能够搭配应用,图中收集器所处的区域,则示意它是属于新生代收集器抑或是老年代收集器。
思维导图
内容
咱们从简略到简单次要剖析 5 种垃圾回收器:Serial 收集器、ParNew 收集器、Parallel Scavenge 收集器、CMS 收集器、G1 收集器.
Serial 收集器
原理 / 是什么:单线程垃圾回收器, 进行垃圾回收时候, 用户线程全副进行, 直到垃圾回收完结。
图解:
单线程工作的收集器,但它的“单线程”的意义并不仅仅是阐明它只会应用一个处理器或一条收集线程去实现垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其余所有工作线程,直到它收集完结。“Stop The World”这个词语也 许听起来很酷,但这项工作是由虚拟机在后盾主动发动和主动实现的,在用户不可知、不可控的状况 下把用户的失常工作的线程全副停掉,这对很多利用来说都是不能承受的。
特点:
毛病:“Stop The World”: 它进⾏垃圾收集时,必须暂停其余所有的⼯作线程,直到它收集完结。在⽤户不可⻅的状况下把⽤户失常⼯作的线程全副停掉。(从 Serial 收集器到 Parallel 收集器,再到 Concurrent Mark Sweep(CMS)和 Garbage First(G1)收集器,最终至当初垃圾收集器的最前沿成绩 Shenandoah 和 ZGC 等,咱们看到了一个个越来越构思精美,越来越优良,也越来越简单的垃圾收集器不断涌现,用户线 程的进展工夫在继续缩短,然而依然没有方法彻底消除)
长处: 多⽤于 桌⾯应⽤ , 是 客户端 Client 模式下的虚拟机。桌⾯应⽤暂用内存⼩,进⾏垃圾回收的工夫⽐较短,只有不频繁发⽣进展就能够承受。(因为是单线程的,所以耗费 cpu 跟内存绝对比拟小)
ParNew 收集器
回顾:
上一节咱们解说了垃圾回收器外面第一种垃圾回收器 Serial。Serial 垃圾回收器采纳单线程垃圾回收,他的性能是比拟差的。为了解决 Serial 的性能问题,咱们引入了另一种垃圾回收器:ParNew 垃圾回收器。
原理:ParNew 收集器本质上是 Serial 收集器的多线程并行版本,除了同时应用多条线程进行垃圾收集之 外,其余的行为包含 Serial 收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure 等)、收集算法、Stop The World、对象调配规 则、回收策略等都与 Serial 收集器完全一致
图解:
1、ParNew 收集器除了反对多线程并行收集之外,其余与 Serial 收集器相比并没有太多翻新之处,但它 却是不少运行在服务端模式下的 HotSpot 虚拟机,尤其是 JDK 7 之前的遗留零碎中首选的新生代收集 器,其中有一个与性能、性能无关但其实很重要的起因是:除了 Serial 收集器外,目前只有它能与 CMS 收集器配合工作。
2、CMS 作为老年代的收集器,却无奈与 JDK 1.4.0 中曾经存在的新生代收集器 Parallel Scavenge 配合工作[1],所以在 JDK 5 中应用 CMS 来收集老年代的时候,新生代只能抉择 ParNew 或者 Serial 收集器中的一个。ParNew 收集器是激活 CMS 后(应用 -XX:+UseConcMarkSweepGC 选项)的默 认新生代收集器,也能够应用 -XX:+/-UseParNewGC 选项来强制指定或者禁用它。
3、G1 是一个面向全堆的收集器,不 再须要其余新生代收集器的配合工作。所以自 JDK 9 开始,ParNew 加 CMS 收集器的组合就不再是官网 举荐的服务端模式下的收集器解决方案了。官网心愿它能齐全被 G1 所取代,甚至还勾销了 ParNew 加 Serial Old 以及 Serial 加 CMS 这两组收集器组合的反对(其实本来也很少人这样应用)
特点:
—1、ParNew 收集器除了多线程收集之外,其余与 Serial 收集器相⽐并没有太多翻新之处,但它却是许多运⾏在 Server 模式下(之前咱们的 Serial 是运行在 client 端的)的虚拟机中⾸选的新⽣代收集器,其中有⼀个与性能⽆关但很重要的起因是,除了 Serial 收集器外,⽬前只有它能与 CMS 收集器配合⼯作(ParNew 个别跟 CMS 一起来应用;咱们的 ParNew 个别用来回收新生代,而 CMS 个别用来回收老年代)。
—2、使⽤ -XX: ParallelGCThreads 参数来限度垃圾收集的线程数。(这个设置有一个参照点:大家晓得咱们的 cpu 有一个核数,这个核数代表了咱们 cpu 同时能解决多少个线程的数量,如果 cpu 核数是 8 的话,倡议将咱们的 ParallelGCThreads 参数值设置为 8)
—3、多线程操作存在高低⽂切换的问题,所以倡议将 -XX: ParallelGCThreads 设置成和 CPU 核数雷同,如果设置太多的话就会产⽣高低⽂切换耗费。(所以并不是咱们这个 ParallelGCThreads 参数越大越好、他会有上下文切换的生效)
收集器的上下文语境中: 并发与并行
并发 :形容 GC 线程跟用户线程之间关系:GC 线程跟用户线程同时运行。
并行:形容 GC 线程间关系: 多个 GC 线程同时运行,用户线程暂停。
Parallel Scavenge 收集器
回顾:上一节咱们解说了 ParNew 垃圾回收器,ParNew 垃圾回收器是在 Serial 根底上实现的一个多线程的裁减。多线程的垃圾回收器除了 ParNew 之外,还有 Parallel Scavenge 垃圾回收器。
是什么:管制的吞吐量的 ParNew 收集器(也能够叫做: 基于标记 - 复制算法实现的多线程吞吐量优先的垃圾回收器)
特点: 1、多线程垃圾回收 2、关注吞吐量 3、参数可调。
与其余垃圾回收器区别: 关注点:其余垃圾回收线程关注缩短垃圾收集时用户线程的进展工夫。Parallel Scavenge 收集器的指标则是达到一个可管制的吞吐 量(Throughput)
吞吐量: 吞吐量 = 用户线程执行工夫 / 用户线程执行工夫 +GC 线程执行工夫
CMS 收集器
回顾: 上一节咱们解说了两个概念,一个是并发一个是并行,并且咱们解说了 ParNew 跟 Parallel Scavenge 收集器,他们都是并行的垃圾回收器,当工作线程运行到一半时候会被阻断运行 GC 线程,GC 垃圾回收之后会再次运行工作线程。除了并行之外,还有一种垃圾回收器他是能够并发执行的。CMS 垃圾收集器。
是什么?: CMS(Concurrent Mark Sweep)基于标记 - 革除算法实现的一种以获取最短回收进展工夫为指标的收集器。目前很 大一部分的 Java 利用集中在互联网网站或者基于浏览器的 B / S 零碎的服务端上,这类利用通常都会较为 关注服务的响应速度,心愿零碎进展工夫尽可能短,以给用户带来良好的交互体验。
图解 4 步骤:
出 CMS 收集器是基于标记 - 革除算法实现的整个过程分为四个步骤,包含:1)初始标记(CMS initial mark): 是标记 GC Roots 能间接关联到的对象,速度很快.
2)并发标记(CMS concurrent mark): 从 GC Roots 的间接关联对象开始遍历整个对象图的过程,这个过程耗时较长然而不须要进展用户线程,能够与垃圾收集线程一起并发运行;
3)从新标记(CMS remark): 为了修改并发标记期间,因用户程序持续运作而导致标记产生变动的那一部分对象的标记记录; 这个阶段的进展工夫通常会比初始标记阶段稍长些,但也远比并发标记阶段的工夫短;
4)并发革除(CMS concurrent sweep): 清理删除掉标记阶段判断的曾经死亡的对象, 因为不须要挪动存活对象, 所以这个阶段也是能够与用户线程同时并发的。
初始标记、从新标记这两个步骤依然须要“Stop The World”.
因为在整个过程中耗时最长的并发标记和并发革除阶段中,垃圾收集器线程都能够与用户线程一 起工作,所以从总体上来说,CMS 收集器的内存回收过程是与用户线程一起并发执行的。能够比较清楚地看到 CMS 收集器的运作步骤中并发和须要进展的阶段。
特点:
长处:并发收集、低进展。
毛病:
1、对 CPU 资源⾮常敏感(特地是单核机器 - 并发会占用更多资源)
2、⽆法解决浮动垃圾,并发革除时候曾经产生了一些垃圾;浮动垃圾:程序在进⾏并发革除阶段⽤户线程所产⽣的新垃圾。
3、标记 - 革除临时空间碎⽚。
G1 收集器.
回顾: 上一节咱们解说 CMS 垃圾收集器,这一节咱们解说更加高效的垃圾收集器:G1 收集器。
是什么?: G1 是⼀款⾯向服务端应⽤的垃圾收集器。是基于标记 - 整顿法;首先他对性能的要求会特地高。
流程步骤:
·初始标记(Initial Marking): 仅仅只是标记一下 GC Roots 能间接关联到的对象,并且批改 TAMS 指针的值,让下一阶段用户线程并发运行时,能正确地在可用的 Region 中调配新对象。这个阶段须要 进展线程,但耗时很短,而且是借用进行 Minor GC 的时候同步实现的,所以 G1 收集器在这个阶段理论 并没有额定的进展。
·并发标记(Concurrent Marking): 从 GC Root 开始对堆中对象进行可达性剖析,递归扫描整个堆 里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。当对象图扫描实现以 后,还要重新处理 SATB 记录下的在并发时有援用变动的对象。
·最终标记(Final Marking): 对用户线程做另一个短暂的暂停,用于解决并发阶段完结后仍遗留 下来的最初那大量的 SATB 记录。
·筛选回收(Live Data Counting and Evacuation): 负责更新 Region 的统计数据,对各个 Region 的回 收价值和老本进行排序,依据用户所冀望的进展工夫来制订回收打算,能够自由选择任意多个 Region 形成回收集,而后把决定回收的那一部分 Region 的存活对象复制到空的 Region 中,再清理掉整个旧 Region 的全副空间。这里的操作波及存活对象的挪动,是必须暂停用户线程,由多条收集器线程并行 实现的。从上述阶段的形容能够看出,G1 收集器除了并发标记外,其余阶段也是要齐全暂停用户线程的,换言之,它并非纯正地谋求低提早,官网给它设定的指标是在提早可控的状况下取得尽可能高的吞吐 量,所以能力担当起“全功能收集器”的重任与冀望
特点: 跟 CMS 相比他有什么特点呢?
G1 会将内存块分成多个 Region.Region 就是一个区域。(咱们之前在将 CMS、Serial 垃圾收集器的时候,咱们都采纳了新生代、老年代分区的收集办法,而在 G1 的时候,对新生代、老年代就不是特地敏感了、他将咱们的每一块内存分成了 Region,内存区域快会分成多个 Region。咱们垃圾回收时候保护的是 Region 外面的信息。)辨别 Region 有什么用呢?Region 外面有与之对应的 RememberSet。当进⾏内存回收时,在 GC 根节点的枚举范畴中加⼊ Remembered Set 即可保障不对全堆扫描也不会有脱漏 查看 Reference 引⽤的对象是否处于不同的 Region。
劣势:
1、空间整合: Region 内基于“标记⼀整顿”算法实现为主(防止空间垃圾碎片)和 Region 之间采⽤复制算法(Region 之间存活对象比拟少,复制算法效率高)实现的垃圾收集。
2、可预测的进展:(起因:因为咱们操作的是 region, 垃圾回收信息都寄存在 region 外面,所以他是能够预测进展工夫的)这是 G1 绝对于 CMS 的另⼀⼤劣势,升高进展工夫是 G1 和 CMS 独特的关 注点,但 G1 除了谋求低进展外,还能建⽴可预测的进展工夫模型。
3、在 G1 之前 的其余收集器进⾏收集的范畴都是整个新⽣代或者⽼年代,⽽ G1 不再是这样。使⽤ G1 收集器时,Java 堆的内存布局就与其余收集器有很⼤差异,它将整个 Java 雄划分为多个⼤⼩相等的独⽴区域(Region),尽管还保留有新⽣代和⽼年代的概念,但新⽣代和⽼年
代不再是物理隔髙的了,它们都是⼀局部 Region(不须要间断)的汇合。
4、G1 收集器 之所以能建⽴可预测的进展工夫模型,是因为它能够有打算地防止在整个 Java 堆
中进⾏全区域的垃圾收集。G1 跟踪各个 Regions ⾥⾯的垃圾沉积的价值⼤⼩(回收所取得的空间⼤⼩以及回收所需工夫的经验值),在后盾保护⼀个优先列表,每次依据容许的收集工夫,优先回收价值最⼤的 Region(这也就是 Garbage- Firsti 名称的来由)。这种使⽤ Region 划分内存空间以及有优先级的区域回收⽅式,保障了 G1 收集器在无限的工夫内能够获取尽可能⾼。
补充:
当初咱们很多公司还是在应用 CMS 垃圾回收器,很少会用 JDK1.9、JDK.10. 所以咱们还很少用 G1。