JVM内存划分为线程私有(程序计数器、虚拟机栈、本地方法栈)和线程共享(堆、元空间)区域,前者随线程创建/销毁,后者由GC管理;直接内存虽非规范区域但被NIO使用。

JVM内存区域划分是面试高频考点,核心在于说清“谁存什么、谁管生命周期、谁容易出问题”。不是罗列名词,而是讲清楚每个区域的职责和设计意图。
线程私有区域:每个线程独享,不共享、不冲突
这类区域随线程创建而分配,随线程结束自动释放,天然线程安全。
- 程序计数器:记录当前线程正在执行的字节码指令地址。如果是本地方法(如JNI调用),值为空。它是唯一不会发生OutOfMemoryError的区域,空间极小且固定。
- 虚拟机栈:为每个Java方法调用分配一个栈帧,存储局部变量表、操作数栈、动态链接、方法出口等。栈帧入栈=方法调用,出栈=方法返回。栈深度超限会抛StackOverflowError;栈扩展失败则报OutOfMemoryError。
- 本地方法栈:作用类似虚拟机栈,但服务于Native方法(如C/C++代码)。HotSpot中两者合二为一,但规范上保持独立。
线程共享区域:所有线程共用,需同步,GC重点照顾对象
这两个区域生命周期与JVM一致,启动时创建,退出时销毁,也是内存溢出最常发生的地带。
- 堆(Heap):存放所有new出来的对象实例和数组。是垃圾回收的主要场所,划分为新生代(Eden + 两个Survivor)、老年代。对象优先在Eden分配,经历多次Minor GC后晋升至老年代。OOM常见于堆空间不足或内存泄漏。
- 元空间(Metaspace):JDK 8起取代永久代,存储类的元信息(类名、字段、方法、常量池、运行时常量池等)。它位于本地内存(非堆),大小默认不限(受物理内存约束),可通过-XX:MaxMetaspaceSize限制。类加载过多(如动态生成大量代理类)易引发元空间OOM。
直接内存:虽不在JVM规范内,但实际影响很大
它不属于JVM运行时数据区,但被NIO广泛使用——通过DirectByteBuffer申请堆外内存,避免Java堆与系统缓冲区之间的数据拷贝,提升IO性能。
- 由操作系统直接分配,不受-Xmx参数控制,但受机器物理内存和-XX:MaxDirectMemorySize限制(默认等于-Xmx)。
- DirectByteBuffer对象本身在堆中,仅作引用;真正内存由Cleaner机制异步回收,若频繁创建又未及时触发清理,可能造成直接内存溢出(OutOfMemoryError: Direct buffer memory)。
为什么这么划分?关键逻辑就三点
一是隔离生命周期:线程私有数据随线程消亡,无需GC介入;共享数据统一管理,便于回收和并发控制。
- 二是提升效率:栈分配快、回收零成本;堆支持动态对象生命周期,配合分代GC优化吞吐与延迟。
- 三是支撑语言特性:比如方法调用依赖栈帧结构,反射/动态代理依赖元空间存储类定义,NIO高性能依赖直接内存。

















