虚拟机相关面试题

JVM 相关知识

Posted by Song on December 18, 2019

基础知识部分

JDK 的主要组成部分及其作用?

  • JDK 是整个Java的核心,包括了Java 运行环境(Java Runtime Environment)、一堆 Java 工具 (javac、java、javadoc、JConsole等) 和 Java 基础的类库 (rt.jar) 。
  • JRE 包括程序发布、集成库、基础库、工具基础库和 JVM。
  • JVM 有自己虚拟的硬件,堆栈,寄存器等,还具有相应的指令系统。是运行 class 字节码虚拟计算机。

JVM 的主要组成部分及其作用?

  • 类加载器(ClassLoader)
  • 运行时数据区(Runtime Data Area)
  • 执行引擎(Execution Engine)
  • 本地库接口(Native Interface)

程序在执行之前先要把 java 代码转换成字节码(class文件),jvm 首先需要把字节码通过一定的方式 类加载器(ClassLoader) 把文件加载到内存中 运行时数据区(Runtime Data Area) ,而字节码文件是 jvm 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器 执行引擎(Execution Engine) 将字节码翻译成底层系统指令再交由 CPU 去执行,而这个过程中需要调用其他语言的接口 本地库接口(Native Interface) 来实现整个程序的功能,这就是这4个主要组成部分的职责与功能。

而我们通常所说的 jvm 组成指的是运行时数据区(Runtime Data Area),因为通常需要程序员调试分析的区域就是“运行时数据区”,或者更具体的来说就是“运行时数据区”里面的 Heap(堆)模块,那接下来我们来看运行时数据区(Runtime Data Area)是由哪些模块组成的。

  • 描述下运行时数据区结构图
  • 描述垃圾回收机制,新生代和老年代
  • 描述运行时数据区各部分 OOM 异常
  • 如何看 Dump 内存文件

JVM 运行时数据区包含哪些?

  • 程序计数器:线程私有;是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器;其值为字节码指令地址;唯一没有 OOM 区域。
  • Java虚拟机栈:线程私有;方法在执行的时候会创建一个栈帧,存储了局部变量表,操作数栈,动态连接,方法出口地址等;每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈;可能 StackOverflowError 或者 OutOfMemoryError。
  • 本地方法栈:线程私有;主要为虚拟机使用到的 Native 方法服务。Hotspot 合并到虚拟机栈。
  • 堆:线程共享;被所有线程共享的一块内存区域,在虚拟机启动时创建,用于存放对象实例和数组。并不是所有对象都存储在堆,JIT 编译优化,栈上分配、标量替换使得并不绝对;分为新生代(Eden、From Survivor、To Survivor) 和老年代;可能存在 TLAB (本地线程缓冲区);无法扩展时 OOM。
  • 方法区:线程共享;被所有线程共享的一块内存区域;用于存储已被虚拟机加载的类信息,常量,静态变量,及时编译器编译后的代码数据等;JDK 1.8 后已将方法区从永久代移除,改用 matespace 代替;内存回收也有常量池和类卸载;存在 OOM。

说下 JVM 的垃圾回收算法,各有什么特点?

  • 标记-清除算法:算法简单、实现简单,标记和清除两个过程效率不高,且会产生大量的空间碎片
  • 标记-整理算法:相对标记清除算法,效率较高,一次清除端外一侧内存;不会产生内存碎片问题
  • 复制算法:效率高,不会产生空间碎片,但是可使用内存减少为原先一般,且如果对象成活率高,复制次数较多影响效率
  • 分代算法:根据对象存活周期,将空间分为新生代和老年代,回收效率高且空间利用率高

详细介绍一下 CMS 垃圾回收器?

CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。 CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候回产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。

GC收集器有哪些?CMS收集器与G1收集器的特点。

  • Serial收集器
  • ParNew收集器
  • Parallel Scavenge收集器
  • Serial Old收集器
  • Parallel Old收集器
  • CMS收集器
  • G1收集器

并行收集器:串行收集器使用一个单独的线程进行收集,GC时服务有停顿时间 串行收集器:次要回收中使用多线程来执行 CMS收集器是基于“标记—清除” 算法实现的,经过多次标记才会被清除 G1从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的

