菜鸡每日一面系列打卡26天
每天一道面试题目
助力小伙伴轻松拿offer
坚持就是胜利,我们一起努力!
题目描述
Java的垃圾回收你了解多少?
题目分析
这简直是又一道明星面试题,其考查频率绝对不亚于JVM运行时数据区的考查频率。很多小伙伴都能讲出一些关于垃圾回收的理解,但很可能缺乏一个逻辑上的梳理,就容易在回答过程中流于零散,也就是我们常说的“想到哪说到哪”。
其实,在面试过程中是比较忌讳这一点的,因为这可能会让面试官觉得你思路不清晰,逻辑思维不强,没有经过系统地学习而是仅仅临时抱佛脚看了些应付面试的文章。接下来,就随菜鸡一起去系统总结一下Java垃圾回收的相关知识。
题目解答
01
垃圾回收基础
在讲述JVM运行时数据区的文章中,我们曾提到过,程序技术器、虚拟机栈、本地方法栈是线程私有的,在方法结束或者线程结束时,内存会随之回收。而堆和方法区是线程共享的,我们讨论的正是这部分内存区域的回收。
回收堆
哪些内存需要回收是以对象是否存活为依据判断的。判断对象是否存活有两种常用的方式:引用计数算法(存在循环引用的问题)和可达性分析算法。Java中采用的是可达性分析算法。可达性分析算法通过GC Roots的根对象作为起始结点集,根据引用关系向下搜索,如果某个对象不可达时,则说明该对象不再被引用。
这里可以延展的问题就比较多了。我们挑一些典型的问题来看一下:
哪些对象可以作为GC Roots?
在虚拟机栈(栈帧中的本地变量表)中引用的对象。
在方法区中类静态属性引用的对象。
在方法区中常量引用的对象。
在本地方法栈中JNI引用的对象。
JVM内部的引用。
所有被同步锁持有的对象。
反映JVM内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
其他临时性的GC Roots集合。
以上是《深入理解Java虚拟机》一书中的总结。
上述可作为GC Roots的对象如果纯靠死记硬背是没有任何意义的,即便你能倒背如流,也可能回答不出面试官的任何更进一步的疑问。我们不妨换个思路想一想,为什么这些对象可以作为GC Roots?当然是因为这些对象不会被GC。仔细观察上述可被选为GC Roots的对象,无一例外,这才算是理解了这个问题的精髓,只要这样说出你自己的理解,哪怕上述可作为GC Roots的对象回答不全,也不影响大局。
Java中的引用有哪几种?
四种,按照由强至弱依次为:
强引用(Object obj = new Object())
软引用(SoftReference,非必须,活到内存溢出异常之前)
弱引用(WeakReference,非必须,活到下次GC)
虚引用(PhantomReference,相当于不被引用,仅为了收到一个系统通知)。
不可达的对象一定会被回收吗?
不一定,被回收要至少经过两次标记。如果在finalize()方法中该对象与引用链建立了关联,就不会被回收,否则,基本是就要被回收了。
回收方法区
主要回收废弃的常量和不再使用的类型。由于对方法区的回收并没有严格的回收要求,在此不再展开。
02
垃圾回收算法
常见的垃圾回收算法有三种:标记-清除算法,标记-复制算法,标记-整理算法。以此为基础,结合分代收集理论进行垃圾回收。
标记-清除算法:算法分为“标记”和“清除”两个阶段,首先标记所有需要回收的对象,标记完成后统一回收所有被标记的对象。简单,但执行效率不稳定,容易产生内存碎片。
标记-复制算法:新生代常用的收集算法。将新生代分为一块较大的Eden和两块较小的Survivor。发生垃圾收集时,将Eden和Survivor中存活的对象复制到另外一块Survivor。HotSpot的默认大小Eden : Survivor : Survivor= 8 : 1 : 1。在Minor GC失败时,需要老年代进行担保,甚至触发Full GC。
标记-整理算法:老年代常用的收集算法,“标记”操作之后,所有存活的对象往内存空间的一端移动,然后清理边界外的内存。
事实上,在具体的垃圾收集器实现中,并未完全生搬硬套上述算法,这个在后续讲述垃圾收集器的时候会讲到,在此就不再展开。
以上便是菜鸡对垃圾回收的一些总结,供小伙伴们参考。
参考资料
《深入理解Java虚拟机》第3版 周志明
相关链接
学习 | 工作 | 分享
????长按关注“有理想的菜鸡”
只有你想不到,没有你学不到