前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试官:简历上说精通垃圾收集器?来吧,挨个给我说一遍

面试官:简历上说精通垃圾收集器?来吧,挨个给我说一遍

作者头像
胖虎
发布2019-07-19 09:49:51
5870
发布2019-07-19 09:49:51
举报
文章被收录于专栏:晏霖晏霖

前言

上文已经讲解垃圾收集的各种算法,算法可以理解为方法,如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

正文

面试官:你认识到的收集器都有哪些啊?

答:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;

面试官:为什么HotSpot虚拟机需要这么多收集器?

答:HotsSpot垃圾是分代收集的,所以不用的分代收集器也不同,即使是同一年代里收集器也会不同,因为每个收集器特点和性能不同也就有了收集器的多样性,所以各个收集器互相组合使用才能适应不同场景。

面试官:那你说一下每个收集器都有什么特点吧!

新生代收集器:Serial、ParNew、Parallel Scavenge;

老年代收集器:Serial Old、Parallel Old、CMS;

整堆收集器:G1;

开始之前我们了解一个概念

Minor GC

又称新生代GC,指发生在新生代的垃圾收集动作;

因为Java对象大多是朝生夕灭,所以Minor GC非常频繁,一般回收速度也比较快;

Full GC

又称Major GC或老年代GC,指发生在老年代的GC;

出现Full GC经常会伴随至少一次的Minor GC(不是绝对,Parallel Sacvenge收集器就可以选择设置Major GC策略);

Major GC速度一般比Minor GC慢10倍以上;

Serial收集器

这个收集器是最基本的,也是历史最悠久的收集器,目前基本不会用了,想当年那也是新生代的唯一选择,但是这么多年过去了,HotSpot也没有说过河拆桥把它废了,“老而无用、食之无味弃之可惜”。

他是一个单线程收集器,他在工作的时候,必须暂停其他所有的工作线程,直到收集结束。这里要知道一个很严重的问题就是,暂停一切线程的结果就是当前运行在这个JDK的所有程序里的用户线程全部暂停,也就是说这一瞬间都是死掉的,用户看到的现象就是页面无任何响应,如果这种现象出现的时间长且频繁用户就崩溃了。

这里埋下了一个伏笔,越优秀的收集器,他的停顿时间一定越短,这也是所有收集器共同追求的目标。

ParNew收集器

他是Serial收集器多线程版本,其所有控制参数、收集算法、对象分配规则、回收策略等都与Serial完全一样。下面是ParNew收集器工作的过程。

他的重要之处在于,除了多线程提高了性能之外,他还可以与CMS收集器(下面介绍)搭配使用的原因。

在单CPU环境下ParNew的性能没办法超过Serial,但是随着CPU数量增多他的优势就会越来越明显。

Parallel Scavenge收集器

他也是一款新生代收集器,使用的是复制算法,并且是并行对线程收集器。可以看到收集器的进步都是保留上一代之长,弥补上一代之短。

很多收集器关注用户线程的停顿时间,但是Parallel Scavenge则关注吞吐量。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),例:虚拟机运行100分钟,其中垃圾收集时间用了1分钟,那吞吐量就是99%。

他是怎样控制吞吐量呢?

使用参数控制最大垃圾收集停顿时间 -XX:MaxGCPauseMillis ,以及直接设置吞吐量大小 -XX:GCTimeRatio参数。

MaxGCPauseMillis参数是一个大于0的毫秒数,收集器一次工作尽可能不超过设定的这个值,但是设置太小GC停顿时间缩短,造成了垃圾收集频率变快。如果你设定停顿100毫秒,10秒收集一次的频率,改成70毫秒的停顿时间,那么频率就可能变成5秒一次。停顿时间下降,吞吐量也会下降,GC还会变得更频繁。

XX:GCTimeRatio参数设置垃圾收集时间占总时间的比率,0<n<100的整数;

GCTimeRatio相当于设置吞吐量大小;

垃圾收集执行时间占应用程序执行时间的比例的计算方法是:1 / (1 + n)

例如,选项-XX:GCTimeRatio=19,设置了垃圾收集时间占总时间的5%--1/(1+19);

默认值是1%--1/(1+99),即n=99;

看来找准最优的临界点真的是Parallel Scavenge收集器比较配置的。

不要担心,HotSpot又提供了一个参数 XX:+UseAdptiveSizePolicy帮助我们实现GC自适应的调节策略,他会根据当前系统运行情况收集性能监控信息,动态调整这些参数,以提供最合适的停顿时间或最大的吞吐量。

这个参数开启,JVM就可以动态分配新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等;

这里插入一个笔者的经历:当时面试有人问我,Eden与Survivor区的比例可以变化吗?

看到这里童鞋们就可以回答:Parallel Scavenge收集器开启 XX:+UseAdptiveSizePolicy可以动态分配。

这也是Parallel Scavenge收集器优越于ParNew收集器一个重要点。

Serial Old收集器

Serial Old是 Serial收集器的老年代版本,也是继承Serial收集器单线程的特点。

工作模型图在Serial收集器中展示了。

Parallel Old收集器

