Java 运行时数据区 - 总览
Java 虚拟机(JVM)在运行 Java 程序时管理一块内存区域,称为运行时数据区。根据《Java 虚拟机规范》,它分为多个部分,各有特定作用。
1. Java 虚拟机栈
定义与特性
Java 虚拟机栈(Java Virtual Machine Stack) 是 JVM 用于管理方法调用的内存区域,采用栈式数据结构(先进后出,FILO)。每个方法调用创建一个栈帧(Stack Frame)。
- 生命周期:随线程创建而创建,随线程销毁而回收。
- 线程私有:每个线程拥有独立栈。
栈帧的组成
1.1 局部变量表
- 作用:存储方法运行时的局部变量。
- 结构:数组形式,每个元素为槽(Slot)。
long
和double
占用 2 个槽。- 其他类型占用 1 个槽。
- 内容:
- 实例方法第 0 槽存储
this
。 - 方法参数按声明顺序存储。
- 方法体内局部变量。
- 实例方法第 0 槽存储
- 优化:槽可复用。
1.2 操作数栈
- 作用:临时存储指令执行的数据。
- 特性:栈式结构,编译期确定最大深度。
1.3 帧数据
- 动态链接:符号引用转为运行时常量池地址。
- 方法出口:记录返回地址。
- 异常表:存储异常处理信息。
栈内存溢出
- 原因:栈帧过多,超出容量。
- 异常:
StackOverflowError
。
示例代码
public class StackOverflowTest {
public static int count = 0;
public static void recursion() {
System.out.println(++count);
recursion(); // 递归调用导致溢出
}
public static void main(String[] args) {
recursion();
}
}
设置栈大小
- 参数:
-Xss<大小>
或-XX:ThreadStackSize=<大小>
。 - 单位:字节(需为 1024 倍数)、
k/K
(KB)、m/M
(MB)、g/G
(GB)。 - 限制(HotSpot JVM):
- Windows 64 位,JDK 8:最小 180KB,最大 1024MB。
- 建议:
-Xss256k
节省内存。
2. 本地方法栈
- 作用:管理
native
本地方法的栈帧。 - 实现:HotSpot 中与 Java 虚拟机栈共用空间。
3. 堆
定义与特性
堆(Heap) 是 JVM 最大内存区域,存储对象实例。
- 线程共享:通过引用(如局部变量或静态变量)访问。
- 关键指标:
used
:已使用内存。total
:已分配可用内存。max
:最大可用内存。
- 默认值:
max
:系统内存 1/4。total
:系统内存 1/64。
设置堆大小
- 参数:
-Xms<大小>
:初始堆大小。-Xmx<大小>
:最大堆大小。
- 单位:字节(需为 1024 倍数)、
k/K
、m/M
、g/G
。 - 建议:
-Xms
和-Xmx
设为相同值,避免频繁调整。
4. 方法区
定义与作用
方法区(Method Area) 是线程共享区域,存储类信息和常量。
核心组成部分
- 类的元信息:
- 存储类的结构(如
InstanceKlass
)。
- 存储类的结构(如
- 运行时常量池:
- 将静态常量池转为直接引用。
- 字符串常量池:
- 存储字符串常量。
方法区溢出
- JDK 7:位于永久代(PermGen),用
-XX:MaxPermSize
控制。 - JDK 8+:移至元空间(Metaspace),默认无上限,可用
-XX:MaxMetaspaceSize
限制。
5. ByteBuddy 框架简介
ByteBuddy 是用于动态生成和操作 Java 字节码的库。
使用步骤
- 引入依赖(Maven):
<dependency> <groupId>net.bytebuddy</groupId> <artifactId>byte-buddy</artifactId> <version>1.14.12</version> </dependency>
- 生成字节码:
import net.bytebuddy.ByteBuddy; import net.bytebuddy.implementation.FixedValue; import static net.bytebuddy.matcher.ElementMatchers.named; public class ByteBuddyExample { public static void main(String[] args) throws Exception { Class<?> dynamicType = new ByteBuddy() .subclass(Object.class) .method(named("toString")) .intercept(FixedValue.value("Hello, ByteBuddy!")) .make() .load(ByteBuddyExample.class.getClassLoader()) .getLoaded(); System.out.println(dynamicType.newInstance().toString()); } }