1. 内存模型结构图解
JVM 内存模型(JDK 8+)
├── **线程私有区**
│ ├── 程序计数器(Program Counter Register)
│ ├── 虚拟机栈(VM Stack)
│ │ └── 栈帧(局部变量表、操作数栈、动态链接、方法出口)
│ └── 本地方法栈(Native Method Stack)
└── **线程共享区**
├── 堆(Heap)
│ ├── 年轻代(Eden、Survivor S0/S1)
│ └── 老年代(Old Generation)
└── 元空间(Metaspace)
2. 各区域功能与代码示例
2.1 虚拟机栈(VM Stack)
功能:存储方法调用的栈帧,每个栈帧对应一个方法的执行状态。
代码示例:
public class StackExample {
public static void main(String[] args) {
int a = 1; // 局部变量a存入局部变量表
int b = a + 2; // 操作数栈 压入a和2,执行加法后存入b
recursiveMethod(10000); // 递归过深触发StackOverflowError
}
public static void recursiveMethod(int n) {
if (n <= 0) return;
recursiveMethod(n - 1); // 栈帧持续入栈,超出栈容量时溢出
}
}
- 溢出场景:递归深度过大或局部变量占用过高。
- 参数调优:
-Xss256k # 设置线程栈大小为256KB(默认1MB)
2.2 堆(Heap)
功能:存储所有对象实例和数组对象。这是 JVM 中最大的一块内存区域。
代码示例:
public class HeapExample {
public static void main(String[] args) {
Object obj1 = new Object(); // 对象分配在堆的Eden区
Object obj2 = new Object();
obj1 = null; // obj1引用断开,对象成为垃圾待回收
System.gc(); // 触发Minor GC,存活对象晋升至Survivor区
}
}
- 堆内存划分:
- 年轻代:新对象优先分配在 Eden 区,存活对象经 Minor GC 后进入 Survivor 区。
- 老年代:长期存活对象或大对象直接进入老年代(如
new byte[10MB]
)。
- 参数调优:
-Xms512m -Xmx2g # 设置堆初始大小512MB,最大2GB
2.3 元空间(Metaspace)
功能:存储类的元数据,如类结构信息(字段、方法、构造器)、常量池(Constant Pool)、静态变量(static
修饰的变量)以及 JIT 编译后的代码缓存。
代码示例:
public class MetaspaceExample {
static final String CONSTANT = "JVM"; // 常量存入运行时常量池
static String staticVar = "Static"; // 静态变量存储于元空间
public static void main(String[] args) {
Class<?> clazz = MetaspaceExample.class; // 类元数据加载到元空间
}
}
- 参数调优:
-XX:MaxMetaspaceSize=256m # 限制元空间最大容量为256MB
2.4 程序计数器(Program Counter Register)
功能:记录当前线程执行的字节码指令地址(Native 方法执行时为空)。
代码示例:
public class CounterExample {
public static void main(String[] args) {
new Thread(() -> {
for (int i = 0; i < 10; i++) {
// 程序计数器记录循环指令地址
System.out.println(i);
}
}).start();
}
}
2.5 本地方法栈(Native Method Stack)
功能:支持 JNI(Java Native Interface)方法的调用。
代码示例:
public class NativeExample {
public native void nativeMethod(); // JNI方法调用使用本地方法栈
static {
System.loadLibrary("NativeLib");
}
}
3. 内存交互与线程安全示例
多线程共享堆内存问题:
public class ThreadSafeExample {
static int count = 0; // 静态变量存储于元空间,线程共享
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
count++; // 非原子操作:读取→修改→写入
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
count--;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count); // 结果可能非0(指令交错执行)
}
}
- 解决方案:
- 使用
synchronized
同步代码块。 - 使用
AtomicInteger
替代int
。
- 使用
4. 总结
- 线程私有区:
- 虚拟机栈管理方法调用栈帧,程序计数器记录指令地址。
- 本地方法栈支持 JNI 方法调用。
- 线程共享区:
- 堆存储对象实例,元空间存储类元数据(JDK 8+)。
- 参数调优:
- 堆大小(
-Xms
、-Xmx
)、线程栈大小(-Xss
)、元空间限制(-XX:MaxMetaspaceSize
)。
- 堆大小(
- 线程安全:共享数据需同步或使用原子类。