2020年金三银四Java面试:JVM会问些啥?

2020年金三银四Java面试:JVM会问些啥?

先扯些闲话,从 2018 年下半年开始,大家都在说互联网的“寒冬”来临,特别是金融类互联网公司,由一系列“暴雷”事件导致的连锁反应,@Python大星 身边好多在金融类公司上班的程序员离职,那年你熟悉 jvm 是很大的加分项。时间拉回到现在 2020 年,jvm 似乎是正常操作,如果不清楚的话等于给自己减分。

面试官:JVM 运行时数据区?

Python 小星:balabala

面试官:你说堆内存是线程共享的,你确定吗?

Python 小星:确定

面试官:你知道内存逃逸是什么?

Python 小星:......

面试官:可达性分析算法中的根节点有哪些?

Python 小星:balabala

面试官:CMS 和 G1 有什么区别?

Python 小星:balabala

面试官:怎样进行 GC 调优

Python 小星:balabala

面试官:怎样排查 CPU 飙高,内存飙高?

Python 小星:balabala

如果你都清楚这些问题,下面可以忽略。

JVM 运行时数据区

2020年金三银四Java面试:JVM会问些啥?

1.8 同 1.7 比,最大的差别就是:元数据区取代了永久代。元空间的本质和永久代类似,都是对 JVM 规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元数据空间并不在虚拟机中,而是使用本地内存。

1、堆内存都是共享的吗???

这句话不完全正确。Java 对象的引用指向内存区域,然后再初始化,如果并发情况下,同时指向一个区域,会出现问题。在 HotSpot 虚拟机中的方案 TLAB(Thread Local Allocation Buffer),这部分 Buffer 是从堆中划分出来的,是线程独享的。

2、什么是内存逃逸???

第一次听说这个还是比较懵逼的。我们看看什么地方会出现内存逃逸,有咩有你熟悉的代码?

2020年金三银四Java面试:JVM会问些啥?

stringBuffer 虽然是方法内的局部变量,但是 stringBuffer 被当作返回值返回,这样 stringBuffer 可能被其他的方法所改变,作用域就不仅仅在本方法内啦,这样就是逃逸到了方法外部。可以在 return 时,加上 stringBuffer.toString()

JVM 如何判断对象已经“死亡”?

1、引用计数法(早期策略,已淘汰)

通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为 0 时,那么该对象就会被回收。



① 当一个对象被创建时,且将该对象实例分配给一个变量,该变量计数设置为 1

② 当任何其它变量被赋值为这个对象的引用时,计数加 1(a = b, 则 b 引用的对象实例的计数器 + 1)

③ 当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减 1

④ 当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减 1

优点:

引用计数收集器可以很快的执行,交织在程序运行中。对程序需要不被长时间打断的实时环境比较有利

缺点:

无法检测出循环引用,如下图

2020年金三银四Java面试:JVM会问些啥?

2、可达性分析算法(GC Roots Tracing)

2020年金三银四Java面试:JVM会问些啥?

从一个节点 GC ROOT 开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。

我们说的 GC Roots 如下:

  • 虚拟机栈中的引用对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用对象
  • 本地方法栈中 JNI 引用对象
  • 刚开始,@Python大星 死记,但是 n 周后就没啥印象。如果知道为什么选这个作为 GC Roots,以后至少你会说出几个。

    GC 管理的主要区域是 Java 堆,堆里不是有新生代和老年代,一般情况下只针对堆进行垃圾回收。

    方法区、栈和本地方法区不被 GC 所管理,因而选择这些区域内的对象作为 GC roots, 被 GC roots 引用的对象不被 GC 回收。

    GC 的垃圾回收器

    1、Java 堆

    作为 GC 主要回收区域,必须盘盘 Java 堆

    2020年金三银四Java面试:JVM会问些啥?

    Java堆

    光这张图,面试官可以问很多东西,以下一笔带过,如有误,评论区你倒是说出来啊!

    ① 为什么要划分新生代和老年代???

    分代回收,提高 gc 回收效率

    ② 为什么新生代与老年代比例是1:2???

    新生代对象出生的地方,大部分对象朝生夕死。

    ③ 新生代为啥 8:1:1???

    这个参考整理算法,是内存利用率达到 90 %

    ④ 新生代的对象什么时候会到老年代???

    当对象年龄到达 15 岁会到老年代中,就像你的社保要交满 15 年一样。

    ⑤ 老年代是怎么回收的?

    老年代是 标记-清除算法回收

    ⑥ 什么时候 minor gc,什么时候 full gc???

    区内存满了,你品,你细品。

    2、GC 垃圾回收器

    2020年金三银四Java面试:JVM会问些啥?

    GC 回收器

    ① 按新生代和老年代划分

    新生代收集器:Serial、ParNew、Parallel Scavenge

    老年代收集器:CMS、Serial Old、Parallel Old

    整堆收集器: G1

    如果两个收集器之间存在连线,则说明它们可以搭配使用。

    ② 按处理方式划分

    串行垃圾回收器(Serial):只使用一个 GC 线程进行回收,会暂停所有的用户线程(淘汰)

    并行垃圾回收器(Parallel):多个 GC 线程进行垃圾回收,也会暂停所有用户线程,适用与和前台交互不强的场景

    发垃圾回收器(CMS :Concurrent-Mark-Sweep):用户线程和 GC 线程同时执行(并不一定时并行,可交替执行),不需要停顿用户线程

    2020年金三银四Java面试:JVM会问些啥?

    CMS

    共四步:初始标记–> 并发标记 —> 重新标记 —> 并发消除

    初始标记 (CMS initail mark): 只是标记 GC Roots 能直接关联的对象,速度快,仍需要【暂停用户线程】;

    并发标记 (CMS concurrent mark) 和用户线程一起:执行 GC Roots 跟踪的过程,不会暂停用户线程,主要标记过程,标记全部对象;

    重新标记 (CMS remark): 由于并发标记时,用户线程仍然运行,因此在清理前再次修正,需【暂停用户线程】;

    并发清除 (CMS concurrent sweep) 和用户线程一起:清除 GC Roots 不可达对象,基于标记的结果直接清除,不需要暂停用户线程。

    优势:并发收集停顿低,并发指 GC 线程与用户线程一起执行。
    缺点:并发执行对 CPU 压力大,采用标记清除算法会导致内存碎片。

    G1 垃圾回收器:将内存分割成不同的区域,然后并发的对其进行垃圾回收。

    这里主要说下 G1 和 CMS 的区别??

    ① 从 GC 回收器 图中我们也可以看出,CMS 主要回收老年代,需要搭配一个回收新生代的 GC 回收器,而 G1 是回收年老年代和新生代;

    ② 与 CMS 的 “标记 -- 清理” 算法不同,G1 从整体来看是基于 “标记整理” 算法实现的收集器;从局部上来看是基于 “复制” 算法实现的。

    jdk8 为例

    ① 查看 JVM 使用的什么 GC ???

    命令:

    java -XX:+PrintCommandLineFlags -version

    2020年金三银四Java面试:JVM会问些啥?

    从图中可以看出Java 8 中使用功能的是 ParallelGC

    具体指的是两个收集器:新生代的 Parallel Scavenge,老年代的 Parallel Old

    关于 cpu 飙高和内存溢出排查,后续单独写文章。码字不易,有问题评论下你快说啊!喜欢的话一键三连。最近一直在写 Java 相关的文章,想看 Python 的可以翻之前的文章,关注 @Python 大星,一个不务正业的程序员。


    @Python大星 | 文


    分享到:


    相關文章: