JVM运行时数据区
Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存区域划分为若干个 不同的数据区域。
这些区域都有各自的用途,以及创建和销毁的时间,有些区域随着虚拟机进程的启动而存在,有些区域则是依赖线程的启动和结束而建立和销毁。Java 虚拟机所管理的内存被划分为如下几个区域不同虚拟机的运行时数据区可能略微有所不同,但都会遵从 Java 虚拟机规范, Java 虚拟机规范规定的
区域分为以下 5 个部分:
1.程序计数器(Program Counter Register)
-
作用:当前线程所执行的字节码的行号 指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的 字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个 计数器来完成;
-
特点:
-
线程私有,生命周期与线程一致。
-
唯一不会发生内存溢出的区域。
-
2.Java 虚拟机栈(Java Virtual Machine Stacks):
-
作用:用于存储局部变量表、操作 数栈、动态链接、方法出口等信息;
-
结构:
- 局部变量表:存放基本数据类型、对象引用(Reference)。
- 操作数栈:执行字节码指令的工作区。
- 动态链接:将符号引用转换为直接引用。
- 返回地址:方法退出后继续执行的地址。
-
特点:
- 线程私有,通过-Xss设置栈大小。
- 栈深度过大或不足时抛出 StackOverflowError 或 OutOfMemoryError。
-
示例:无限递归导致栈溢出。
3.本地方法栈(Native Method Stack)
-
作用:与虚拟机栈的作用是一样的,只不过虚 拟机栈是服务 Java方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;。
-
特点:类似虚拟机栈,部分JVM实现(如HotSpot)将其与虚拟机栈合并。
-
异常:同虚拟机栈。
4.Java 堆(Java Heap)
-
作用:Java 虚拟机中内存大的一块,是被所有线程共享 的,几乎所有的对象实例都在这里分配内存,垃圾回收的主战场。
-
结构:
- 新生代(Young Generation):Eden区、Survivor区(From/To)。
- 老年代(Old Generation):长期存活的对象。
-
特点:
- 线程共享,通过-Xms(初始堆)、-Xmx(最大堆)调整大小。
- 内存不足时抛出 OutOfMemoryError。
-
GC机制:
-
对象优先在Eden分配,大对象直接进入老年代。
-
长期存活(默认15次GC)或动态年龄判断的对象晋升老年代。
-
5.方法区(Methed Area)
- 作用:方法区,好像也叫元空间(在JDK8之后),用于存储已被虚拟机加载的类信息、常量、静态变 量、即时编译后的代码等数据。
- 实现:
- JDK8前:永久代(PermGen),通过-XX:PermSize调整。
- JDK8+:元空间(Metaspace),使用本地内存,通过-XX:MaxMetaspaceSize限制。
- 特点:
- 线程共享,内存不足时抛出 OutOfMemoryError。
- 运行时常量池:存放编译期生成的字面量、符号引用,String.intern()会在此操作。
各区域对比表
区域 | 线程共享 | 内存溢出 | 存储内容 | 参数配置 |
---|---|---|---|---|
程序计数器 | 私有 | 无 | 当前指令地址 | 无 |
虚拟机栈 | 私有 | StackOverflowError/OOM | 栈帧、局部变量等 | -Xss |
本地方法栈 | 私有 | StackOverflowError/OOM | Native方法调用 | 依赖JVM实现 |
堆 | 共享 | OutOfMemoryError | 对象实例、数组 | -Xms, -Xmx |
方法区(元空间) | 共享 | OutOfMemoryError | 类信息、运行时常量池 | -XX:MaxMetaspaceSize |
常见问题及调优
- 堆内存溢出:检查对象生命周期,避免内存泄漏,调整堆大小。
- 栈溢出:优化递归或循环,增加栈大小(-Xss)。
- 元空间溢出:检查类加载器泄漏,限制元空间大小。
核心要点总结
- 线程私有区域(独立隔离,无并发问题)
- 程序计数器:唯一无内存溢出的区域,标记线程执行位置。
- 虚拟机栈:方法调用的栈帧(局部变量、操作数栈等),递归过深导致
StackOverflowError
。 - 本地方法栈:服务于Native方法(如JNI调用),部分JVM与虚拟机栈合并。
- 线程共享区域(需关注线程安全与垃圾回收)
- 堆(Heap):
- 对象实例和数组的存储核心区,GC主战场。
- 分代设计(新生代/老年代)优化垃圾回收效率。
- 内存不足时抛出
OutOfMemoryError
,需关注对象生命周期和泄漏。
- 方法区(元空间):
- 存储类元数据、常量池(JDK8后元空间取代永久代,避免
PermGen OOM
)。 String.intern()
直接操作运行时常量池。
- 存储类元数据、常量池(JDK8后元空间取代永久代,避免
- 堆(Heap):