JVM 运行时数据区一览
线程私有的区域
- 程序计数器:记录当前线程执行到的字节码位置,是唯一不会发生内存溢出的区域。
- Java 虚拟机栈:每个方法调用对应一个栈帧,包含局部变量表、操作数栈、动态链接和返回地址。大小用
-Xss设置,递归过深会抛出StackOverflowError。 - 本地方法栈:为 native 方法服务,作用和虚拟机栈类似。
线程共享的区域
- 堆:存放对象实例,是垃圾回收的主要区域。传统分代模型把它分成新生代(Eden 和两个 Survivor)与老年代;G1 把堆划成大小相等的 Region,但逻辑上仍然分代。初始和最大值用
-Xms、-Xmx设置。 - 方法区:存放类的元信息、运行时常量池等。JDK 8 起由元空间(Metaspace)实现,使用本地内存,上限用
-XX:MaxMetaspaceSize设置。字符串常量池在 JDK 7 就已经移到了堆中。
不属于运行时数据区,但常被问到的:直接内存
NIO 的 DirectByteBuffer 在堆外分配内存,减少了在 Java 堆和本地内存之间来回复制。上限用 -XX:MaxDirectMemorySize 设置。
常见错误对照
| 错误信息 | 通常的原因 |
|---|---|
OutOfMemoryError: Java heap space | 堆太小,或者有内存泄漏 |
OutOfMemoryError: Metaspace | 加载的类太多,例如动态生成大量代理类 |
OutOfMemoryError: Direct buffer memory | 直接内存用尽,常见于 Netty 等 NIO 应用 |
StackOverflowError | 递归过深或栈设置过小 |