一、引言
Java 内存模型(Java Memory Model,JMM)是理解 Java 程序运行机制的关键。它规定了 Java 虚拟机(JVM)如何管理和访问内存,涉及方法区、栈、堆等核心区域。本文将结合代码示例与内存图,详细剖析 Java 内存的工作原理,帮助你从底层理解程序的执行过程。
二、Java 内存核心区域概述
(一)方法区
方法区是 JVM 中用于存储类结构信息的区域,包含类常量池和静态常量池:
- 类常量池:存储类的元数据,如类名、方法名、字段信息、常量等,是类加载时的 “蓝图”。
- 静态常量池:专门存放静态成员(
static修饰的变量、方法),属于类的共享资源,所有实例共享同一静态数据。
(二)栈(Stack)
栈又称 “线程栈”,为每个线程单独分配,用于存储方法执行时的局部变量、方法调用栈帧等。特点:
- 线程私有,随线程创建而创建,销毁而销毁。
- 栈帧(Stack Frame):每个方法调用对应一个栈帧,包含局部变量表、操作数栈、返回地址等。
(三)堆(Heap)
堆是 JVM 中最大的内存区域,用于存储对象实例和数组。特点:
- 线程共享,所有线程可访问堆中对象。
- 对象创建(
new)时在堆中分配空间,包含对象头(如类型指针、锁信息)、实例数据(普通变量)等。
(四)字符串常量池(String Constant Pool)
属于方法区的一部分,专门存储字符串常量(如 String res = "demo"; 中的 "demo"),避免重复创建,提升内存效率。
三、代码示例与内存图解析
(一)基础类型与字符串示例
public class Test {
public static void main(String[] args) {
int a = 20;
String res = "demo";
}
}

内存图剖析:
- 方法区:
- 类常量池:存储
Test类的元数据(如main方法定义)。 - 静态常量池:因
main中无静态成员,暂时为空(若有static变量,会在此分配)。
- 类常量池:存储
- 栈(线程栈 - main 方法栈帧):
- 局部变量表:
a(基本类型int)直接存储值20;res(引用类型String)存储堆中对象的地址,指向字符串常量池的"demo"。
- 局部变量表:
- 堆:无普通对象(
res实际指向字符串常量池,堆中无额外对象)。 - 字符串常量池:存储
"demo",res引用指向此处。
关键点:
- 基本类型(如
int)的局部变量直接在栈中存值。 - 字符串常量(
"demo")优先存入字符串常量池,引用类型变量存地址。
(二)对象与静态成员示例
public class Cat {
public String name;
public int weight;
public static String country = "中国";
public static void run() {
System.out.println("跑跑跑");
}
public void eat() {
System.out.println("吃吃吃");
}
@Override
public String toString() {
return "Cat [name=" + name + ", weight=" + weight + ", country=" + country + "]";
}
}
public class Test {
public static void main(String[] args) {
Cat cat1 = new Cat();
cat1.name = "小花";
cat1.weight = 10;
Cat cat2 = new Cat();
cat2.name = "小黑";
cat2.weight = 18;
System.out.println("小猫1:" + cat1);
System.out.println("小猫2:" + cat2);
}
}

内存图剖析:
- 方法区:
- 类常量池:存储
Cat和Test类的元数据(方法定义、字段信息等)。 - 静态常量池:存储
Cat的静态成员country(值为"中国")和run方法,所有Cat实例共享。
- 类常量池:存储
- 栈(线程栈 - main 方法栈帧):
- 局部变量
cat1、cat2存储堆中Cat对象的地址。
- 局部变量
- 堆:
- 两个
Cat对象(cat1、cat2):- 对象头:包含类型指针(指向方法区
Cat类常量池)、锁信息等。 - 实例数据:
name(存字符串常量池地址,如"小花"、"小黑")、weight(存值10、18)。
- 对象头:包含类型指针(指向方法区
- 两个
- 字符串常量池:存储
"中国"、"小花"、"小黑",供name和country引用。
关键点:
- 静态成员(
country、run)属于类,存方法区静态常量池,所有实例共享。 - 对象(
cat1、cat2)在堆中分配,包含类型指针(关联方法区类信息)。
(三)数组与方法调用示例
public class Test {
public static void main(String[] args) {
int[] arr = {5, 7, 100, 2};
sort(arr);
}
public static void sort(int[] arr) {
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
}

内存图剖析:
- 方法区:
- 类常量池:存储
Test类元数据(main、sort方法定义)。 - 静态常量池:无额外静态成员(
Arrays类的静态方法sort属于 Java 核心库,在 JVM 启动时加载)。
- 类常量池:存储
- 栈(线程栈):
main栈帧:局部变量arr存储堆中数组的地址。sort栈帧:参数arr与main中的arr指向同一堆数组。
- 堆:
- 数组对象:存储元素
5,7,100,2,排序后变为2,5,7,100(数组在堆中,方法调用修改堆中数据,会影响所有引用)。
- 数组对象:存储元素
关键点:
- 数组是对象,存于堆中,引用类型变量存地址。
- 方法调用时,栈帧嵌套,引用类型参数传递的是地址(堆数据共享)。
四、深入理解内存机制
(一)引用类型与基本类型的区别
| 类型 | 存储位置(局部变量) | 特点 | 示例 |
|---|---|---|---|
基本类型(int) | 栈(直接存值) | 变量直接持有值 | int a = 20; |
引用类型(String) | 栈(存地址) | 变量持有堆 / 常量池的地址 | String res = "demo"; |
(二)== 与 equals 的本质
==:判断地址是否相同(栈中存储的引用是否指向同一堆 / 常量池对象)。equals:默认判断地址(Object类实现),但像String重写后判断值是否相同。
(三)方法调用的栈帧机制
- 方法调用时,JVM 为每个方法创建栈帧,入栈执行;方法返回时,栈帧出栈。
- 局部变量、参数存储于栈帧的局部变量表,堆数据通过地址共享(引用类型)。
五、总结
Java 内存模型通过方法区、栈、堆的协同工作,支撑程序的运行:
- 方法区:存储类元数据、静态成员,是类的 “蓝图”。
- 栈:为线程和方法提供临时存储,管理局部变量与调用栈。
- 堆:存储对象实例与数组,是程序数据的核心载体。
- 字符串常量池:优化字符串存储,避免重复创建。
理解内存模型,能帮你解决对象共享、空指针异常、内存泄漏等问题,是进阶 Java 开发的必经之路。下次写代码时,不妨想想变量在内存中的 “行踪”,你会对程序运行有更深的掌控!
(注:内存图可结合文中描述,手绘或用工具(如 ProcessOn)绘制,辅助理解区域关联。)
789

被折叠的 条评论
为什么被折叠?



