Java面经之垃圾回收机制

共 6947字,需浏览 14分钟

 ·

2021-03-26 21:51

点击上方蓝字关注我们




Java垃圾回收机制



一. 在java中如何判断一个对象是垃圾?

1. 如何判断一个对象是垃圾?

首先,在Java中,没有任何引用的对象就是一个垃圾。


2. 在Java中,我们用什么方法来判断这个对象是垃圾?

引用计数算法

可达性分析算法。


3. 什么是引用计算算法?

通过判断对象的引用数量,来决定对象是否可以被回收。

每个对象实例都有一个引用计数器,如果对象被引用,引用计数器加1,引用完成,则-1.

任何引用计算为零的对象实例都可以当做垃圾被回收。


4. 引用计数算法的优缺点是什么?

优点:执行效率高,程序执行受影响小。

缺点:无法检测出循环引用的情况,导致内存泄漏。


5. 什么是可达性分析算法?

通过判断对象的引用链是否可达来决定对象是否可以被回收。

小知识:可达性分析算法是从离散数学中的图论引入的,程序把所有的引用关系看成是一张图,通过一系列名为GC Root 作为起始点,从这些节点开始向下搜索,搜索所走过的路径就被称为引用链,(也就是:Reference Chain ),当一个对象从GC root 开始 没有任何引用链相连,从图论上说,就是从GC Root 到这个对象是不可达的,那这时就证明了这个对象是不可用的。它也就被标记为垃圾了。

具体执行过程:垃圾回收器会对整个对象图进行遍历,它从GC root 开始,一直查找其他对象,垃圾回收器会对所有遍历到的对象,标记为存活,GC root 无法到达的对象,就被标记为不可达对象。也就被标记为垃圾。


补充:哪些对象可以被当做GC Root ?

虚拟机栈中引用的对象,(栈帧中的本地变量表)

方法区中的常量引用的对象。

方法区中类静态属性引用的对象。

本地方法栈中JNI(Native方法)的引用对象。

活跃线程的引用对象。


二:谈谈你对垃圾回收算法的了解。

1. 垃圾回收算法

标记清除算法

复制算法

标记整理算法。

分代收集算法。


2. 什么是标记清除算法?

标记:从根集合进行扫描,对存活的对象进行标记。

清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存。


3. 标记-清除算法的缺点是什么?

会造成很多内存碎片。

补充:为什么会造成很多内存碎片?

因为标记清除算法。不需要进行对象的移动,并且仅对不存活的对象进行处理,因此标记清除以后,会造成大量的不连续的内存碎片,空间碎片太多,可能会导致,在以后需要创建较大对象时候,无法找到足够的连续内存,而不得不提前触发另一次垃圾回收,


4. 什么是复制算法?(复制算法:适合对象存活率低的场景)

复制算法将内存按容量按一定的比例分为对象面和空闲面。

对象在对象面上创建。

存活的对象被从对象面复制到空闲面。(这个时候空闲面变为对象面)

将对象面中所有的对象全部清空。(这个时候,对象面变为空闲面)。年轻代很多垃圾收集器,都用这个算法回收。


5. 复制算法的优点,缺点各是什么?

优点:

解决了内存碎片化的问题。

顺序分配内存,简单高效。

适用于对象存活率低的场景。

缺点:

对于对象存活率高的场景不适合。


6. 什么是标记-整理算法?

标记:从根集合进行扫描,对存活的对象进行标记。

清除:移动所有存活的对象, 且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。


7. 标记-整理算法有什么优点?

避免了内存的不连续性。

不用设置两块内存互换。

适用于存活率高的场景。


8. 什么是分代收集算法?

垃圾回收算法的组合拳。

按照对象生命周期的不同划分区域以 采用不同的垃圾回收算法。

目的:提高JVM的 回收效率。


补充:

JDK7 ,及以前:垃圾回收区分为三部分:Eden Space(年轻代), Tenured Space(老年代) , Permanent Space(永久代).

年轻代:年轻代又被 划分为 Eden Space,(Eden 区 ) , From Space, To Space.(也就是 两个Survivor 区)

