今天会是有Offer的一天么:面试时被问到的JAVA中的垃圾收集算法

前几天收到了美团实习生的面试通知(后台开发),感觉真的是慌得不行。昨天视频一面之后今天打算把面试的主要内容做一个总结回顾,方便和小伙伴们一起交流学习。

在这里插入图片描述

面试官:咱们来谈谈JAVA方面的相关知识吧。知道有什么垃圾收集器么,知道有哪些垃圾收集算法么?

我:心中一阵窃喜。这不是我前两天晚上打游戏。呸这不是前两天晚上我在《JAVA虚拟机》这本书第三章看过的内容么。其实这个面试问题非常常见了,基本各个公司JAVA面试都会涉及到这个问题。

在这里插入图片描述

我们在进行垃圾回收的时候,首先要明白一点:什么样的对象会被回收掉?一定是已经“死亡”不再会被使用的对象。那怎么判断对象是否死亡或者存活呢。接下来我们就介绍两种判断对象是否“死亡”的算法。

引用计数法

简单点说就是给每个对象添加一个引用计数器。当一个地方引用这个对象的时候就加一。同理,当一个引用失效的时候,计数器的值就减一。如果计数器的值等于0,也就意味着,这个对象不会在被使用,可以被垃圾回收器回收。

那引用计数法有什么优缺点呢?

总的来说引用计数法实现非常简单,效率比较高,但是在主流的JAVA虚拟机里却没有选用引用计数法来管理内存,这是为什么呢?
因为引用计数法会带来一个问题就是很那解决对象之间的循环引用问题。例如说:
在这里插入图片描述对象ceshi1和对象ceshi2都有一个字段refertest,它们之间相互引用,实际上这两个对象都无法再被访问,但是它们相互引用导致它们的引用计数都不为0,于是GC垃圾收集器无法回收它们。

额外说一下JAVA中的引用,面试的时候被问到过。

JAVA中的引用可以分为四种,由强到弱依次是:强引用、软引用、弱引用以及虚引用。具体区别如下(JAVA虚拟机这本书86页有详细介绍):
1.强引用:我们最常见的引用,例如声明一个学生类:Student A=new Student(),我们称它为强引用。对于一个强引用,只要引用还存在,垃圾回收器永远都不会回收掉引用的对象。
2.软引用:它稍稍若于强引用,主要用来管理一些还有用但是并不是必须的对象。对于弱引用,在要发生内存溢出之前,会把这些对象列进范围进行二次回收。也就是说对于软引用,它只能存活到下次将要发生内存溢出之前。
3.弱引用:要更弱一些,对于弱引用只能存活到下次垃圾收集发生之前(无论内存是否足够都会被回收)。
4.虚引用:最弱的一种引用。书上给的解释是:为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

可达性算法分析

在JAVA的主流实现中,我们是通过可达性算法分析来判断对象是否存活。我们首先从一系列称为“GC Roots”的对象开始遍历,你可以把“GC Roots”理解为我们数据结构中树的根节点。那么从这些节点开始向下遍历搜索,可以遍历到的对象我们称它是存活的。如果一个对象对于任何一个“GC Roots”都不可达,那么我们判定它是可以被回收的。
如图对于Object1,Object2,Object3是可达的,Object4是不可达的,即Object4是可以被回收的

在这里插入图片描述

那什么样的对象可以作为GC Roots对象呢?在JAVA中一般可以作为GC Roots的对象包括四种:

1.虚拟机栈( 栈帧中的本地变量表) 中引用的对象。
2.方法区中类静态属性引用的对象。
3.方法区中常量引用的对象。
4.本地方法栈中JNI( 即一般说的Native方法) 引用的对象。

写到这就涉及到了JAVA中的内存区域,这一点我们以后再总结。

那对于我们判断已经“死亡”的对象我们该如何进行回收呢?这就涉及到了我们接下来要说的垃圾收集算法。

垃圾收集算法是个什么鬼?

标记清除算法(CMS收集器使用)

主要分为两个步骤,“标记”阶段和“清除”阶段。第一步标记出所需要回收的对象,在标记完成之后收回标记的对象。标记清除算法带来的最大的问题就是会产生大量不连续的内存碎片,因为标记清除算法并没有对清除过的空间进行整理。太多不连续的内存碎片可能会导致你在下一次分配大对象的时候,无法找到足够的内存导致触发GC。
在这里插入图片描述

复制算法

复制算法将内存分为两块,每次只使用其中的一块,当一块用完之后就把存活的对象一次性复制到另一块上,再把使用过的一块一次性清除掉。带来的好处就是不会产生内存碎片,但是会导致内存的利用率不足,每次只能使用一半的内存。
在这里插入图片描述

标记-整理算法

标记-整理算法,标记过程与“标记清除”算法一样,但在标记之后不是就直接清除,而是让所有存活的对象向一段移动,然后清除掉边界外的内存。
在这里插入图片描述

分代收集算法

分代收集算法的思想就是:根据对象存活周期的不同将内存划分为新生代和老年代。 在新生代中的对象朝生暮死, 每次垃圾收集时都有大量对象“死亡”, 所以我们选择使用复制算法。我们将新生代内存分为一块Eden空间和两块Survivor空间, 每次使用Eden空间和一块Survivor空间。Eden和Survivor空间的默认比是8:1,因此我们每次能够利用90%的空间(Eden和一块Survivor),当回收的时候, 将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上, 最后清理掉Eden和刚才用过的Survivor空间。 而老年代中对象存活率比较高所以我们使用“标记清理”(CMS收集器基于标记清除算法)或“标记整理”算法(Serial Old收集器和Parallel Old收集器基于标记整理算法)来进行回收。

在这里插入图片描述

内存分配策略

新生代GC( Minor GC) : 指发生在新生代的垃圾收集,Minor GC非常频繁, 一般回收速度也比较快。
老年代GC( Full GC) : 指发生在老年代的GC, 出现了Full GC, 经常会伴随至少一次的Minor GC( 但非绝对的)。 Full GC的速度一般会比Minor GC慢10倍以上。
一般情况对象在新生代Eden区中分配。 当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。大对象以及长期存活的对象将直接进入老年代。虚拟机给每个对象定义了一个对象年龄。如果对象经过第一次Minor GC后仍然存活, 它的对象年龄就增加1岁, 当它的年龄增加一定年龄时( 默认为15岁), 就会晋升到老年代中。在发生Minor GC之前, 虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间, 如果大于, 那么直接进行Minor GC。 如果小于,虚拟机会查看是否设置允许担保失败。 如果允许, 那么会检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小, 如果大于,将尝试着进行Minor GC,尽管这次Minor GC是有风险的; 如果小于或者不允许担保失败,那这时也要改为进行一次Full GC。
在这里插入图片描述

关于垃圾收集器相关知识过两天再做总结,不能再熬夜修仙了。

在这里插入图片描述

感觉今年找实习比往年难了好多,毕竟是大环境影响,好多公司缩招,感觉七八月开始的秋招可能也会比往年难很多。不过时也命也,大家一起加油一起好好准备吧,相信每个人都能拿到属于自己的Offer。

最后分享一句特别喜欢的话:“梦想就是这样,我们常常受挫,我们也常常收获”。

发布了4 篇原创文章 · 获赞 3 · 访问量 2474

猜你喜欢

转载自blog.csdn.net/HZGuilty/article/details/105542381