Parallel Old垃圾收集器是Parallel Scavenge收集器的老年代版本,继承了Parallel New多线程对特点,在JDK1.6及之后用来代替老年代的Serial Old收集器;

参数"-XX:+UseParallelOldGC":指定使用Parallel Old收集器;

工作模型图在Parallel New收集器中展示了。

接下来讲解CMS于G1收集器,在将之前要理解一个概念

可能前面也提到过,不过在这里了解以下也不晚

并行(Parallel)

指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态;

如ParNew、Parallel Scavenge、Parallel Old;

并发(Concurrent)

指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行);

用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上;

如CMS、G1(也有并行);

CMS收集器

并发标记清理收集器也称为并发低停顿收集器或低延迟垃圾收集器;他的宗旨是:低停顿。

HotSpot在JDK1.5推出的第一款真正意义上的并发(Concurrent)收集器

他采用的是“标记-清除算法",因此会生大量的空间碎片。为了解决这个问题,CMS可以通过配置以下两种参数解决:

  1. -XX:+UseCMSCompactAtFullCollection:参数,强制JVM在FGC完成后対老年代迸行圧縮,执行一次空间碎片整理,但是空间碎片整理阶段也会引发STW。为了减少STW次数,CMS还可以通过配置。
  2. -XX:+CMSFullGCsBeforeCompaction=n :参数,在执行了n次FGC后, JVM再在老年代执行空间碎片整理

初始标记 (Initial Mark)

停止一切用户线程,仅使用一条初始标记线程对所有与GC Roots直接相关联的 老年代对象进行标记,速度很快

并发标记 (Concurrent Marking Phase)

使用多条并发标记线程并行执行,并与用户线程并发执行.此过程进行可达性分析,标记所有这些对象可达的存货对象,速度很慢

重新标记 ( Remark)

因为并发标记时有用户线程在执行,标记结果可能有变化

停止一切用户线程,并使用多条重新标记线程并行执行,重新遍历所有在并发标记期间有变化的对象进行最后的标记.这个过程的运行时间介于初始标记和并发标记之间

并发清除 (Concurrent Sweeping)

只使用一条并发清除线程,和用户线程们并发执行,清除刚才标记的对象

这个过程非常耗时

G1收集器

G1(Garbage-First)是JDK7-u4才推出商用的收集器;他比CMS更高级了,他是并行与并发,能充分利用多CPU、多核环境下的硬件优势;

G1以下特点

并行与并发

能充分利用多CPU、多核环境下的硬件优势;

可以并行来缩短"Stop The World"停顿时间;

也可以并发让垃圾收集与用户程序同时进行;

分代收集,收集范围包括新生代和老年代

能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配;

能够采用不同方式处理不同时期的对象;

虽然保留分代概念,但Java堆的内存布局有很大差别;

将整个堆划分为多个大小相等的独立区域(Region);

新生代和老年代不再是物理隔离,它们都是一部分Region(不需要连续)的集合;

结合多种垃圾收集算法,空间整合,不产生碎片

从整体看,是基于标记-整理算法;

从局部(两个Region间)看,是基于复制算法;

这是一种类似火车算法的实现;

都不会产生内存碎片,有利于长时间运行;

可预测的停顿:低停顿的同时实现高吞吐量

G1除了追求低停顿处,还能建立可预测的停顿时间模型;

可以明确指定M毫秒时间片内,垃圾收集消耗的时间不超过N毫秒;

如:在堆大小约6GB或更大时,可预测的暂停时间可以低于0.5秒;

用来替换掉JDK1.5中的CMS收集器;

上面提到Region 概念,肯定都不会理解,让我们看一下G1的内存模型

G1将Java堆空间分割成了若干相同大小的区域,即region

Humongous是特殊的Old类型,专门放置大型对象

在JDK11中,已经将G1设为默认垃圾回收器

G1垃圾收集过程

初始标记

标记与GC Roots直接关联的对象,停止所有用户线程,只启动一条初始标记线程,这个过程很快。

并发标记

进行全面的可达性分析,开启一条并发标记线程与用户线程并行执行.这个过程比较长。

最终标记

标记出并发标记过程中用户线程新产生的垃圾.停止所有用户线程,并使用多条最终标记线程并行执行。

筛选回收

回收废弃的对象.此时也需要停止一切用户线程,并使用多条筛选回收线程并行执行。

题外话:我们知道目前为止JDK8是应用最广泛对版本,并且JDK9、10是一个过度版,企业应用效果不好,JDK11是一个里程碑版本,重要程度相当于现在JDK8,并且JDK11默认使用G1收集器,G1的性能在早些年就突出于CMS并且官方对性能测试结果也是这样说明的。

HotSpot-为什么你没有女朋友,因为你不了解‘对象’

详细讲解JVM(Hotspot)运行时数据区

为什么HotSpot有‘对象’,因为他会垃圾分类和回收

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 晏霖 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
应用性能监控
应用性能监控(Application Performance Management,APM)是一款应用性能管理平台,基于实时多语言应用探针全量采集技术,为您提供分布式性能分析和故障自检能力。APM 协助您在复杂的业务系统里快速定位性能问题,降低 MTTR(平均故障恢复时间),实时了解并追踪应用性能,提升用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档