Java面试-02-JVM虚拟机

📅 2026/6/27 6:59:09
Java面试-02-JVM虚拟机
JVM虚拟机面试题完整版目录1. JVM主要组成部分及作用2. 类加载器2.1 类加载器分类2.2 类加载机制及过程2.3 双亲委派机制3. 运行时数据区4. 本地方法接口5. JVM垃圾回收5.1 垃圾回收机制5.2 对象回收判断算法5.3 垃圾回收算法5.4 Java堆分代模型5.5 垃圾回收器5.6 对象分配与回收策略5.7 对象流转完整过程6. JVM常见异常及排查7. JVM调优7.1 JVM调优步骤7.2 调优核心原则7.3 可视化监控工具7.4 实际项目调优案例8. 高频面试问题1. JVM主要组成部分及作用JVM核心分为两大子系统和两大组件两大子系统类加载器子系统负责将.class字节码文件加载到JVM内存中执行引擎子系统执行加载后类中的字节码指令会先编译为机器码再执行两大组件运行时数据区JVM内存区域存储程序运行数据本地方法接口与native本地方法交互的接口2. 类加载器2.1 类加载器分类启动类加载器Bootstrap加载Java核心类库rt.jar扩展类加载器Extension加载JDK扩展目录下的jar包应用类加载器App加载项目classpath下的类文件自定义类加载器加载自定义路径的JAR/类文件2.2 类加载机制及过程类加载完整流程加载 → 连接 → 初始化加载通过类加载器读取.class文件加载到JVM内存连接校验验证字节码文件的安全性、正确性准备为静态变量分配内存设置默认初始值解析将常量池符号引用转换为直接引用初始化执行静态代码块为静态变量赋实际值2.3 双亲委派机制定义类加载器收到加载请求时优先委托父类加载器执行递归向上直到启动类加载器父类无法加载时子类才尝试加载核心优势避免类重复加载保证核心类安全3. 运行时数据区JVM内存模型分为5个区域按线程共享/私有划分区域名称作用存储内容线程特性堆HeapJVM最大内存区域GC主要区域对象实例、数组共享方法区存储类元数据类信息、常量、静态变量、编译后代码共享虚拟机栈方法执行内存模型局部变量表、操作数栈、方法返回地址私有程序计数器记录线程执行字节码行号当前执行指令地址私有本地方法栈执行native方法服务本地方法调用信息私有4. 本地方法接口作用用于JVM与底层操作系统、本地native方法进行交互场景调用C/C实现的底层系统功能5. JVM垃圾回收5.1 垃圾回收机制自动回收堆中无引用、不可达的垃圾对象释放内存System.gc()手动触发GC不保证立即执行finalize()GC回收对象前自动调用的方法5.2 对象回收判断算法引用计数法给对象添加计数器引用1失效-1计数器0可回收缺点无法解决对象循环引用问题可达性分析算法JVM默认以GC Roots为起点遍历引用链不可达对象可回收可作为GC Roots的对象虚拟机栈中引用的对象方法区静态变量引用的对象方法区常量引用的对象本地方法栈引用的对象5.3 垃圾回收算法算法原理优点缺点标记-清除先标记存活对象再清除垃圾简单产生内存碎片复制算法内存分两块存活对象复制到空块清空原块无碎片浪费50%内存标记-整理标记后将存活对象移到一端清理边界外内存无碎片移动对象开销大分代收集新生代用复制老年代用标记清除/整理综合效率最高逻辑复杂5.4 Java堆分代模型新生代对象存活率低分区Eden : From Survivor : To Survivor 8:1:1新生代分为三个区域分别是一个 Eden 区和两个 Survivor 区它们的默认比例是 8:1:1。Eden 区新创建的对象首先会被分配到 Eden 区。当 Eden 区满时会触发一次 Minor GC新生代垃圾回收。Survivor 区在 Minor GC 时Eden 区中存活的对象会被移动到其中一个 Survivor 区。同时From Space 中之前存活的对象如果经过这次垃圾回收仍然存活并且年龄达到一定阈值默认是 15可以通过-XX:MaxTenuringThreshold参数调整会被移动到老年代如果未达到阈值则会和 Eden 区移动过来的对象一起被复制到另一个 Survivor 区。之后两个Survivor 的角色会互换垃圾回收Minor GC频率高、速度快算法复制算法老年代对象存活率高存储长期存活对象、大对象年龄阈值对象在 Survivor 区经过多次 Minor GC 后年龄达到一定阈值会被晋升到老年代。大对象直接进入当创建的对象占用内存超过一定大小可以通过-XX:PretenureSizeThreshold参数设置时会直接在老年代分配内存。Survivor 区空间不足如果在 Survivor 区中相同年龄的所有对象大小总和大于 Survivor区空间的一半年龄大于或等于该年龄的对象就可以直接进入老年代。垃圾回收Full GC/Major GC频率低、速度慢算法标记-清除 / 标记-整理元空间Java8替代永久代永久代Java 7 及以前存储内容主要存储类的元数据信息如类的结构、方法、字段等、常量池、静态变量等。永久代的大小在启动时就需要指定且不能动态扩展。问题由于永久代的大小是固定的如果加载的类过多或者常量池过大容易导致OutOfMemoryError:PermGenspace异常。元空间Java 8 及以后存储内容同样用于存储类的元数据信息但与永久代不同的是元空间使用的是本地内存Native Memory而不是 JVM 堆内存。优点元空间的大小可以动态扩展只要系统的本地内存足够就不会出现永久代那样的内存溢出问题。存储类元数据、常量池5.5 垃圾回收器新生代收集器Serial单线程、复制算法、停顿用户线程ParNewSerial多线程版本Parallel Scavenge关注高吞吐量老年代收集器Serial OldSerial老年代版本标记-整理Parallel Old多线程、标记-整理CMS最短回收停顿并发回收整堆收集器G1跨代收集兼顾吞吐量与停顿时间5.6 对象分配与回收策略新对象优先分配在Eden区大对象直接进入老年代长期存活对象晋升老年代默认年龄阈值15Eden区满 →Minor GC老年代满 →Full GC5.7 对象流转完整过程创建 → 分配到Eden区Eden满 → Minor GC → 存活对象进入SurvivorSurvivor中对象年龄递增 → 达标晋升老年代老年代满 → Full GC回收整个堆无引用 → 被GC彻底回收6. JVM常见异常及排查StackOverflowError栈溢出原因无限递归、方法调用层级过深、大量局部变量排查检查递归逻辑、方法调用链OutOfMemoryError堆溢出/OOM原因加载数据过大、集合未释放、死循环创建对象、内存参数过小排查分析dump文件、检查对象引用、优化代码7. JVM调优7.1 JVM调优步骤监控使用JConsole、VisualVM查看GC日志、堆内存快照分析看日志,判断GC频率、停顿时间是否异常Minor GC执行时间不到50msMinor GC执行不频繁约10秒一次Full GC执行时间不到1sFull GC执行频率不算频繁不低于10分钟1次调整修改内存参数、GC收集器、分代比例验证持续监控找到最优参数7.2 调优核心原则-Xms初始堆与-Xmx最大堆设置为相同值减少GC次数新生代:老年代默认比例1:2可根据场景调整高并发场景优先使用并行收集器/G1避免大对象直接进入老年代7.3 可视化监控工具JConsoleJDK自带监控内存、线程、GCVisualVM功能全面的JVM分析工具MAT分析OOM dump文件定位内存泄漏7.4 实际项目调优案例项目场景知识图谱工具部署后响应慢、频繁OOM问题原因数据导入创建大量临时大对象堆内存不足调优过程增大堆内存-Xms4g -Xmx4g调整分代比例-XX:NewRatio1新生代:老年代1:1启用GC收集器-XX:UseG1GC开启dump-XX:HeapDumpOnOutOfMemoryError代码优化使用对象池、重构导入逻辑CSV导入替代内存操作最终方案代码重构合理JVM参数解决OOM问题8. 高频面试问题对象一定分配在堆中吗不一定。无逃逸对象可在栈上分配简单对象可通过标量替换拆分存储。Minor GC和Full GC区别Minor GC新生代回收速度快、频率高Full GC整堆回收速度慢、频率低永久代和元空间区别永久代Java7及以前使用堆内存固定大小元空间Java8使用本地内存可动态扩展CMS收集器特点并发收集、低停顿、适用于互联网服务端