1、并行于并发:G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。 2、分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。它能够采用不同的方式去处理新创建的对象和已经存活了一段时间,熬过多次GC的旧对象以获取更好的收集效果。 3、空间整合:与CMS的“标记–清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。 4、可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,

上面几个步骤的运作过程和CMS有很多相似之处。初始标记阶段仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS的值,让下一个阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这一阶段需要停顿线程,但是耗时很短,并发标记阶段是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段时耗时较长,但可与用户程序并发执行。

说下对象创建的 5 个步骤

  • 类加载:JVM遇到一条新建对象的指令时首先去检查这个指令的参数是否能在常量池中定义到一个类的符号引用
  • 分配内存:为对象分配内存。一种办法“指针碰撞”、一种办法“空闲列表”,最终常用的办法“本地线程缓冲分配(TLAB)”
  • 内存置零:将除对象头外的对象内存空间初始化为0
  • 设置对象头:对对象头进行必要设置
  • 初始化其他操作

说一下类加载的执行过程?

1.加载:根据查找路径找到相应的 class 文件然后导入; 2.检查:检查加载的 class 文件的正确性; 3.准备:给类中的静态变量分配内存空间; 4.解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址; 5.初始化:对静态变量和静态代码块执行初始化工作。

加载、验证、准备、解析、初始化。然后是使用和卸载了 通过全限定名来加载生成class对象到内存中,然后进行验证这个class文件,包括文件格式校验、元数据验证,字节码校验等。准备是对这个对象分配内存。解析是将符号引用转化为直接引用(指针引用),初始化就是开始执行构造器的代码。

Eden 和 Survivor 的比例分配

Eden 8 : Survivor 1。其中 Survivor 分为 From Survivor 和 To Survivor 他们不断执行分区交换

什么是 Class 文件? Class 文件主要的信息结构有哪些?

说下深拷贝和浅拷贝

什么是类加载器,类加载器有哪些?什么是双亲委派模型?

先说下类加载器。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间。类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。 类加载器分类:

启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被-Xbootclasspath参数所指定的路径中并且被虚拟机识别的类库; 扩展类加载器(Extension ClassLoader):负责加载JAVA_HOME/lib/ext目录中的类库或Java. ext. dirs系统变量指定的路径中的所有类库; 应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。

对象的内存布局是怎样的?

  • 对象头 i. 对象自身的运行时数据(Mark Word):hash 码、GC 分代年龄、锁状态标志;非确定的数据结构存储,以节省空间;32 位虚拟机 32 位长,64 位虚拟机 64 位长 ii. 类型指针:指向类元数据指针 iii. 若为数据,还存储数组长度

    • 对象头大小是 8 字节的 1 倍或者 2 倍大小
  • 实例数据

    对象存储的真正的有效信息

  • 对齐填充

    Hotspot 虚拟机要求对象地址必须是 8 直接整数倍,未满足 8 字节需要用 0 填充对齐

如何判断一个常量是废弃常量

废弃常量回收和堆中对象类似,常量对象没有被任何地方引用,则可能被回收。 如字符串,若当前系统中没有任何一个 String 对象引用常量池中 “abc” 常量,也没有其他地方引用这个字面量,如果发生 GC ,有必要的话这个常量会别清除出常量池。常量池中的其他类、方法、字段、符号引用也和这字符串常量类似。

JDK 1.7 及之后版本的 JVM 已经将字符串常量池从永久代中移了出来。

如何判断一个类是无用的类

  • 该类所有实例都已被回收,即堆中不存在该类任何实例
  • 加载该类的 ClassLoader 已经被回收
  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法再任何地方通过反射访问该类的方法

说一下 jvm 有哪些垃圾回收器?

Serial:最早的单线程串行垃圾回收器。 Serial Old:Serial 垃圾回收器的老年版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。 ParNew:是 Serial 的多线程版本。 Parallel 和 ParNew 收集器类似是多线程的,但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系统的吞吐量。 Parallel Old 是 Parallel 老生代版本,Parallel 使用的是复制的内存回收算法,Parallel Old 使用的是标记-整理的内存回收算法。 CMS:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。 G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项。

新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?

  • 新生代回收器:Serial、ParNew、Parallel Scavenge
  • 老年代回收器:Serial Old、Parallel Old、CMS
  • 整堆回收器:G1

