目录
[一、先明确:JVM 对变量的存储规则(固定不变)](#一、先明确:JVM 对变量的存储规则(固定不变))
[二、基本类型:int a = 10; int b = 10;](#二、基本类型:int a = 10; int b = 10;)
[1. 存储流程](#1. 存储流程)
[2. 内存样子(栈中)](#2. 内存样子(栈中))
[3. a == b 底层执行](#3. a == b 底层执行)
[三、引用类型:Integer a = new Integer(10); Integer b = new Integer(10);](#三、引用类型:Integer a = new Integer(10); Integer b = new Integer(10);)
[1. 存储流程](#1. 存储流程)
[2. 内存样子](#2. 内存样子)
[3. a == b 底层执行](#3. a == b 底层执行)
[四、一句话总结 == 的底层逻辑](#四、一句话总结 == 的底层逻辑)
[1. 为什么 new 一定是不同地址?](#1. 为什么 new 一定是不同地址?)
[2. 为什么基本类型没有地址概念?](#2. 为什么基本类型没有地址概念?)
[3. 为什么引用类型变量本身在栈?](#3. 为什么引用类型变量本身在栈?)
用 纯流程、纯内存结构讲一遍。
一、先明确:JVM 对变量的存储规则(固定不变)
- 方法内局部变量 :全部放在 虚拟机栈(VM Stack)
- new 出来的对象实例 :全部放在 堆(Heap)
- 引用变量本身 :存在栈里,存的是堆地址
- 基本类型变量 :存在栈里,存的是真实值
二、基本类型: int a = 10; int b = 10;
1. 存储流程
- 线程执行到 int a = 10;
- 在当前栈帧开辟一块小空间
- 直接把 数值 10 的二进制 存进去
- 执行 int b = 10;
- 再在栈帧开另一块空间
- 直接把 数值 10 的二进制 存进去
2. 内存样子(栈中)
a: 10
b: 10
没有堆参与, 没有对象,没有地址。
3. a == b 底层执行
JVM 执行 数值比较指令(如 if_icmpne 等)
直接读取栈上两块内存的二进制 → 完全一样 → true
三、引用类型: Integer a = new Integer(10); Integer b = new Integer(10);
1. 存储流程
- 执行 new Integer(10)
- 在堆上创建一个 Integer 对象
- 该对象内部存着值 10
-
把这个堆对象的内存地址,存入栈上的变量 a
-
再次 new Integer(10)
- 在堆上另一个位置再创建一个新对象
- 把新地址存入栈上变量 b
2. 内存样子
栈:
a = 0x11223344(堆地址1)
b = 0x55667788(堆地址2)
堆:
0x11223344: Integer{ value=10 }
0x55667788: Integer{ value=10 }
3. a == b 底层执行
JVM 不看对象内容
只比较 栈里存的两个地址值
0x11223344 != 0x55667788 → 结果 false
四、一句话总结 == 的底层逻辑
- 基本类型 == :栈 ↔ 栈,直接比二进制值
- 引用类型 == :栈 ↔ 栈,比的是两个引用存的堆地址 跟对象内部的值完全无关
五、最容易混淆的点,一次性说透
1. 为什么 new 一定是不同地址?
因为 new 指令的语义就是: 在堆上分配新内存
每次 new 都是一块 全新、独立的内存
所以地址一定不同 → == 一定 false
2. 为什么基本类型没有地址概念?
因为基本类型 不是对象
JVM 设计就是: 值直接存在栈上,不进堆
所以 == 只能是值比较
3. 为什么引用类型变量本身在栈?
因为引用本质是 指针/地址
很小(4或8字节),存在栈里访问极快
对象本体大,放堆里统一管理
六、最精简记忆版
- 基本类型变量 :栈里存值
- 引用类型变量 :栈里存堆地址
- == :永远只比较**栈里存的东西。**基本类型 → 比值。 引用类型 → 比地址