第2章:JVM内存模型与垃圾回收调优
本章定位:高频面试问答章节,聚焦JVM内存分区、对象分配、GC算法三大核心问题。掌握回答框架,即可应对90%的JVM性能优化提问。
回答框架图:JVM内存分区问题的结构化应答
回答框架口诀:“一概念二结构三作用,四举案例五挖坑”。先讲JVM内存模型是什么,再拆分堆、栈、元空间,说明各自用途,最后结合OOM或大对象分配给出真实案例。
问答卡片1:JVM内存分区
| 面试问题 | 请简述JVM内存模型分区,并说明每个分区的作用。 |
|---|---|
| 考察点 | 基础概念清晰度、是否理解堆内/堆外、元空间与永久代的区别。 |
| 回答框架 | ①整体概述(线程私有 vs 共享)→②堆(年轻代+老年代)→③方法区(元空间)→④栈(局部变量)→⑤程序计数器。 |
| 简短示例回答 | JVM内存分为线程私有的程序计数器、虚拟机栈、本地方法栈,以及线程共享的堆和方法区。堆存对象实例,分年轻代和老年代;方法区在JDK8后改为元空间,使用直接内存,存放类元数据。这是GC和性能分析的基础。 |
| 可能追问 | 元空间为什么取代永久代?Java8以后字符串常量池在哪?如何通过jstat查看各分区使用率? |
| 避坑提醒 | 不要混淆堆和栈的作用;注意JDK版本差异(例如永久代在8以后移除);避免只背概念不举例,可结合-XX:MaxMetaspaceSize等参数说明。 |
问答卡片2:对象创建与分配过程
| 面试问题 | 请描述一个Java对象从new到被回收的完整生命周期。 |
|---|---|
| 考察点 | 对象分配规则(栈上分配/TLAB/Eden)、GC晋升逻辑、逃逸分析。 |
| 回答框架 | ①类加载→②分配内存(指针碰撞/空闲列表)→③初始化→④逃逸分析判断栈分配→⑤TLAB/Eden→⑥Minor GC后存活进入Survivor→⑦年龄提升→⑧老年代→⑨Full GC回收。 |
| 简短示例回答 | new对象经类加载后,首选在栈上分配(逃逸分析),否则进入TLAB或Eden。Minor GC后存活对象复制到Survivor,每次年龄+1,默认15次后晋升老年代。老年代空间不足触发Full GC,最终由CMS或G1回收。可通过-XX:+PrintGCDetails验证。 |
| 可能追问 | 大对象直接进入老年代的条件?TLAB大小如何调节?为什么Survivor区要分为From和To? |
| 避坑提醒 | 不要漏掉逃逸分析和栈上分配优化;说明对象分配时地址冲突的解决(CAS或本地线程缓冲);提到-XX:PretenureSizeThreshold参数时需要结合实际场景。 |
问答卡片3:GC算法与调优
| 面试问题 | 你在项目中如何选择垃圾回收器?遇到过哪些GC问题并如何调优? |
|---|---|
| 考察点 | 各GC特点(吞吐量、延迟、内存占用)、调优参数、实际案例。 |
| 回答框架 | ①业务需求分析(吞吐优先 vs 低延迟)→②对比CMS/G1/ZGC→③选择依据→④典型问题(停顿过长)→⑤参数调整→⑥效果验证。 |
| 简短示例回答 | 当前项目为在线交易,要求低延迟,选G1。曾遇到Mixed GC停顿超过300ms,调整-XX:G1HeapRegionSize=4M、-XX:InitiatingHeapOccupancyPercent=35,并启用-XX:+UseLargePages,停顿降低至100ms内。同时配合jstat监控堆占用变化。 |
| 可能追问 | G1的SATB与CMS的增量更新区别?如何判断是否发生老年代碎片化?ZGC的染色指针原理? |
| 避坑提醒 | 勿背参数而不说场景;调优必须附带前后对比数据;避免直接说“用默认参数”,应展示主动优化过程;注意JDK版本对GC的支持变化(如JDK9默认G1)。 |
面试官视角:提问者并非考察背诵能力,而是希望看到你用结构化思维拆解复杂问题,并能用真实案例佐证。三个问答卡片的框架可通用到其他性能优化问题(线程池、IO模型等)。