JVM 知识点汇总(问题)
JVM 知识点有 6 个大方向,其中,内存模型、类加载机制、GC 垃圾回收是比较重点的内容。性能调优部分偏重实际应用,重点突出实践能力。编译器优化和执行模式部分偏重理论基础,主要掌握知识点。
各个部分需要了解的知识点如下。
-
内存模型:程序计数器、方法区、堆、栈、本地方法栈的作用,保存哪些数据。
-
类加载:双亲委派的加载机制,以及常用类加载器分别加载哪种类型的类。
-
GC:分代回收的思想和依据,以及不同垃圾回收算法实现的思路、适合的场景。
-
性能调优:常用的 JVM 优化参数的作用,参数调优的依据,常用的 JVM 分析工具能分析哪类问题,以及使用方法。
-
执行模式:解释、编译、混合模式的优缺点,Java7 提供的分层编译技术。需要知道 JIT 即时编译技术和 OSR(栈上替换),知道 C1、C2 编译器针对的场景,其中 C2 针对 Server 模式,优化更激进。在新技术方面可以了解 Java10 提供的由 Java 实现的 Graal 编译器。
-
编译优化:前端编译器 javac 的编译过程、AST 抽象语法树、编译期优化和运行期优化。编译优化的常用技术包括公共子表达式的消除、方法内联、逃逸分析、栈上分配、同步消除等。明白了这些才能写出对编译器友好的代码。
JVM 的内容相对来说比较集中,但是对知识深度的掌握要求较高。
内存模型(看这里)
- 运行时数据区: 程序计数器、栈、本地方法栈、方法区、堆
- Java内存模型JMM
- HotSpot虚拟机对象
说明:主要了解他们分别保存了哪些数据,以及有什么作用
类加载机制(看这里)
- 类加载的基本流程
- 双亲委派机制
- 启动类加载器Bootstrap ClassLoader
- 扩展类加载器 Extension ClassLoader
- 应用程序类加载器 Application ClassLoader
- 自定义类加载器
- 破坏双亲委派模型:JDBC、热部署都是指定类加载器加载。
说明:了解双亲委派机制,以及常见的类加载器分别加载哪种类型的类和他们的执行顺序
GC(看这里)
- GC算法及其各自的优缺点:标记-清除、复制、标记-整理、分代收集
- 分代回收
年轻代、老年代 - 收集器分类:串行收集器、并行收集器、并发收集器
- 垃圾收集器
年轻代:Serial、ParNew、Parallel Scavenge
老年代:Serial Old、Parallel Old、CMS
年轻代和老年代:G1
说明:分代回收的思想和依据,以及不同GC算法实现的思路和适合的场景
性能调优
- JVM参数看这里
- 调优的参数主要分为下面几个方向考虑:
1. 内存管理配置,如:新生代老年代永久代的大小、新生代老年代的比例、新生代各个区域所占比例、新生代对象晋升老年代的年龄阈值
2. 垃圾收集器配置,如:新生代和老年代分别采用的收集器类型,各个收集器的运行参数配置、并行收集器并行的线程数
3. 多线程相关配置:使用自旋锁时默认的自旋次数,是否使用偏向锁等
4. 调试参数配置:内存溢出时是否生成快照、打印JUC中锁状态、打印GC详细信息等等。
具体配置参数可以通过查看Hotspot的参数表。
可以简单的将调优细分为下面几个点:
1. 设置堆的最大和最小值
2. 新生代老年代永久代的大小、新生代老年代的比例、新生代各个区域所占比例、新生代对象晋升老年代的年龄阈值
3. 查看峰值的老年代的大小,在不影响Full GC的情况下可以加大年轻代,减少Minor GC
3. 配置好的机器可以采用并发收集器
4. 每个线程默认开启1M堆栈存放栈帧,可以根据情况调小
关于OOM问题和CPU占用过高问题,都可以通过JVisualVM来观察各个线程CPU占用和内存占用
说明:常用的JVM优化参数的作用,参数调优的依据,常用的JVM分析工具能分析哪一类问题以及使用方法。
-Xms1024M 初始堆内存空间
-Xmx1024M 最大堆内存空间
-XX:NewRatio=4 设置年轻的和老年代的内存比例为 1:4
-XX:SurvivorRatio=3 设置新生代 From Survivor或To Survivor 与Eden 比例为 1:3,即:Eden区占新生代的3/(3+1+1)
-XX:PermSize=128M JDK1.8后用-XX:MetaspaceSize代替
-XX:MaxPermSize=246M JDK1.8后用-XX:MaxMetaspaceSize代替
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息
还可以指定新生代和老年代的垃圾收集器
JVM调优主要是配置:堆大小、各分代的大小、指定各分代串行和并行的收集器、指定收集器的相关参数、配置打印GC回收的过程日志信息
编译优化(了解)
- 指令重排
- 内联
- 逃逸分析:方法逃逸、线程逃逸
说明:编译优化包括javac编译过程、AST抽象语法树、编译期优化和运行期优化。常用的技术包括公共子表达式的消除、方法内联、逃逸分析、同步消除等。
常见问题排查
- 内存泄露
方式一:JDK工具:jvisualvm的profiler分析器插件可以直接进行内存分析。
方式二:JDK工具:jvisualvm可以到处内存快照dump文件,然后使用idea插件jprofiler分析dump文件
方式三:先使用jps命令查看Java进程pid,然后使用jmap命令导出内存快照dump文件,然后使用idea插件jprofiler分析dump文件。还可以使用jstat命令查看GC情况 - 死锁
方式一:JDK工具:jconsole的线程监控提供了检测死锁的功能
方式二:先试用jps查看Java进程的pid,在使用jstack命令查看对应进程的线程情况。 - CPU占用过高
方式一:JDK工具:jvisualvm工具的profiler分析器插件可以直接分析线程的CPU占用情况。
方式二:先试用linux的top命令查看对应占用CPU高的进程,然后在利用linux的ps命令查看对应的进程的线程大致情况,在利用jstack命令查看具体有问题线程的堆栈情况。
考察点
总结 JVM 相关的面试考察点如下:
-
深入了解 JVM 的内存模型和 Java 的内存模型;
-
要了解类的加载过程,了解双亲委派机制;
-
要理解内存的可见性与 Java 内存模型对原子性、可见性、有序性的保证机制;
-
要了解常用的 GC 算法的特点、执行过程,和适用场景,例如 G1 适合对最大延迟有要求的场合,ZGC 适用于 64 位系统的大内存服务中;
-
要了解常用的 JVM 参数,明白对不同参数的调整会有怎样的影响,适用什么样的场景,例如垃圾回收的并发数、偏向锁设置等。
加分项
如果想要给面试官留下更好的印象,注意这些加分项。
-
如果在编译器优化方面有深入的了解的话,会让面试官觉得你对技术的深度比较有追求。例如知道在编程时如何合理利用栈上分配降低 GC 压力、如何编写适合内联优化等代码等。
-
如果你能有线上实际问题的排查经验或思路那就更好了,面试官都喜欢动手能力强的同学。例如解决过线上经常 FullGC 问题,排查过内存泄露问题等。
-
如果能有针对特定场景的 JVM 优化实践或者优化思路,也会有意想不到的效果。例如针对高并发低延迟的场景,如何调整 GC 参数尽量降低 GC 停顿时间,针对队列处理机如何尽可能提高吞吐率等;
-
如果对最新的 JVM 技术趋势有所了解,也会给面试官留下比较深刻的印象。例如了解 ZGC 高效的实现原理,了解 Graalvm 的特点等。
关于JVM常见的问题
-
简单描述一下JVM内存模型,回答这个问题时记得和面试官确认是希望回答 JVM 的内存模型,还是 Java 对内存访问的模型,不要答跑偏。
-
什么情况下会触发MinGC、MajorGC、FullGC?
Minor GC 用于清理年轻代,Eden满了,就触发Minor GC ,清理无用对象,把有用对象放到Survivor1或Survivor2中。Major GC 用于清理老年代。
Full GC 清理年轻代、老年代区域,成本高,对系统性能产生影响 -
双亲委派机制的加载流程是怎样的,有什么好处?能否说出一些破坏双亲委派机制的情况?
-
JDK1.8为什么用Metaspace替换的PermGen?Metaspace保存在哪里
-
编译期会对指令做哪些优化?简单描述编译期的指令重排
-
简单描述一下volatile可以解决什么问题?如何做到的? volatile 要重点回答强制主内存读写同步以及防止指令重排序两点。
-
简单描述一下GC的分代回收。
-
G1垃圾回收算法与CMS的区别有哪些?
-
对象的引用有哪几种?强、软、弱、虚,这几种引用有什么特点?答出分别在 GC 中的处理方式。
-
用过哪些JVM调试工具,他们分别用来分析什么内容?
jvisualvm 内存占用信息、CPU占用信息。
jconsole可以查看内存情,也可以查看活跃的线程情况。
线程分析工具 jstack 和获取堆信息的 jmap 等。