首页 > 编程知识 正文

jvm垃圾回收器有哪些,垃圾回收机制面试题

时间:2023-05-05 05:14:37 阅读:33196 作者:4201

文章目录垃圾回收JVM垃圾回收垃圾回收算法如何判断死亡垃圾回收器

垃圾回收

这里主要谈论CMS和G1,可以很容易地谈论其他事情

JVM垃圾回收面试官:你知道JVM垃圾回收的工作原理吗?

我:我知道。 先谈谈垃圾回收算法吧。 为了按一代收集,将堆分为年轻一代和老年代。

垃圾回收算法垃圾回收算法:

3358www.Sina.com/该算法分为“标记”和“清除”阶段。 首先是3358www.Sina.com/,标记完成后是3358www.Sina.com/。 这是最基础的采集算法,后续的算法都是对其不足的改进。 这个垃圾收集算法带来了两个明显的问题:

根据3358 www.Sina.com/http://www.Sina.com/http://www.Sina.com/基于http://www.Sina.com /提出的一种标记算法

由于3358 www.Sina.com/http://www.Sina.com /问题,出现了“复制”收集算法。 它可以使用标记-清除,每次都可以使用其中之一。 使用此块的内存后,将仍然存在的对象复制到其他块中,并一次清理使用中的空间。标记出所有需要回收的对象(堆栈的年轻一代又是怡保店员,分为s0和s1 ) )。

http://www.Sina.com/http://www.Sina.com /

一般情况:

在大多数情况下,如果统一回收所有被标记的对象eden区域没有足够的空间,虚拟机将启动Minor GC. 效率问题一次。 大对象需要空间问题(标记清除后会产生大量不连续的碎片)。 例如,字符串、数组等。 频繁复制导致的性能下降。 怡园店员出生,经第一次Minor GC存活,可容纳于Survivor时,转至Survivor空间,对象年龄为1。 对象为Survivor每次跨越Minor GC时年龄增加1岁,该年龄增加到一定程度时为35岁。 对象升级到上一代的年龄阈值可以在参数-XX:MaxTenuringThreshold中设置。 如何判断死亡面试官:如何判断对象的死亡?

我:有两种策略。 一个是标记-整理,另一个是老年代的特点

将参考计数器添加到3358www.Sina.com/对象。 每次有地方让所有存活的对象向一端移动复制解决效率内存分为大小相同的两块; 随时这样就使每次的内存回收都是对内存区间的一半进行回收

分代

3358www.Sina.com/算法的基本思想是以一系列被称为比如在drdxl代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。的对象为起点,对象在drdxl代中 eden 区分配

可以用作GC路由的:

应用于虚拟机堆栈(堆栈帧中的局部变量区域,也称为局部变量表)的对象。 本地方法堆栈中的JNI (朴素方法)引用的对象方法区域中的类静态属性引用的对象方法区域中的常量引用的对象面试者:一种枚举根节点的方法

我:以上引用作用GC Roots的根为大对象直接进入老年代,如果枚举根节点需要停顿。 在HotSpot实现中,使用称为3358www.Sina.com/的组来实现此目的,类加载完成时为大量连续内存空间的对象,JIT编译时为http://www.Sina

是引用。这样,GC在扫描时就可以直接得这心信息了。

但一个很现实的问题:可能导致引用关系变化,或者说OopMap内容变化的指令非常多,如果为每一条指令都生成对应的OopMap,那么会需要大量的额外空间,这样GC成本很高,安全点由此而来

实际上,在JIT编译过程中,在特定的位置记录下栈和寄存器哪些位置是引用,实际上这些位置就是安全点,意思就是说,程序执行时并非在所有地方都能停顿下来开始GC,只有在达到安全点时才能暂停

Safepoint机制保证了程序执行时,在不太长的时间内就会遇到可进入GC的Safepoint,但如果线程处于Sleep或者Blocked状态,这时候线程无法响应JVM的中断请求,JVM也显然不太可能等待线程重新被分配CPU时间,这种情况就需要安全域来解决。安全域是指在一段代码片段中,引用关系不会发生变化。在这个区域中的任意地方开始GC都是安全的。这时候安全点就被扩展到了Safe Region

垃圾回收器

面试官:给我讲讲垃圾收集器吧

我:当然没问题,有一张有趣的图

小插曲:咱们知道,堆分为drdxl代和老年代,那么从这张图可以看出,drdxl代有Serial、ParNew和Parallel Scavenge而老年代自然也有Serial Old和Parallel Old,drdxl代和老年代都有串并行收集器,能互相搭配,但看CMS就很特殊,它是老年代的收集器,能从图中可以看出来,它不稳定呀,居然用Serial Old当备胎,而且为了能搭配CMS的并行垃圾收集器,就给它造了一个ParNew,哈哈哈(开个玩笑)。G1暂且不说,横跨drdxl和老年。在它这一块不分家,一把抓。

我就简单说一下串并行垃圾收集器,太古老了,面试官也不想听。

