如果Java虚拟机中标记清除算法、标记整理算法、复制算法、分代算法这些属于GC收集算法中的方法论,那么“GC收集器”则是这些方法论的具体实现。

在面试过程中这个深度的问题涉及的比较少,但对于理解上面的这些算法有很好的帮助。如果能够如数家珍,也是面试中的加分项,还是那句话,毕竟面试官的时间也不多了。

概念准备

在学习Java GC收集器之前,需要先了解一些内容和概念,首先如果没有学习《面试官,不要再问我“Java GC垃圾回收机制”了》的可先学习该篇文章,了解基本算法方法论。

下面了解几个概念以帮助后面的学习:线程暂停(Stop The World)、安全点(Safepoint)、安全区(Safe region)。

在执行可达性分析的时候会出现在分析的过程中对象关系引用等发生了变化,为了保证分析的准确性,就必须在分析的过程中暂停所有Java线程,Sun将这一事件称作“Stop The World”。

那么,什么时候暂停合适呢?并不是所有的时刻都可以暂停所有线程进行GC的,只有到达某些点才可以进行GC操作,这些点就称作安全点(Safepoint)。

安全点的设置不能太少,那样GC等待的时间就会太长,但也不能太多否则会增加运行时的负担。

所以,安全点的选定基本上是以程序“是否具有让程序长时间执行的特征”为标准进行选定的。比如,循环的末尾、方法临返回前/调用方法的call指令后、可能抛异常的位置等。

HotSpot采用主动中断的方式,让执行线程在运行期轮询是否需要暂停的(GC设置的)标志,若需要则中断挂起。

对于正在运行的线程,可以主动运行到安全点并暂停执行,但是对于那些正在Sleep或阻塞的线程,当它们重新执行时可能已经过了安全点,但此时GC可能还没完成垃圾回收,这种情况该怎么办呢?

于是就有了安全区(Safe region)的概念,安全区是一块区域,在该区域中引用都不会被修改。比如,线程进入到安全区的时候先标识自己进入了安全区,等它被唤醒准备离开时,先检查GC是否完成,如果完成则可以离开,否则就在安全区等待。

了解了上面的基本概念之后,下面正式进入垃圾收集器的讲解。

垃圾收集器分类

先通过下图了解一下Hotspot的8种垃圾收集器及其应用。

两个收集器之间的连线,表示它们可以搭配使用。收集器所处的区域表示它是属于新生代收集器还是老年代收集器。其中ZGC为Java11引入的新的垃圾收集器。

默认垃圾收集器

不同Java版本采用的默认收集器如下。

Serial收集器

Serial收集器是最基本、发展历史最悠久的收集器,是一个单线程的收集器。在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。就是所谓的“Stop The World。”

ParNew收集器

ParNew收集器其实就是Serial收集器的多线程版本。除了使用多线程进行垃圾收集外,其余行为包括Serial收集器可用的所有控制参数、收集算法(复制算法)、Stop The World、对象分配规则、回收策略等与Serial收集器完全相同,两者共用了相当多的代码。

Parallel Scavenge收集器

Parallel Scavenge收集器是一个新生代搜集器,主要采用复制算法,与ParNew类似。但关注点与其他搜集器不同,目标是达到一个可控的吞吐量。

Serial Old收集器

Serial Old是Serial收集器的老年代版本,同样是一个单线程收集器,使用标记-整理算法。运作图同Serial搜集器。

Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。在JDK 1.6中才开始提供。

CMS收集器

CMS(Concurrent Mark and Sweep 并发-标记-清除),是一种以获取最短回收停顿时间为目标的收集器。基于并发、使用标记清除算法,只针对老年代进行垃圾回收。

CMS收集器工作时,尽可能让GC线程和用户线程并发执行,以达到降低STW时间的目的。

整个操作步骤分为四步:初始标记(CMS initial mark)、并发标记(CMS concurrent mark)、重新标记(CMS remark)、并发清除(CMS concurrent sweep)。

在上图过程中,初始标记和重新标记都会触发“Stop The World”。

初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,在Java7中是单线程,在Java8以后可采用多线程。

发标记阶段GC线程和应用线程并发执行,初始标记出来的存活对象,然后继续递归标记这些对象可达的对象。

重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

优点:并发收集、低停顿。

缺点:对CPU资源非常敏感、无法处理浮动垃圾、标记-清除算法导致的空间碎片。