新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

知识进阶部分

怎样判断对象是否可以被回收?

  • 从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
  • 对不可达对象第一次标记,执行第一次筛选过程,若覆盖了 finalize() 方法或者未执行 finalize() ,则将对象加入到 F-Queue 队列中(优先级很低的队列)在这里 finalize() 方法被执行,之后进行第二次标记,否则直接回收
  • 对第二次标记对象,进行筛选,若在 finalize() 方法执行自救程序 (当前对象被指向 GC Roots) 则不回收,否则直接回收

    自救程序只能执行一次,第二次不执行,而直接回收

JVM 新生代到年老代的晋升过程的判断条件是什么呢

Minor GC 之后判定下述条件

  • 大对象(虚拟机参数设置)直接晋升到老年代 (节省从 Eden 复制到 To Survivor空间开销)
  • To Survivor 区域对象 GC 年龄大小超过阈值 (默认 15 岁,由JVM参数MaxTenuringThreshold决定) 晋升到老年代
  • 动态年龄判断,To Survivor 区域同龄对象之和大于 To Survivor 空间一半直接晋升老年代

简述分代垃圾回收器是怎么工作的?

分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。 新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是8:1:1,它的执行流程如下:

  • 把 Eden + From Survivor 存活的对象放入 To Survivor 区;
  • 清空 Eden 和 From Survivor 分区;
  • From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。
  • 每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。

老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。

简述垃圾回收机制

  • 怎样判断对象是否死亡
  • 说明四种内存回收算法
  • 堆内存分为新生代和老年代区域

回收方法区

方法区回收价值很低,主要回收废弃的常量和无用的类。

如何判断无用的类:

1.该类所有实例都被回收(Java堆中没有该类的对象)

2.加载该类的ClassLoader已经被回收

3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方利用反射访问该类

Minor GC 和 Full GC 有什么不同呢?

  • Minor Gc:发生在新生代 GC,因为对象大都朝生夕灭,所以 Minor GC 非常频繁,且一般回收速度快
  • Full GC:发生在老年代 GC,Full GC 至少伴随一次 Minor GC(并不绝对),且回收速度一般比 Minor GC 慢 10 倍以上

什么时候触发 Full GC ?

  • 调用 System.gc 时,系统建议执 Full GC,但是不必然执行。
  • 老年代空间不足。
  • 方法区空间不足。
  • 通过 Minor GC 后进入老年代的平均大小大于老年代的可用内存。 由 Eden 区、survivor space1(From Space)区向 survivor space2(To Space)区复制时,对象大小大于 To Space 可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。

JVM 垃圾回收机制,何时触发 MinorGC 等操作

  • 当 young gen 中的 eden 区分配满的时候触发 MinorGC (新生代的空间不够放的时候)

分代垃圾回收机制:不同的对象生命周期不同。把不同生命周期的对象放在不同代上,不同代上采用最合适它的垃圾回收方式进行回收。 JVM中共划分为三个代:年轻代、年老代和持久代, 年轻代:存放所有新生成的对象; 年老代:在年轻代中经历了N次垃圾回收仍然存活的对象,将被放到年老代中,故都是一些生命周期较长的对象; 持久代:用于存放静态文件,如Java类、方法等。 新生代的垃圾收集器命名为“minor gc”,老生代的GC命名为”Full Gc 或者Major GC”.其中用System.gc()强制执行的是Full Gc.

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

针对对象什么周期特点将朝生夕灭对象分配到新生代,将长时间存活对象分配到老年代。这样处理,对象回收效率高,且空间利用率高,不存在空间碎片问题

说一下堆栈的区别?

  • 栈内存存储的是局部变量而堆内存存储的是实体;
  • 栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;
  • 栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。

队列和栈是什么?有什么区别?

  1. 队列和栈都是被用来预存储数据的。
  2. 队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。
  3. 栈和队列很相似,但它运行对元素进行后进先出进行检索。

栈内存存储的是局部变量,堆内存存储的是实体; 栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短; 栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。

GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,如果让你优化收集方法,有什么思路?

  • 标记-清除算法:算法简单、实现简单,标记和清除两个过程效率不高,且会产生大量的空间碎片
  • 标记-整理算法:相对标记清除算法,效率较高,一次清除端外一侧内存;不会产生内存碎片问题
  • 复制算法:效率高,不会产生空间碎片,但是可使用内存减少为原先一般,且如果对象成活率高,复制次数较多影响效率
  • 分代算法:根据对象存活周期,将空间分为新生代和老年代,回收效率高且空间利用率高

根绝对象存活生命周期不同,将空间分为新生代和老年代,新生代使用复制算法,老年代使用标记-清除和标记整理算法

volatile的语义,它修饰的变量一定线程安全吗

什么是内存溢出,内存泄漏?如何阻止内存泄漏?

什么情况下会出现栈溢出

  • 方法创建了一个很大的对象,如 List,Array。
  • 是否产生了循环调用、死循环。
  • 是否引用了较大的全局变量。

垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗? 有什么办法主动通知虚拟机进行垃圾回收?

  • 基本原理是分代收集算法
  • 垃圾回收有一个过程,会进行两次标记,且可能将第一次标记的对象放入 F-Queue 队列,等待第二次标记回收
  • 可以执行 System.gc() 请求 JVM 执行 GC,但是可能会被拒绝

System.gc() 和 Runtime.gc() 区别

  • System.gc() 请求 JVM 执行 Full GC
  • 唯一的区别就是 System.gc() 写起来比 Runtime.gc() 简单点。其实基本没什么机会用得到这个命令,因为这个命令只是建议 JVM 安排GC运行, 还有可能完全被拒绝。 GC本身是会周期性的自动运行的,由JVM决定运行的时机,而且现在的版本有多种更智能的模式可以选择,还会根据运行的机器自动去做选择,就算真的有性能上的需求,也应该去对GC的运行机制进行微调,而不是通过使用这个命令来实现性能的优化。

简单的介绍一下强引用、软引用、弱引用、虚引用

  • 强引用:GC时不会被回收
  • 软引用:描述有用但不是必须的对象,在发生内存溢出异常之前被回收
  • 弱引用:描述有用但不是必须的对象,在下一次GC时被回收
  • 虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用PhantomReference实现虚引用,虚引用用来在GC时返回一个通知。

场景分析部分

永久代有内存回收吗?为什么?

两个部分明确问题,首先 jdk1.8 之前 Hotspot 才有永久代;其次,永久代有内存回收,只是回收的是常量池和卸载类

对象什么时候进入老年代?

  • 对象优先在 Eden 区分配 当对象首次创建时, 会放在新生代的 eden 区, 若没有 GC 的介入,会一直在 eden 区,GC 后,是可能进入 survivor 区或者年老代。

  • 大对象直接进入老年代 所谓的大对象是指需要大量连续内存空间的 Java 对象,最典型的大对象就是那种很长的字符串以及数组,大对象对虚拟机的内存分配就是坏消息,尤其是一些朝生夕灭的短命大对象,写程序时应避免。

  • 长期存活的对象进入老年代 虚拟机给每个对象定义了一个对象年龄(Age)计数器,对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1,当他的年龄增加到一定程度(默认是 15 岁), 就将会被晋升到老年代中。

    • 动态对象年龄判定 为了更好的适应不同程序的内存情况,虚拟机不是永远要求对象年龄必须达到了某个值才能进入老年代,如果 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需达到要求的年龄

知识扩展部分

调优命令

Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo

  • jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
  • jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
  • jmap,JVM Memory Map命令用于生成heap dump文件
  • jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
  • jstack,用于生成java虚拟机当前时刻的线程快照。
  • jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。

JVM 调优的工具?

JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm这两款视图监控工具。

  • jconsole:用于对 JVM 中的内存、线程和类等进行监控;
  • jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。
  • MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
  • GChisto,一款专业分析gc日志的工具

常用的JVM调优参数?

1
2
3
4
5
6
7
8
9
-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息。

几种常用的内存调试工具:

jmap、jstack、jconsole。

“地球人都知道,Java有个东西叫垃圾收集器,它让创建的对象不需要像c/cpp那样delete、free掉,你能不能谈谈,GC是在什么时候,对什么东西,做了什么事情?”

我自己分析一下这个问题,首先是“什么时候”,不同层次的回答从低到高排列:

1
2
3
4
5
6
7
8
9
10
11
1.系统空闲的时候。
分析:这种回答大约占30%,遇到的话一般我就会准备转向别的话题,譬如算法、譬如SSH看看能否发掘一些他擅长的其他方面。

2.系统自身决定,不可预测的时间/调用System.gc()的时候。
分析:这种回答大约占55%,大部分应届生都能回答到这个答案,起码不能算错误是吧,后续应当细分一下到底是语言表述导致答案太笼统,还是本身就只有这样一个模糊的认识。

3.能说出新生代、老年代结构,能提出minor gc/full gc
分析:到了这个层次,基本上能说对GC运作有概念上的了解,譬如看过《深入JVM虚拟机》之类的。这部分不足10%。

4.能说明minor gc/full gc的触发条件、OOM的触发条件,降低GC的调优的策略。
分析:列举一些我期望的回答:eden满了minor gc,升到老年代的对象大于老年代剩余空间full gc,或者小于时被HandlePromotionFailure参数强制full gc;gc与非gc时间耗时超过了GCTimeRatio的限制引发OOM,调优诸如通过NewRatio控制新生代老年代比例,通过MaxTenuringThreshold控制进入老年前生存次数等……能回答道这个阶段就会给我带来比较高的期望了,当然面试的时候正常人都不会记得每个参数的拼写,我自己写这段话的时候也是翻过手册的。回答道这部分的小于2%。

PS:加起来不到100%,是因为有确实少数直接说不知道,或者直接拒绝回答的= =#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
分析第二个问题:“对什么东西”:

1.不使用的对象。
分析:相当于没有回答,问题就是在问什么对象才是“不使用的对象”。大约占30%。

2.超出作用域的对象/引用计数为空的对象。
分析:这2个回答站了60%,相当高的比例,估计学校教java的时候老师就是这样教的。第一个回答没有解决我的疑问,gc到底怎么判断哪些对象在不在作用域的?至于引用计数来判断对象是否可收集的,我可以会补充一个下面这个例子让面试者分析一下obj1、obj2是否会被GC掉?
class C{
     public Object x;
}
C obj1、obj2 = new C();
obj1.x = obj2;
obj2.x = obj1;
obj1、obj2 = null;

3.从gc root开始搜索,搜索不到的对象。
分析:根对象查找、标记已经算是不错了,小于5%的人可以回答道这步,估计是引用计数的方式太“深入民心”了。基本可以得到这个问题全部分数。
PS:有面试者在这个问补充强引用、弱引用、软引用、幻影引用区别等,不是我想问的答案,但可以加分。

4.从root搜索不到,而且经过第一次标记、清理后,仍然没有复活的对象。
分析:我期待的答案。但是的确很少面试者会回答到这一点,所以在我心中回答道第3点我就给全部分数。

最后由一个问题:“做什么事情”,这个问发挥的空间就太大了,不同年代、不同收集器的动作非常多。

1.删除不使用的对象,腾出内存空间。
分析:同问题2第一点。40%。

2.补充一些诸如停止其他线程执行、运行finalize等的说明。
分析:起码把问题具体化了一些,如果像答案1那样我很难在回答中找到话题继续展开,大约占40%的人。
补充一点题外话,面试时我最怕遇到的回答就是“这个问题我说不上来,但是遇到的时候我上网搜一下能做出来”。做程序开发确实不是去锻炼茴香豆的“茴”有几种写法,不死记硬背我同意,我不会纠语法、单词,但是多少你说个思路呀,要直接回答一个上网搜,我完全没办法从中获取可以评价应聘者的信息,也很难从回答中继续发掘话题展开讨论。建议大家尽量回答引向自己熟悉的,可讨论的领域,展现给面试官最擅长的一面。

3.能说出诸如新生代做的是复制清理、from survivor、to survivor是干啥用的、老年代做的是标记清理、标记清理后碎片要不要整理、复制清理和标记清理有有什么优劣势等。
分析:也是看过《深入JVM虚拟机》的基本都能回答道这个程度,其实到这个程度我已经比较期待了。同样小于10%。

4.除了3外,还能讲清楚串行、并行(整理/不整理碎片)、CMS等搜集器可作用的年代、特点、优劣势,并且能说明控制/调整收集器选择的方式。
分析:同上面2个问题的第四点。