Eden区:伊甸园。如果Eden区放不下,会直接放在 Survivor 区,或者老年区。

Survivor 区分为:from 区, to 区, 划分不一定。


JDK8 : 垃圾回收区分为两部分:Eden Space, Tenured Space , 没有 Permanent Space。


9. GC的分类,你了解吗?都有什么?

GC:被分为两类。Minor GC, Full GC。

Minor GC:用来处理年轻代区域

Full GC:用来收集老年代区域。


10. 年轻代的特点?

年轻代:尽可能的快速收集掉 那些生命周期短的对象。

年轻代(Young)和 老年代(Old) 将堆的空间划分为 1 :2. 总共三份,

年轻代:将空间划分为 一个Eden区,两个Survivor区。比例:8:1:1.


11. 设置Minor GC的最大次数的参数是什么?-XX:MaxTenuringThreshold:


12. 什么样的对象会晋升到老年代?

经历一定的Minor次数依然存活的对象。

Survivor区中存放不下的对象。

新生成的大对象。()。


13. 常用的调优参数知道吗?

-XX : SurvivorRatio: Eden和Survivor 的比例,默认 8:1

-XX : NewRatio:老年代和年轻代的内存的大小比例。默认是2:1

-XX: MaxTenURingThreshold:对象从年轻代晋升到老生代经过GC次数的最大阈值。


14. 老年代的作用是什么? 存放生命周期较长的对象。

补充:年轻代:使用复制算法。

老年代:使用 标记-清理算法 和 标记-整理算法 清理垃圾回收。


15. 当触发老年代垃圾回收的时候, 发生了什么?

当触发老年代垃圾回收的时候,会伴随着新生代的垃圾回收,老年代的垃圾回收。

这里经常发生的是Full GC 和 Major GC 。(这两个GC 是等价的)。用来收集整个GC 堆。

Full GC 比Minor GC 慢的多,大概 10倍, 执行频率低。


16. 什么时候会触发Full GC?

老年代空间不足的时候。

永久代空间不足的时候.(这里只指JDK7 及以前,从JDK8 开始没有 永久区,JDK8 用元空间取代了永久代,以减少发生 Full GC 的频率)

CMS GC 时出现 Promotion failed ,concurrent mode failure .

Minor GC 晋升到 老年代的平均大小大于老年代的剩余空间。

调用 System.gc() .

使用 RMI 来进行RPC 或者 管理JDK应用,每小时执行一次 Full GC。1 小时进行一次垃圾收集。


17. 什么是 Stop-the-World?

Stop-the-World:JVM由于要执行GC 而停止了应用程序的执行。

这种现象在任何一种GC算法中都会发生。直到GC 任务完成。

多数GC 优化通过减少stop-the-world 发生的时间来提高程序性能。

所谓的Stop the World机制,简称STW,即在执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集收集器线程之外的线程都被挂起。

此时,系统只能允许GC线程进行运行,其他线程则会全部暂停,等待GC线程执行完毕后才能再次运行。

这些工作都是由虚拟机在后台自动发起和自动完成的,是在用户不可见的情况下把用户正常工作的线程全部停下来,这对于很多的应用程序,尤其是那些对于实时性要求很高的程序来说是难以接受的。


除了垃圾回收外,还有一些其他的操作会触发STW。


并发(CMS)收集器:(-XX:+UseConcMarkSweepGC):用于对年老代进行回收。

串行收集和并行收集在进行垃圾回收工作时,需要暂停整个运行环境,因此,系统在垃圾回收时会有明显的暂停,并且暂停时间会因为堆越大而越长。

并发收集器可以保证大部分工作都并发执行,应用不暂停,这个垃圾收集器适用于尽可能减少应用的停顿时间,减少full gc发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代,适用于响应时间优先的系统(比较常用,适用于较大型的系统)。

CMS并非没有暂停,而是用两次短暂停来替代串行标记整理算法的长暂停。


18. 什么是GC优化?

GC 优化就是指通过减少stop-the-world 发生的时间,来提高程序性能, 从而让系统实现高吞吐,低停顿的特点。


