内存地址(Memory Address)
内存就像一个巨大的“公寓楼”,每间房都有一个编号(比如 0x1000、0x1004),这个编号就是内存地址。
- 💡 作用:
计算机通过地址找到数据存放在哪里。变量、对象、函数代码都住在内存的不同“房间”里。
一.什么是“内存”?
💡 想象:内存就像一栋大楼
- 这栋大楼有很多房间(每个房间是一个“内存地址”)。
- 每个房间可以存放数据(比如数字、字母)。
- 程序运行时,所有的变量、对象都住在这栋“内存大楼”里。
✅ 内存(Memory) = 程序运行时用来存储数据的物理空间(通常是 RAM)。
二、内存的两个主要区域:栈 和 堆
这栋“内存大楼”被划分为两个区域:
区域 | 英文 | 特点 | 存什么 |
---|---|---|---|
栈 | Stack | 自动管理、速度快、空间小 | 局部变量、方法调用 |
堆 | Heap | 手动/自动管理、速度慢、空间大 | 对象、数组 |
我们来一个个讲。
三、栈(Stack)——“自动售货机式的存储”
🎯 特点:
- 后进先出(LIFO):就像一摞盘子,只能从上面拿或放。
- 自动管理:函数调用结束,变量自动销毁。
- 速度快:因为结构简单,编译器能高效管理。
- 空间小:不能存太大或太多东西。
🧩 栈里存什么?
- 基本类型变量:
int
,boolean
,double
等 - 引用变量(注意:引用本身在栈里,它指向的对象在堆里)
- 方法调用信息:比如参数、返回地址
public void methodA() { int a = 10; // a 在栈上 boolean flag = true; // flag 在栈上 ListNode cur = new ListNode(0); // cur(引用)在栈上,但 new ListNode(0) 在堆上! }
执行时,栈的样子:
栈(Stack)
+----------------+
| cur -> 0x123 | ← methodA 调用时
| flag = true |
| a = 10 |
+----------------+
当 methodA
执行完,整个这一块自动“弹出”,a
, flag
, cur
全部消失。
四、堆(Heap)——“自由市场式的存储”
🎯 特点:
- 手动/自动管理:Java 用垃圾回收(GC)自动清理。
- 空间大:可以存大型对象、数组。
- 速度慢:因为要动态分配,管理复杂。
- 全局访问:堆上的对象可以被多个引用访问。
🧩 堆里存什么?
- 对象:
new
出来的任何东西 - 数组
- 所有通过
new
创建的数据
ListNode node = new ListNode(0);
内存布局:
栈(Stack) 堆(Heap)
+-----------+ +-------------+
node:| 0x123 | --> | val: 0 |
+-----------+ | next: null |
+-------------+
node
是一个引用,它在栈上,存的是地址0x123
。new ListNode(0)
是一个对象,它在堆上,地址是0x123
。
五、关键区别:引用 vs. 对象
项目 | 栈(Stack) | 堆(Heap) |
---|---|---|
存什么 | 引用变量(如 node ) |
对象本身(如 new ListNode(0) ) |
生命周期 | 方法结束就销毁 | 直到没人引用它(GC 回收) |
访问方式 | 直接访问 | 通过引用访问 |
🔑 引用是“钥匙”,堆是“房子”,栈是“钥匙串”。
六、一个完整例子:看栈和堆如何协作
public class Example {
public static void main(String[] args) {
ListNode pre = new ListNode(0);
ListNode cur = pre;
cur.val = 999;
}
}
🧱 执行过程:
1. ListNode pre = new ListNode(0);
栈(Stack) 堆(Heap)
+-----------+ +-------------+
pre: | 0x123 | --> | val: 0 |
+-----------+ | next: null |
+-------------+
2. ListNode cur = pre;
栈(Stack) 堆(Heap)
+-----------+ +-------------+
pre: | 0x123 | --> | val: 0 |
+-----------+ | next: null |
cur: | 0x123 | --> | |
+-----------+ +-------------+
👉 pre
和 cur
都在栈上,指向堆中同一个对象。
3. cur.val = 999;
栈(Stack) 堆(Heap)
+-----------+ +-------------+
pre: | 0x123 | --> | val: 999 | ← 被修改了!
+-----------+ | next: null |
cur: | 0x123 | --> | |
+-----------+ +-------------+
👉 虽然 cur
在栈上,但它通过引用修改了堆上的对象。
所以 pre.val
也会是 999!