JVM内存区域分为五部分:堆(线程共享,存放对象实例和数组,GC主要管理区)、方法区/元空间(线程共享,存储类信息、常量、静态变量等)、虚拟机栈(线程私有,管理Java方法调用)、本地方法栈(线程私有,服务Native方法)、程序计数器(线程私有,记录当前执行字节码行号)。

面试中问JVM内存区域,不是让你背出所有名词,而是看你能否说清每个区域干什么、谁来管理、常见问题在哪。重点不在堆和方法区的大小配置,而在它们如何配合支撑Java程序运行。
堆:对象的“主仓库”,GC盯得最紧的地方
所有通过new创建的对象实例和数组都分配在堆里。它是线程共享的,也是垃圾收集器(GC)主要管理的区域。
- 新生代(Eden + 两个Survivor)负责大部分短期对象;老年代放长期存活或大对象
- OOM最常见原因:堆空间不足(比如内存泄漏、大缓存没清理、堆设置过小)
- 注意:String.intern()在JDK 7+后指向堆中的字符串常量池,不是永久代
方法区:类信息的“档案室”,现在归到元空间(Metaspace)
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等。JDK 8起,永久代被移除,改用本地内存的元空间实现。
- 元空间不再受-XX:MaxPermSize限制,而是由-XX:MaxMetaspaceSize控制(默认无上限,容易撑爆本地内存)
- 动态生成类过多(如CGLIB代理、大量反射、OSGi场景)会快速耗尽元空间
- 常量池现在分两块:运行时常量池(在元空间) + 字符串常量池(在堆)
虚拟机栈与本地方法栈:每个线程独享的“执行台账”
每个线程启动时创建一个虚拟机栈,里面是多个栈帧——每个方法调用对应一个栈帧,保存局部变量表、操作数栈、动态链接、方法出口等。
- 栈深度超限(如无限递归)抛StackOverflowError
- 线程太多或单个栈太大(-Xss设置过高),导致创建新线程失败,报OutOfMemoryError: unable to create new native thread
- 本地方法栈为Native方法服务,行为类似,但具体实现依赖虚拟机
程序计数器:线程私有的“下一行指令标记”
记录当前线程所执行字节码的行号。唯一一个在JVM规范中没有规定OOM情况的区域。
- 多线程环境下,线程切换后靠它恢复执行位置
- 执行Java方法时指向下一条字节码指令;执行Native方法时值为空(Undefined)
- 极轻量,几乎不参与调优,但它是理解线程隔离机制的关键一环

