G1收集器

G1(Garbage-First)是一款面向服务端应用的垃圾收集器。支持新生代和老年代空间的垃圾收集。

该收集器可充分利用CPU和硬件缩短STW的时间,还具有“整合空间”、“可预测停顿”等特点。比如,可建立可预测的停顿时间模型,能让使用者明确指定在一个长度为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。

使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region的集合。

G1会跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region(这也就是Garbage-First名称的来由)。

G1收集器的运作大致可划分为以下几个步骤:初始标记(Initial Marking)、并发标记(Concurrent Marking)、最终标记(Final Marking)、筛选回收(Live Data Counting and Evacuation)。

整个流程来看,前几个步骤与CMS的流程很相似。同样的在初始标记和最终标记的过程中都会触发“Stop The World”。

其中,筛选回收阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。

ZGC收集器

ZGC(Z Garbage Collector),是一款可伸缩、低延迟、并发垃圾回收器。在Java11中引入,应用Linux64位系统。

其旨在实现以下几个目标:停顿时间不超过10ms、停顿时间不随heap大小或存活对象大小增大而增大、可以处理从几百兆到几T的内存大小。

ZGC将内存划分为多个区域,也称为ZPage。ZPages可以动态创建和销毁。它们也可以动态调整大小(与G1 GC不同),是2 MB的倍数。以下是堆区域的大小组:Small (2 MB)、Medium (32 MB)、Large (N * 2 MB)。

ZGC堆可以多次出现这些堆区域。中型和大型区域是连续分配的,如下图所示:

与其他GC不同,ZGC的物理堆区域可以映射到更大的堆地址空间(其中可以包括虚拟内存)。

ZGC的执行过程包括:标记(初始标记、并发标记、边缘情况处理)、重新定位(查找重新定位块、根引用重新定位并更新、并发定位其他对象并存储新旧地址映射)、重新映射。

其中标记中的初始标记和边缘情况处理会引发“Stop The World”,重新定位中的“根引用重新定位并更新”也会引发“Stop The World”。

其中重新映射流程图如下:

ZGC打算以较短的应用程序暂停时间来支持大堆大小。为了实现此目标,它使用了包括彩色64位指针,负载屏障,重定位和重新映射的技术。

小结

本文介绍了场景的垃圾收集器以及相关的概念,属于较深层次的内容,针对这些内容还可以进一步进行横向或纵向拓展。

有朋友在评论区问,学这些有底层什么用?当然我们不仅仅是为了面试,就拿关于JVM结构及Java8 JVM内存结构变动来说吧。

昨天在部署一个比较大的项目时就出现“java.lang.OutOfMemoryError: Metaspace”异常,如果学习了之前的相关内容可以很轻易的定位到是因为JVM设置了Metaspace的上限参数,并且参数值设置小导致的。

最后,《面试官系列》正在持续更新,欢迎关注公众号“程序新视界”,获得最新内容。

原文链接:《面试官,不要再问我“Java 垃圾收集器”了

《面试官》系列文章:

程序新视界:精彩和成长都不容错过