19. 垃圾收集器里面 Safepoint(安全点) 是什么?

定义:在可达性分析中,分析哪个对象没有引用的时候,必须在一个快照的情况下进行,在这个点,所有的线程都被冻结了,不可以出现,分析过程中,对象引用还在变化的情况,因此分析结果在某个节点,具备确定性,该节点被叫做安全点。(该节点不是随便哪个点就停下来,而是到达安全点,就停顿下来。)一旦GC 发生,就让所有的线程都跑到最新的安全点,再停顿下来,如果发现线程不在安全点,就恢复线程,等其跑到安全点,再停下来。

补充:安全点不能选择太少,这样会让GC 等待太长的时间。也不能太多,会让程序增加运行的负荷。


总结:可达性分析过程中对象引用不会发生变化的点。

提问:什么地方会产生 Safepoint (安全点)?

方法调用,循环跳转,异常跳转等。


20. JVM 的运行模式有几种?

两种: Server 和Client 。

区别:Client启动较快,Server启动慢,但是Server 启动进入稳定期,Server 比Client 运行快。这是因为Server 模式启动运行的是采用的是重型JVM虚拟机,对程序采用了更多的优化,而Client模式启动的采用的是 轻量级JVM虚拟机。用java -version 就可以查询虚拟机启动模式。


21. 常见的 垃圾收集器有哪些?,以及它们之间的关系?



年轻代:Serial,ParNew ,Parallel Scavenge.

老年代:CMS, Serial Old(MSC), Parallel Old。


22.年轻代常见的垃圾收集器有哪些?各有什么特点?

Serial 收集器:(-XX:+ UseSerialGC ,复制算法)。

单线程收集,进行垃圾收集时,必须暂停所有工作线程。(适合于用户交互比较多的场景。专注 用户线程停顿时间)

简单高效,Client模式下默认的年轻代收集器。

ParNew收集器:(-XX:+ UseParNewGC : 复制算法),只有它可以和CMS收集器配合工作。

多线程收集,其余的行为,特点,和Serial收集器一样。(适合于用户交互比较多的场景。专注 用户线程停顿时间)

单核执行效率不如Serial ,在多核下执行才有优势。


Parallel Scavenge 收集器(-XX: + UseParallelGC ,复制算法)。

比起关注用户线程停顿时间,更关注系统的吞吐量。(适合不需要与用户过多交互的场景)

在多核下执行才有优势,Server模式下默认的年轻代收集器。

补充:什么是吞吐量?

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


23.年老代常见的垃圾收集器有哪些?各有什么特点?

Serial Old 收集器(-XX : UseSerialOldGC , 标记-整理算法):

单线程收集,进行垃圾收集时,必须暂停所有工作线程。

简单高效,Client模式下默认的老年代收集器。

Parallel Old 收集器 (-XX :UseParallelOldGC, 标记整理算法)

多线程,吞吐量优先。

CMS 收集器:(-XX:+ UseConcMarkSweepGC, 标记-清除算法)

CMS的优点:可以做到垃圾回收线程几乎能与用户线程做到同时工作。如果你的应用程序,对停顿比较敏感,并且在应用程序运行的时候可以提供更大的内存和更多的CPU,那么使用CMS 会有更大的好处。还有在JVM中,还有相对较多存活时间较长的对象,会更适合使用CMS。

CMS收集器垃圾回收过程是什么,用的什么垃圾回收算法(标记清除算法)?

初始标记:Stop-the-world.(这个时候需要虚拟机停止正在执行的任务),这个过程从垃圾回收的根对象开始,直接扫描到能够和根对象直接关联的对象,并进行标记。

并发标记:并发追溯标记,程序不会停顿。(并发标记紧随初始标记阶段,并发标记阶段,并发标记线程和应用程序线程并发执行,所以用户不会感受到停顿)。

并发预清理:查找执行并发标记阶段,从年轻代晋升到老年代的对象。

重新标记:暂停虚拟机,扫描CMS堆中的剩余对象。(从根对象开始,向下扫描)

并发清理:清理垃圾对象,程序不会停顿。(这个阶段:垃圾收集线程和应用线程并发执行)。

