文章目录
-
- 面试官: 今天我们来聊聊JVM吧!!!
- 面试官:那你先说一下 JVM 的内存区域有哪些?
- 面试官:很好,你刚刚提到永久代,那你知道永久代和元空间的区别吗
- 面试官:很不错,那我再问问,那你知道为什么 Java8 之后要移除永久代替换成元空间吗?
- 面试官:JMM 是什么能简单说一下么?
- 面试官:那你能聊聊主内存和工作内存是如何交互操作的吗?
- 面试官:我们聊聊堆这块内存,你刚刚提到了对象是分配在堆内存里的,那怎么判断是否要回收呢?
- 面试官:老年代的对象可能引用新生代的对象,那标记存活对象的时候,需要扫描老年代中的所有对象。因为该对象拥有对新生代对象的引用,那么这个引用也会被称为GC Roots。那不是得又做全堆扫描?成本太高了吧?
- 面试官:不错,你刚刚提到了引用,能说说引用分为那几种吗?还有被引用的对象一定能活吗?
- 面试官:对象引用被置为 null 该对象会立即被回收吗?
- 面试官:你刚刚提到垃圾回收,你知道哪几种垃圾回收算法?
- 面试官:行,那你说说这几种垃圾回收算法分别是怎么做的吧!
- 面试官:先说说CMS回收的过程?
- 面试官:串行(serial)收集器和吞吐量收集器有什么区别?
- 面试官:我们聊聊 G1 垃圾收集器吧,你知道 G1 分区是怎么样的吗?
- 面试官:你知道 G1 中的 RSet 和 CSet 吗?
- 面试官:你知道 G1 是怎么高效的标记这些对象的呢?
- 面试官:可以这么说,那你聊聊三色标记法吧
- 面试官:那你简单聊一聊它的标记过程是怎样的吧
- 面试官:并发标记时,即标记期间应用线程还在继续跑,对象间的引用可能发生变化,就会出现多标和漏标的情况,你能说说这两种情况吗?
- 面试官:那漏标记呢?
- 面试官:既然漏标记非常危险,那它是怎么解决的呢?
- 面试官:聊聊 G1 的收集模式吧
- 面试官:你用过哪几个 G1 垃圾回收器哪几个重要参数?
- 面试官:很好,你刚刚提到可达性分析,你知道在可达性分析过程中借助了OopMap来增加效率吗?
- 面试官:那你能了说说这个 OopMap吗?以及什么是 SavePoint 什么是 SaveRegion?
面试官: 今天我们来聊聊JVM吧!!!
我:好的,我准备好了。
面试官:来,给你张草稿纸,需要方便你画图和写伪码。
我:谢谢!
面试官:那你先说一下 JVM 的内存区域有哪些?
我:好的。
我: 根据 《Java 虚拟机规范》的规定,Java 虚拟机锁管理的内存将会包括程序计数器、虚拟机栈、本地方法栈、堆、方法区这几个运行时区域。
我:我挨个讲一下这些内存区域的用途吧!
我:首先是程序计数器,是一块相对较小的空间,它相当于一个执行程序的行号指示器。它记录程序下一条需要执行的指令的地址,分支、循环、跳转、异常处理、线程恢复等基础功能并且是线程私有的。
我:Java 虚拟机栈,我们在写程序的时候都会为了复用封装很多个方法,每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等这些在执行函数时会用到的信息信息。和程序计数器一样,这块内存也是线程私有的。
我:本地方法栈,它与虚拟机栈的作用非常的像,它们的区别就是虚拟机栈是为执行 Java 方法服务的,而本地方法栈是为 native 服务的,它也是线程私有的
我:Java堆,在日常的代码里面处处都有创建对象的代码,堆就是存放这些对象的内存区域,可想而知这块区域也是最大的一块区域,而且它是被所有线程共享的区域
我:最后一个方法区,他和Java堆一样是在各个线程中共享的内存区域,它是用来存储已经被虚拟机加载的类信息、常量、静态变量以及即时编译后的代码,在 HotSpot 虚拟机上 虚拟机的设计人员用永久代来实现方法区。
面试官:很好,你刚刚提到永久代,那你知道永久代和元空间的区别吗
我:嗯嗯,知道的
我:我们刚刚讲的方法区这块运行内存它是在《Java 虚拟机规范》里面定义的。不同的虚拟机对这个方法区的定义有不同的实现,在HotSpot虚拟机中在 JDK1.7 版本之前的方法区的实现叫永久代,JDK1.8 之后的实现叫元空间
我:至于它们两个的区别嘛,最大的区别在于元空间并不在虚拟机中,而是在本地内存。
我:除此之外它们参数调配也改变了,JDK1.7 通过-xx:Permsize来设置永久代初始分配空间和-XX:MaxPermsize来设定永久代最大可分配空间,而元空间大小可以使用参数 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize指定。
面试官:很不错,那我再问问,那你知道为什么 Java8 之后要移除永久代替换成元空间吗?
我:这个嘛!
我:按照官方上的说法是为了 JRockit 和 Hotspot 融合工作,JRockit不需要配置永久代,因为JRockit没有永久代
我:而且程序员很难确定永久代的空间大小,某些业务场景下不断的做类加载等工作会导致永久代空间不足,但是元空间使用本地内存,默认情况下本地大小限制的。
我:还有一点就是调优困难,方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。一般来说这个区域的回收效果比较难令人满意但是这部分区域的回收有时又确实是必要的。以前sun公司的Bug列表中,曾出现过的若干个严重的Bug就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。
面试官:JMM 是什么能简单说一下么?
我:JMM (Java Memory Model)是Java内存模型,JMM定义了程序中各个共享变量的访问规则,即在虚拟机中将变量存储到内存和从内存读取变量这样的底层细节
我:我来给您画一画这个图:
我:每条线程还有自己的工作内存,线程的工作内存保存了主内存的副本拷贝,对变量的操作在工作内存中进行,不能直接操作主内存中的变量.不同线程间无法直接访问对方的工作内存变量,需要通过主内存完成
面试官:那你能聊聊主内存和工作内存是如何交互操作的吗?
我:可以的!
我:Java 内存模型定义了以下8种操作来完成,它们都是原子操作(除了对 long 和double 类型的变量)
lock(锁定),作用于主内存中的变量,它将一个变量标志为一个线程独占的状态。
unlock(解锁),作用于主内存中的变量,解除变量的锁定状态,被解除锁定状态的变量才能被其他线程锁定。
read(读取),作用于主内存中的变量,它把一个变量的值从主内存中传递到工作内存,以便进行下一步的load操作。
load(载入),作用于工作内存中的变量,它把read操作传递来的变量值放到工作内存中的变量副本中。
use(使用),作用于工作内存中的变量,这个操作把变量副本中的值传递给执行引擎。当执行需要使用到变量值的字节码指令的时候就会执行这个操作。
assign(赋值),作用于工作内存中的变量,接收执行引擎传递过来的值,将其赋给工作内存中的变量。当执行赋值的字节码指令的时候就会执行这个操作。
store(存储),作用于工作内存中的变量,它把工作内存中的值传递到主内存中来,以便进行下一步write操作。
write(写入),作用于主内存中的变量,它把store传递过来的值放到主内存的变量中。
这里给您画一下具体的交互流程图:
面试官:可以的,我们接着来聊。
面试官:我们聊聊堆这块内存,你刚刚提到了对象是分配在堆内存里的,那怎么判断是否要回收呢?
我:主流的商用程序都是用可达性分析算法来判定对象是否存活,还有一种引用计数的方法也可以判断,不过有些缺陷