你像Serial和ParNew呀,其实在STW的时候,一个是单线程,一个是多线程回收垃圾。而ParNew和Parallel Scavenge的区别仅仅是吞吐量,后者重在吞吐量上(高效率利用CPU)。所以,Serial 收集器对于运行在 Client 模式下的虚拟机来说是个不错的选择。而ParNew是在Server 模式下的虚拟机的首要选择之一。以上垃圾收集器drdxl代采用复制,而老年代采用标记-整理。

CMS垃圾收集器

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用。

CMS(Concurrent Mark Sweep)收集器是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

从名字中的Mark Sweep这两个词可以看出,CMS 收集器是一种 “标记-清除”算法实现的,它的运作过程相比于前面几种垃圾收集器来说更加复杂一些。整个过程分为四个步骤:

初始标记:暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快 ;并发标记:同时开启 GC 和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以 GC 线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。重新标记:重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短并发清除:开启用户线程,同时 GC 线程开始对为标记的区域做清扫。

从它的名字就可以看出它是一款优秀的垃圾收集器,主要优点:并发收集、低停顿。但是它有下面三个明显的缺点:

对 CPU 资源敏感;无法处理浮动垃圾;它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生。

因此,为了解决以上缺点,G1就出现了:

将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留drdxl代和老年代的概念,但drdxl代和老年代不再是物理隔离的了,而都是一部分Region(不需要连续)的集合

并行与并发: G1 能充分利用 CPU、多核环境下的硬件优势,使用多个 CPU来缩短 Stop-The-World 停顿时间。

分代收集:虽然 G1 可以不需要其他收集器配合就能独立管理整个 GC 堆,但是还是保留了分代的概念。

空间整合:G1 从整体来看是基于**“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法**实现的。

可预测停顿:这是 G1 相对于 CMS 的另一个大优势,降低停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需要时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region

G1的跨代引用

在G1收集器中,Region之间的对象引用以及其他收集器中的drdxl代与老年代之间的对象引用,虚拟机都是使用Remembered Set(RS)来避免全堆扫描的。G1中每个Region都有一个与之对应的RS,虚拟机发现程序对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断操作检查Reference引用的对象是否处于不同的Region之间(在分代的例子中就是检查是否老年代中的对象引用了drdxl代中方的对象)如果是,便通过CardTable(每个Region块又细分了2000多个卡表,记录一波我引用了哪个对象)把相关引用信息记录到被引用对象所属的Region的RS之中。当进行内存回收时,在GC根节点的枚举范围中加入RS即可保证不对全堆扫描,也不会又遗漏

当然G1有也大致的四个过程:

初始标记:仅仅只是标记一下GC Roots 能直接关联到的对象,并且修改TAMS(Nest Top Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可以的Region中创建对象,此阶段需要停顿线程,但耗时很短。并发标记:从GC Root 开始对堆中对象进行可达性分析,找到存活对象,此阶段耗时较长,但可与用户程序并发执行。最终标记:为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并行执行。筛选回收:首先对各个Region中的回收价值和成本进行排序,根据用户所期望的GC 停顿是时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。

在这里,简单做一个CMS和G1的比较:

CMS收集器是获取最短回收停顿时间为目标的收集器,因为CMS工作时,GC工作线程与用户线程可以并发执行,以此来达到降低收集停顿时间的目的(只有初始标记和重新标记会STW)。但是CMS收集器对CPU资源非常敏感。在并发阶段,虽然不会导致用户线程停顿,但是会占用CPU资源而导致引用程序变慢,总吞吐量下降。CMS仅作用于老年代,是基于标记清除算法,所以清理的过程中会有大量的空间碎片。CMS收集器无法处理浮动垃圾由于CMS并发清理阶段用户线程还在运行,伴随程序的运行自然会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理它们,只好留在下一次GC时将其清理掉。G1是一款面向服务端应用的垃圾收集器,适用于多核处理器、大内存容量的服务端系统。G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU或核心来缩短STW的停顿时间,它满足短时间停顿的同时达到一个高的吞吐量。从JDK 9开始,G1成为默认的垃圾回收器。当应用有以下任何一种特性时非常适合用G1:Full GC持续时间太长或者太频繁;对象的创建速率和存活率变动很大;应用不希望停顿时间长(长于0.5s甚至1s)。G1将空间划分成很多块(Region),然后他们各自进行回收。堆比较大的时候可以采用,采用复制算法,碎片化问题不严重。整体上看属于标记整理算法,局部(region之间)属于复制算法。G1 需要记忆集 (具体来说是卡表)来记录drdxl代和老年代之间的引用关系,这种数据结构在 G1 中需要占用大量的内存,可能达到整个堆内存容量的 20% 甚至更多。而且 G1 中维护记忆集的成本较高,带来了更高的执行负载,影响效率。所以 CMS 在小内存应用上的表现要优于 G1,而大内存应用上 G1 更有优势,大小内存的界限是6GB到8GB

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。