上面这张图是我还是北漂的时候,在鼓楼附近的胡同里面拍的。
那天刚刚下完雨,路过这个地方的时候,一瞬间就被这五颜六色的门板和自行车给吸引了,于是拍下了这张图片。看到这张图片的时候我就很开心,多鲜活、多舒服的画面呀。
以后的文章里面我的第一张配图都用自己随时拍下的照片吧。分享生活、分享技术,哈哈。
好了,说回文章。
这次的文章我们聊聊jvm。jvm可以说是面试必备技能了。简历上写了,多问几句。简历上没写,也得提上几句。
我们先从一个简单的热身题入手,引出本文想要分享的内容。
当面试扯到jvm这一部分的时候,面试官大概率会问你jvm怎么判断哪些对象应该回收呢?
这种经典的面试题当然难不住你。
你会脱口而出引用计数算法和可达性分析算法。
然后你就停下来了吗?难道你不知道你回答了一句话之后,面试官肯定会接着问你能详细说明一下吗?所以,不要停。主动点,面试的时候主动点。你要抓住面试官把话语权交给你的宝贵机会,接着说啊,你得支棱起来
因为引用计数法的算法是这样的:在对象中添加一个引用计数器,每当一个地方引用它时,计数器就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。
但是这样的算法有个问题,是什么呢?
不经意间来一波自问自答。让面试官听的一愣一愣的。
就是不能解决循环依赖的问题。
并拿着自己准备的纸和笔快速的画出下面这样的图:
网络异常,图片无法展示|
Object 1和Object 2其实都可以被回收,但是它们之间还有相互引用,所以它们各自的计数器为1,则还是不会被回收。网络异常,图片无法展示|
所以,Java虚拟机没有采用引用计数法。它采用的是可达性分析算法。
可达性分析算法的思路就是通过一系列的“GC Roots”,也就是根对象作为起始节点集合,从根节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为引用链,如果某个对象到GC Roots间没有任何引用链相连。
用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。所以此对象就是可以被回收的对象。
说这句话的时候再次,快速的纸上画出下面的图:网络异常,图片无法展示|
网络异常,图片无法展示|
好了,到这里就可以把话语权交给面试官了。因为到这里,他接下来可以问的点有很多,你不知道他会问什么,比如:网络异常,图片无法展示|
你刚刚谈到了根节点,那你知道哪些对象可以作为根对象吗?
你刚刚谈到了引用,那你知道java里面有哪几种引用吗?
你刚刚谈到了可达性分析算法,那如果在该算法中被判定不可达对象,是不是一定会被回收呢?
谈谈你熟悉的垃圾回收器和他们的工作过程?
.......
上面的这些问题都太常规了,任何一份面经里面都会有这样的几个问题。
而本文要解决的是下面这个稍微不那么常见,但是你答题的过程中一定会提到的点“并发标记”、“浮动垃圾”。
CMS和G1都是有一个并发标记的过程,并发标记要解决什么问题?带来了什么问题?怎么解决这些问题呢?
网络异常,图片无法展示|
网络异常,图片无法展示|
网络异常,图片无法展示|
并发标记要解决什么问题?
刚刚我们谈到的可达性分析算法是需要一个理论上的前提的:该算法的全过程都需要基于一个能保障一致性的快照中才能够分析,这意味着必须全程冻结用户线程的运行。
为了不冻结用户线程的运行,那我们就需要让垃圾回收线程和用户线程同时运行。
所有我们来个反证法,先假设不并发标记,即只有垃圾回收线程在运行的流程是怎样的:
第一步是需要找到根节点,也就是我们常说的根节点枚举。
而在这个过程中,由于GC Roots是远远少于整个java堆中的全部对象的,而且在OopMap此类优化技巧的加持下,它带来的停顿时间是非常短暂且相对固定的,可以理解为不会随着堆里面的对象的增加而增加。大概就是下面这个图的意思:
网络异常,图片无法展示|
但是我们做完根节点枚举,只是做完了第一步。接下来,我们需要从GC Roots往下继续遍历对象图,进行"标记"过程。而这一步的停顿时间必然是随着java堆中的对象增加而增加的。大概就是下面这个图的意思:
这个逻辑不复杂:堆约大,存储的对象越多,对象图结构越复杂,要标记更多对象,所以产生的停顿时间也自然就长了。网络异常,图片无法展示|
所有,经过上面的分析,我们知道了,根节点的枚举阶段是不太耗时的,也不会随着java堆里面存储的对象增加而增加耗时。而"标记"过程的耗时是会随着java堆里面存储的对象增加而增加的。
"标记"阶段是所有使用可达性分析算法的垃圾回收器都有的阶段。因此我们可以知道,如果能够削减"标记"过程这部分的停顿时间,那么收益将是可观的。
所以并发标记要解决什么问题呢?
就是要消减这一部分的停顿时间。那就是让垃圾回收器和用户线程同时运行,并发工作。也就是我们说的并发标记的阶段。
网络异常,图片无法展示|
网络异常,图片无法展示|
网络异常,图片无法展示|