并发重置阶段:重置CMS收集器的数据结构。


24. 什么是 G1 (Garbage First)收集器?。

G1 收集器(-XX:+ UseG1GC ,复制 + 标记- 整理算法.

特点:

并行和并发(使用多个CPU来缩短stop-the-world 的停顿时间,与用户线程并发执行)

分代收集(G1 独立管理整个堆,但是能够采用不同的方式去处理新创建的对象和已经存活了一段时间熬过了多次GC清理的旧对象,这样可以获得更好的收集效果)。

空间整合(标记-整理算法:解决内存碎片的问题)

可预测的停顿。(G1 能够建立可预测停顿的时间模型,能让使用者,明确指定在一个长度n 毫秒的时间片段内,消耗在垃圾收集时间不得超过n 毫秒。)


25. G1 收集器对空间的划分是怎样的?

将整个Java堆内存划分成多个大小相等的独立区域Region 。

年轻代和老年代不再物理隔离。(它们可以是一部分不是物理空间连续的内存了,这个时间再分配内存时,就不需要一段连续的内存,即不用在JVM中指定哪些属于年轻代,哪些属于老年代。因为年轻代被回收以后,就变成可用的Region ,也可以被分配为老年代。)

G1 收集器是 并行stop-the-world收集器, 当一个年轻代GC 发生垃圾回收时,整个年轻代会被回收,G1的老年代回收器有所不同于其他收集器的老年代,G1 的老年代不需要整个老年代进行回收,只有一部分Region被调用,G1 GC 年轻代由 Eden和Survivor 组成,当一个JVM分配一个Eden Region 失败后,就会触发一个年轻代回收,意味着Eden区间满了,然后GC 开始释放空间,第一个年轻代收集器,会移动所有的存储对象,从Eden Region 到 Survivor Region,这就是 copy Survivor的过程。


三. GC 相关的面试题:

1. Object 的finalize() 方法的作用是否与C++ 的析构函数作用相同?

Object 的finalize() 方法的作用是否与C++ 的析构函数作用是不同的,C++ 中的析构函数调用时机是确定的,即对象离开作用域后,就会被清除,但是Java 中的 finalize() 方法具有不确定性。

当一个垃圾收集器宣布一个对象死亡时,至少要经过两次的标记过程,在进行可达性分析的过程中,发现没有和GC Root相连的引用链就会被标记,判断是否执行finalize () 方法,如果对象覆盖finalize () 方法,并且未被引用过,这个对象就会被放置在 F-Queue队列中,并在稍后由一个虚拟机自动建立的低优先级的Finalize() 线程 ,去执行触发Finalize() 方法,由于优先级较低,方法执行随时可能会被终止。

方法随时可能被终止。(因为优先级较低)

finalize() 给予对象最后一次重生的机会。


2. Java中的强引用,软引用,弱引用,虚引用有什么用?

软引用:

对象处在有用但是非必须的状态。

只有当内存空间不足时,GC会回收该引用的对象的内存。

可以用来实现高速缓存。

String str = new String("abc"); // 强引用。

SoftReference<String> softRef = new SoftReference<String> (str); // 软引用

弱引用:

非必须的对象,比软引用更弱一些。

GC 时会被回收。

被回收的概率也不大,因为GC线程的优先级比较低。

适用于引用偶尔被使用且不影响垃圾收集的对象。

虚引用:

不会决定对象的生命周期。

任何时候都可能被垃圾收集器回收。

跟踪对象被垃圾收集器回收的活动,起哨兵的作用。(垃圾回收要开始的时候,会先收集这类对象)。

必须和引用队列Reference Queue 联合使用。


3. 引用队列是干嘛的?

引用队列,没有实际的存储结构,存储逻辑依赖于内部节点之间的关系来表达。

存储与引用队列关联的并且被GC 的软引用,弱引用,以及虚引用。











往期精彩




快手-Java开发面经(一)
字节跳动-Java开发面经(一)
顺丰-Java开发面经(一)


 扫码二维码

获取更多面经

扶遥就业


浏览 8
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报