Skip to content

运行时数据区总结与大厂面试题

JVM 运行时数据区全图

在学完运行时数据区的所有章节后,让我们用一张全景图做最后的总结。

┌─────────────────────────────────────────────────────────────────┐
│                     JVM 运行时数据区                              │
│                                                               │
│  ┌──────────────────┐     ┌────────────────────────────────┐ │
│  │    线程私有区域     │     │       线程共享区域               │ │
│  │                    │     │                                │ │
│  │  ┌────────────┐  │     │  ┌──────────────────────┐  │ │
│  │  │ PC 寄存器   │  │     │  │      堆(Heap)      │  │ │
│  │  └────────────┘  │     │  │  Eden │ S0 │ S1 │ Old │  │ │
│  │                    │     │  └──────────────────────┘  │ │
│  │  ┌────────────┐  │     │  ┌──────────────────────┐  │ │
│  │  │ 虚拟机栈    │  │     │  │   方法区 / 元空间      │  │ │
│  │  │ 栈帧/局部变量│  │     │  │ 类信息/常量池/代码缓存│  │ │
│  │  └────────────┘  │     │  └──────────────────────┘  │ │
│  │                    │     │  ┌──────────────────────┐  │ │
│  │  ┌────────────┐  │     │  │      直接内存        │  │ │
│  │  │ 本地方法栈  │  │     │  │   NIO / Netty / Kafka│  │ │
│  │  └────────────┘  │     │  └──────────────────────┘  │ │
│  └──────────────────┘     └────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

核心对比总结

线程私有 vs 线程共享

维度线程私有线程共享
区域PC 寄存器、虚拟机栈、本地方法栈堆、方法区、直接内存
生命周期与线程共存亡与 JVM 进程共存亡
GC无需 GC(线程结束即释放)GC 管理
线程安全天然线程安全需要考虑并发
异常StackOverflowErrorOutOfMemoryError

堆 vs 方法区

维度堆(Heap)方法区(Metaspace)
存储内容对象实例、数组类信息、字节码、常量、代码缓存
JDK 8+ 位置JVM 堆本地内存
GC 频率高(MinorGC / FullGC)低(主要 FullGC 时)
OOM 原因Java heap spaceMetaspace

大厂面试题精选

Q1:运行时数据区有哪些?哪些是线程私有的?

答案

  • 线程私有:PC 寄存器、虚拟机栈、本地方法栈
  • 线程共享:堆、方法区/元空间、直接内存(严格说不是 JVM 规范的一部分)

Q2:对象的创建过程是怎样的?

答案

  1. 检查类是否已加载
  2. 在堆中分配内存(TLAB / 指针碰撞)
  3. 设置对象头(Mark Word + 类型指针)
  4. 执行构造器 <init>
  5. 返回引用

Q3:什么情况下会 OOM?OOM 的类型有哪些?

答案

类型原因
Java heap space堆内存耗尽(分配对象过多、内存泄漏)
Metaspace元空间耗尽(加载类过多)
Direct buffer memory直接内存耗尽(NIO 使用过多)
Unable to create new native thread线程数过多,栈空间不足
Requested array size exceeds VM limit数组过大,超过了 JVM 允许的最大数组大小

Q4:StringTable 在哪个区域?

答案

  • JDK 7+:(从 PermGen 移入)
  • JDK 6:PermGen(堆内)

注意:字符串常量池(StringTable)是运行时常量池的一部分,但它在 JDK 7 时被移入了堆。

Q5:GC 在什么时候会影响用户线程?

答案:所有 GC 都会产生 Stop-The-World(STW)。

  • MinorGC:短暂的 STW(几十毫秒)
  • FullGC:较长的 STW(数百毫秒到秒级)
  • CMS/G1:部分阶段并发执行,STW 时间缩短
  • ZGC/Shenandoah:STW 时间极短(亚毫秒级)

Q6:虚拟机栈和本地方法栈有什么区别?

答案

  • 虚拟机栈:为 Java 方法服务,存储栈帧(局部变量表、操作数栈、动态链接、返回地址)
  • 本地方法栈:为 native 方法服务,技术上可以分离,但 HotSpot 将两者合二为一

Q7:对象的内存布局是什么?

答案:对象在堆中的内存布局分为三部分:

  1. 对象头(Header):Mark Word(哈希、分代年龄、锁信息)+ 类型指针(指向类元数据)
  2. 实例数据(Instance Data):对象字段的内容
  3. 对齐填充(Padding):保证对象大小是 8 字节的倍数

Q8:直接内存是什么?有什么特点?

答案

  • 直接内存是操作系统分配的本地内存,不属于 JVM 堆
  • 通过 ByteBuffer.allocateDirect() 分配
  • 用途:高性能 IO(Netty、Kafka)
  • 不受 -Xmx 控制,受 -XX:MaxDirectMemorySize 控制
  • 超出限制抛出 OutOfMemoryError: Direct buffer memory

场景化面试题

场景题一:服务器 CPU 100%,如何定位?

思路

  1. top / jps 找到进程
  2. jstack <pid> 查看线程堆栈
  3. jstat -gc <pid> 查看 GC 情况
  4. jmap -heap <pid> 查看堆使用
  5. jmap -dump:file=/tmp/heap.hprof <pid> 导出堆文件分析

场景题二:如何排查 OOM?

思路

  1. 开启 -XX:+HeapDumpOnOutOfMemoryError
  2. 分析堆转储(MAT / JProfiler)
  3. 找到占用最大的对象
  4. 定位引用链路(Who holds the reference?)
  5. 修复代码

场景题三:如何减少 FullGC 频率?

思路

  1. 减少对象分配频率(对象池、缓存)
  2. 增大堆或年轻代(减少 GC 频率)
  3. 减少大对象直接进入老年代(-XX:PretenureSizeThreshold
  4. 使用 G1 或 ZGC(减少 FullGC 停顿)

本节小结

运行时数据区的核心知识点:

区域存储内容线程关系GC
PC 寄存器当前指令地址私有
虚拟机栈栈帧/局部变量/操作数栈私有
本地方法栈native 方法栈帧私有
对象实例/数组共享
方法区/元空间类信息/常量/代码共享
直接内存NIO 缓冲区共享Cleaner

通过这张全景图和面试题,你可以系统性地回顾运行时数据区的所有知识点。

到这里,「JVM 运行时数据区」部分全部完成。接下来进入 对象实例化专题

基于 VitePress 构建