📌 文章目录
一、JVM 内存结构与运行时模型
1. JVM 内存结构分区及作用
JVM 运行时内存结构主要包含以下部分:
- 程序计数器:线程私有,记录当前线程执行的字节码指令地址,确保线程切换后能继续执行。
- 虚拟机栈:线程私有,通过栈帧保存方法调用的局部变量、操作数栈、动态链接等信息。
- 本地方法栈:与虚拟机栈类似,专用于支持 native 方法的调用。
- 堆(Heap):线程共享,是对象实例的主要分配区域,分为新生代(Eden+Survivor)和老年代。
- 方法区(MetaSpace):从 JDK8 开始,HotSpot 将永久代(PermGen)替换为元空间(MetaSpace),用于存储类元信息、常量池等。
堆与元空间的区别:堆用于存储对象实例,而元空间用于存储类元数据,如类结构、方法、常量池等。堆位于 JVM 内部,元空间则使用本地内存。
2. 栈帧结构及方法调用链维护
每个方法调用都会创建一个栈帧,包含以下结构:
- 局部变量表(Local Variable Table)
- 操作数栈(Operand Stack)
- 动态链接(Dynamic Linking)
- 方法返回地址
- 额外信息(如异常处理表)
调用链维护机制:每次方法调用时,JVM 将栈帧压入虚拟机栈(LIFO 结构);方法返回时,栈帧出栈并返回执行结果,从而形成调用链。
3. 逃逸分析及其对对象分配策略的影响
逃逸分析(Escape Analysis)是 JIT 编译阶段的优化技术,用于判断对象是否会逃离当前方法或线程作用域。
影响:
- 栈上分配:若对象不会逃逸,可在栈上分配,生命周期与方法同步,避免堆分配和 GC。
- 同步省略:不逃逸的对象无需加锁,省去 synchronized 带来的性能损耗。
- 标量替换:将对象拆解为多个标量变量进行优化。
4. TLAB 的作用及提升对象创建效率的机制
TLAB(Thread-Local Allocation Buffer)是 JVM 为每个线程预留的一块内存区域,用于在 Eden 区中进行快速对象分配。
作用:
- 减少多线程环境下对 Eden 区的竞争。
- 对象分配只需指针移动(bump-the-pointer)。
- 小对象可以在用户态完成分配,提升效率。
二、垃圾回收器与 GC 调优
1. CMS 与 G1 垃圾收集器的设计区别及适用场景
CMS(Concurrent Mark-Sweep):关注低延迟,基于“标记-清除”算法,老年代并发回收但容易产生碎片。
G1(Garbage First):采用区域化堆设计,按 Region 管理,可预测停顿时间,适用于大堆和高并发场景。
选择 G1 的场景:
- 服务端应用
- 大堆(>4GB)
- STW 敏感业务(如支付、游戏)
2. Full GC 频繁问题的排查流程及调优思路
常见原因:
- 老年代空间不足
- 元空间溢出
- GC Root 增加导致存活对象增多
- 内存泄漏
排查流程:
- 开启 GC 日志:
-Xlog:gc*
- 分析 YGC 与 FGC 频率
- 使用
jmap -heap
查看堆结构 - 使用
jmap -histo
或 MAT 工具查找泄漏 - 检查对象引用链及缓存清理策略
调优手段:
- 增加老年代大小
- 调整
-XX:MetaspaceSize
和-XX:MaxMetaspaceSize
- 优化代码中的引用和缓存逻辑
- 切换为 G1 或 ZGC,减少 FGC 触发
3. 控制 GC 日志输出的参数及常用日志字段
JDK 8:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/gc.log
JDK 9+:
-Xlog:gc*,safepoint,gc+heap=debug:file=gc.log
常用字段:
- GC 类型(YGC/FGC)<