面试官,不要再问我“Java 垃圾收集器”了的更多相关文章

  1. 面试官,不要再问我“Java 垃圾收集器”了(转载)

    如果Java虚拟机中标记清除算法.标记整理算法.复制算法.分代算法这些属于GC收集算法中的方法论,那么"GC收集器"则是这些方法论的具体实现. 在 面试过程中这个深度的问题涉及的比 ...

  2. 面试官:怎么做JDK8的垃圾收集器的调优?

    面试官:怎么做JDK8的垃圾收集器的调优? 看着面试官真诚的眼神,心中暗想看起来年纪轻轻却提出如此直击灵魂的问题.擦了擦额头上汗,我稍微调整了一下紧张的情绪,对面试官说: 在JDK8中有Serial收 ...

  3. 面试官,不要再问我“Java虚拟机类加载机制”了

    关于Java虚拟机类加载机制往往有两方面的面试题:根据程序判断输出结果和讲讲虚拟机类加载机制的流程.其实这两类题本质上都是考察面试者对Java虚拟机类加载机制的了解. 面试题试水 现在有这样一道判断程 ...

  4. 面试官,不要再问我“Java虚拟机类加载机制”了(转载)

    关于Java虚拟机类加载机制往往有两方面的 面试题:根据程序判断输出结果和讲讲虚拟机类加载机制的流程.其实这两类题本质上都是考察面试者对Java虚拟机类加载机制的了解. 面试题试水 现在有这样一道判断 ...

  5. Java面试官最爱问的volatile关键字

    在Java的面试当中,面试官最爱问的就是volatile关键字相关的问题.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...

  6. 面试官,不要再问我“Java GC垃圾回收机制”了

    Java GC垃圾回收几乎是面试必问的JVM问题之一,本篇文章带领大家了解Java GC的底层原理,图文并茂,突破学习及面试瓶颈. 楔子-JVM内存结构补充 在上篇<JVM之内存结构详解> ...

  7. 求你了,再问你Java内存模型的时候别再给我讲堆栈方法区了…

    GitHub 4.1k Star 的Java工程师成神之路 ,不来了解一下吗? GitHub 4.1k Star 的Java工程师成神之路 ,真的不来了解一下吗? GitHub 4.1k Star 的 ...

  8. 基础面试,为什么面试官总喜欢问String?

    关于 Java String,这是面试的基础,但是还有很多童鞋不能说清楚,所以本文将简单而又透彻的说明一下那个让你迷惑的 String 在 Java 中,我们有两种方式创建一个字符串 String x ...

  9. 大厂面试官最常问的@Configuration+@Bean(JDKConfig编程方式)

    大厂面试官最常问的@Configuration+@Bean(JDKConfig编程方式)   现在大部分的Spring项目都采用了基于注解的配置,采用了@Configuration 替换标签的做法.一 ...

随机推荐

  1. 手把手教你使用Java实现一个神经网络

    首先看一下运行效果: 下面是项目整体目录: 0.实现神经网络总览 神经网络由层.神经元.权重.激活函数和偏置组成.每层都有一个或者多个神经元,每一个神经元都和神经输入/输出连接,这些连接就是权重. 需 ...

  2. 21 (OC) 数据持久化

    概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据.在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案: plist文件(属性列表) ...

  3. Java面向对象程序设计第5章1-9

    1.面向对象的主要特征是什么? 三大特征是:封装.继承和多态. 封装:是指将某事物的属性和行为包装到对象中,这个对象只对外公布需要公开的属性和行为,而这个公布也是可以有选择性的公布给其它对象. 继承: ...

  4. spring框架对于实体类复杂属性注入xml文件的配置

    spring框架是javaWeb项目中至关重要的一个框架,大多web 项目在工作层次上分为持久层.服务层.控制层.持久层(dao.mapper)用于连接数据库,完成项目与数据库中数据的传递:服务层(s ...

  5. vue-router之路由元信息

    路由元信息?(黑人问号脸???)是不是这么官方的解释很多人都会一脸懵?那么我们说meta,是不是很多人恍然大悟,因为在项目中用到或者看到过呢? 是的,路由元信息就是我们定义路由时配置的meta字段:那 ...

  6. pikachu-数字型注入(post)#手工注入

    1, 因为是post型,所以需要抓取数据包 2, 测试结果为数字型注入 提交恒等的语句可以查询到所有的数据信息 3, 使用UNION联合查询法 判断字段数,测试为2个字段时没有报错,所以可以判断字段数 ...

  7. Spark 学习笔记之 MONGODB SPARK CONNECTOR 插入性能测试

    MONGODB SPARK CONNECTOR 测试数据量: 测试结果: 116万数据通过4个表的join,从SQL Server查出,耗时1分多.MongoSparkConnector插入平均耗时: ...

  8. WKWebView针对于Cordova的IOS平台性能提升

    使用cordova做跨平台开发已久,针对于Android的性能与页面渲染问题仍然让人头疼,因为仍然有一部分人使用性能一般的手机,版本在 4.2-4.4之间,甚至都无法支持HTML5的flex布局,使得 ...

  9. js匿名函数自执行的好处

    我们知道,在创建一个函数时如果要用到变量来存取信息的话,要尽量使用局部变量. 因为一方面局部变量会随着函数的执行结束被销毁:另一方面在不执行函数的时候也不会创建这个局部变量,对节省空间资源有很大的好处 ...

  10. android 6.0导航栏 NavigationBar影响视图解决办法

    在开发app的时候会遇到有些测试手机没有物理按钮,比如最近在做的一个app在小米手机上运行显示效果很好,但是在华为P7手机上显示就乱了,底部的NavigationBar直接覆盖在主视图上,导致按钮无法 ...