JVM 常见面试题

1.1、JDK、 JRE、JVM 的关系是什么?
什么是 JVM ?
英文名称 ( Java Virtual Machine ),就是 JAVA 虚拟机, 它只识别 .class 类型文件,它能够将 class 文件中的字节码指令进行识别并调用操作系统向上的 API 完成动作。

什么是 JRE ?
英文名称( Java Runtime Environment ),Java 运行时环境。它主要包含两个部分:JVM 的标准实现和 Java 的一些基本类库。相对于 JVM 来说,JRE多出来一部分 Java 类库。

什么是 JDK?
英文名称( Java Development Kit ),Java 开发工具包。JDK 是整个 Java 开发的核心,它集成了 JRE 和一些好用的小工具。例如:javac.exe、java.exe、jar.exe 等。

这三者的关系:一层层的嵌套关系。JDK > JRE > JVM。

1.2 JVM 的内存模型以及分区情况和作用
如下图所示:
JVM 常见面试题

黄色部分为线程共有,蓝色部分为线程私有。

方法区
用于存储虚拟机加载的类信息,常量,静态变量等数据。


存放对象实例,所有的对象和数组都要在堆上分配。 是 JVM 所管理的内存中最大的一块区域。


Java 方法执行的内存模型:存储局部变量表,操作数栈,动态链接,方法出口等信息。生命周期与线程相同。

本地方法栈
作用与虚拟机栈类似,不同点本地方法栈为 native 方法执行服务,虚拟机栈为虚拟机执行的 Java 方法服务。

程序计数器
当前线程所执行的行号指示器。是 JVM 内存区域最小的一块区域。执行字节码工作时就是利用程序计数器来选取下一条需要执行的字节码指令。

1.3 JVM 对象创建步骤流程是什么?
整体流程如下图所示:
JVM 常见面试题

第 1 步:虚拟机遇到一个 new 指令,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用, 并且检查这个符号引用的类是否已经被加载&解析&初始化。

第 2 步:如果类已经被加载那么进行第 3 步; 如果没有进行加载, 那么就就需要先进行类的加载。

第 3 步:类加载检查通过之后, 接下来进行新生对象的内存分配。

第 4 步:对象生成需要的内存大小在类加载完成后便可完全确定,为对象分配空间等同于把一块确定大小的内存从 Java 堆中划分出来

第 5 步:内存大小的划分分为两种情况: 第一种情况:JVM 的内存是规整的, 所有的使用的内存都放到一边, 空闲的内存在另外一边, 中间放一个指针作为分界点的指示器。 那么这时候分配内存就比较简单, 只要讲指针向空闲空间那边挪动一段与对象大小相同的距离。 这种就是“指针碰撞”。

第二种情况:JVM 的内存不是规整的, 也就是说已使用的内存与未使用的内存相互交错。 这时候就没办法利用指正碰撞了。 这时候我们就需要维护一张表,用于记录那些内存可用, 在分配的时候从列表中找到一块足够大的空间划分给对象实例, 并更新到记录表上。

第 6 步:空间申请完成之后, JVM 需要将内存的空间都初始化为 0 值。如果使用 TLAB, 就可以在 TLAB 分配的时候就可以进行该工作。

第 7 步: JVM 对对象进行必要的设置。 例如, 这个对象是哪个类的实例、对象的哈希码、GC 年代等信息。

第 8 步:完成了上面的步骤之后 从 JVM 来看一个对象基本上完成了, 但从 Java 程序代码绝对来看, 对象创建才刚刚开始, 需要执行 < init > 方法, 按照程序中设定的初始化操作初始化, 这时候一个真正的程序对象生成了。

2、判断一个对象应该被回收
1.该对象没有与GC Roots相连
2.该对象没有重写finalize()方法或finalize()已经被执行过则直接回收(第一次标记)、否则将对象加入到F-Queue队列中(优先级很低的队列)在这里finalize()方法被执行,之后进行第二次标记,如果对象仍然应该被GC则GC,否则移除队列。 (在finalize方法中,对象很可能和其他 GC Roots中的某一个对象建立了关联,finalize方法只会被调用一次,且不推荐使用finalize方法)

3、回收方法区
方法区回收价值很低,主要回收废弃的常量和无用的类。
如何判断无用的类:
1.该类所有实例都被回收(Java堆中没有该类的对象)
2.加载该类的ClassLoader已经被回收
3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方利用反射访问该类

4、垃圾收集算法
GC最基础的算法有三种: 标记 -清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。

标记 -清除算法,“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
复制算法,“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
标记-压缩算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

5、垃圾回收器
Serial收集器,串行收集器是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收。
ParNew收集器,ParNew收集器其实就是Serial收集器的多线程版本。
Parallel收集器,Parallel Scavenge收集器类似ParNew收集器,Parallel收集器更关注系统的吞吐量。
Parallel Old 收集器,Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法
CMS收集器,CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
G1收集器,G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征

6、GC日志分析
摘录GC日志一部分(前部分为年轻代gc回收;后部分为full gc回收):
2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs]
7、16-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs]
通过上面日志分析得出,PSYoungGen、ParOldGen、PSPermGen属于Parallel收集器。其中PSYoungGen表示gc回收前后年轻代的内存变化;ParOldGen表示gc回收前后老年代的内存变化;PSPermGen表示gc回收前后永久区的内存变化。young gc 主要是针对年轻代进行内存回收比较频繁,耗时短;full gc 会对整个堆内存进行回城,耗时长,因此一般尽量减少full gc的次数

8、调优命令
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 这个命令作用是实时查看和调整虚拟机运行参数。

9、调优工具
常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。

jconsole,Java Monitoring and Management Console是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控
jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。
MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗
GChisto,一款专业分析gc日志的工具

10、Minor GC与Full GC分别在什么时候发生?
新生代内存不够用时候发生MGC也叫YGC,JVM内存不够的时候发生FGC

11、你知道哪些JVM性能调优
设定堆内存大小
-Xmx:堆内存最大限制。

设定新生代大小。 新生代不宜太小,否则会有大量对象涌入老年代
-XX:NewSize:新生代大小

-XX:NewRatio 新生代和老生代占比

-XX:SurvivorRatio:伊甸园空间和幸存者空间的占比

设定垃圾回收器 年轻代用 -XX:+UseParNewGC 年老代用-XX:+UseConcMarkSweepGC