关于 "int a = 5" 引出对JVM堆栈的理解

本文深入解析Java内存模型,包括基本类型如int变量的内存分配与赋值过程,以及引用类型如int数组的内存布局,详细阐述了JVM中栈和堆的交互方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先上一张Java虚拟机运行时数据区域的图

第一、int a = 5,在内存中是如何的呢?

一位老兄给的言简意赅的回答: 
"int a" => 分配一块内块
"= 5" => 内存赋值为常量5[/quote]

引用这位老兄的回答,再加上我的理解: 

int a = 5;
1.我们假设int a 分配到的内存地址为0xaa。
2.int a存储空间最大为4个字节, 也就是2^32, 也就是可以存储的最大值为2^32.
3. = 5, 就是把这块地址0xaa, 存储值为5.

综上所述, int a = 5. 
1. a是这块内存的名称, 程序员写代码时规定的, 经过编译器编译后, a是计算机不可见.
2. 0xaa是这块内存的地址, 是计算机内存自动分配的, 编译后0xaa计算机仍然可见.
3. int型 值5 是名字为a且地址为0xaa的内存存储的值.

对应到JVM中, 这段语句赋值, 就是虚拟机栈中的一块内存的赋值操作, 其中, 这块内存被程序员写代码时叫做a, 且被虚拟机分配了内存地址为0xaa, 且存储了int型 值5.

第二、int[] a = {5,6,7,8,9}内存模型又是怎样的?

因为是数组类型int[] a, 所以a便是一个引用类型, 则必然有名称为a的内存, 同第一的分析: 这块内存必然有内存地址, 我们也将其地址假设为0xaa.

承上, 这里就有一个问题了, 既然是名称为a且内存地址为0xaa的一块内存, 那么这块内存必然存储了一个值(因为这里赋值了嘛), 这个值会不会就是是{5,6,7,8,9}呢?

不是的, 在多数JVM中规定: 引用类型和数组类型的对象都要被保存到堆内存中. 所以数组对象{5,6,7,8,9} 是保存在堆内存中的, 假设保存数组对象{5,6,7,8,9}的内存的内存地址为0xbb.

综上所述: 上面 "栈内存中名字为a且内存地址为0xaa的一块内存" 存储的值是 "堆内存中一块内存的地址0xbb", 0xbb是 "堆内存中的一块内存的地址", 指向了 "保存数组对象{5,6,7,8,9} 的内存".

ps: 这句总结略绕, 请注意理解句子中各个部分的逻辑关系, 已经添加了双引号, 作为断句.

从这里引出的堆栈关系如下图:

"int a, 地址为0xaa的内存" 保存了 "用来存储数组对象{5, 6, 7, 8, 9}的内存的地址值0xbb", 因此, a 是指向了内存0xbb的, 从逻辑上就是指向了数组对象{5, 6, 7, 8, 9}.

 

ps: 这个堆栈模型是很粗浅的JVM堆栈模型, 是我们了解基础类型, 引用类型对象存储的内存模型。若想深入理解JVM请参看Java虚拟机规范, 以及深入理解Java虚拟机规范等丛书.

,

 

 

### JVM堆栈的工作原理 JVM堆栈是Java虚拟机中用于管理程序执行的重要组成部分,主要分为**堆(Heap)**和**栈(Stack)**两部分。它们各自有不同的职责和工作方式。 #### 堆(Heap) 堆是JVM内存模型中最大的一块区域,所有线程共享。它主要用于存储对象实例和数组。堆的生命周期与JVM的启动和关闭同步,即在JVM启动时创建,在JVM关闭时释放。堆的内存空间是不连续的,因此分配的内存大小是在运行期确定的,具有较大的灵活性,但同时也带来了性能上的挑战。 堆中的内存管理由垃圾收集器负责,通常被划分为**新生代**和**老年代**。新生代使用复制算法进行垃圾回收,而老年代则使用标记-压缩算法。这种分代策略有助于提高垃圾回收的效率。 如果堆中没有足够的内存来为新对象分配空间,并且堆也无法扩展,则会抛出`OutOfMemoryError`异常[^3]。 #### 栈(Stack) 栈是每个线程私有的内存区域,其生命周期与线程相同。栈主要用于存储方法调用时所需的局部变量、操作数栈、动态链接以及返回地址等信息。每个方法调用都会在栈中生成一个对应的**栈帧(Stack Frame)**,栈帧的结构包括局部变量表、操作数栈、动态链接和方法出口等部分。 栈的物理地址分配是连续的,因此它的访问速度较快。由于栈的大小在编译期就已经确定,因此栈的空间相对较小[^1]。 #### 内存分配 - **堆**的内存分配是动态的,对象的创建和销毁都发生在堆上。当对象被创建时,JVM会在堆中为其分配内存;当对象不再被引用时,垃圾收集器会自动回收这些对象所用的内存。 - **栈**的内存分配是静态的,每个方法调用时会在当前线程的栈中压入一个新的栈帧。栈帧的大小和结构在编译期就已经确定,因此栈的内存分配非常高效[^3]。 #### 栈帧的作用 栈帧是JVM执行引擎的核心数据结构之一,它是方法调用的基本单位。每当一个方法被调用时,JVM会在当前线程的栈中创建一个新的栈帧,并将其压入栈顶。栈帧的主要作用如下: - **局部变量表**:用于存储方法参数和局部变量。局部变量表是一个数组,其中每个元素可以是基本类型或对象引用。 - **操作数栈**:用于存放方法执行过程中所需的操作数。例如,在执行加法指令时,操作数栈中的两个值会被弹出并相加,结果再被压回栈顶。 - **动态链接**:用于支持方法调用中的符号引用解析。类加载时,方法的符号引用会被解析为实际的内存地址。 - **方法出口**:用于记录方法调用结束后应返回到何处继续执行。方法正常退出时会通过`return`指令返回,异常退出时则会抛出异常并找到合适的异常处理器[^4]。 ### 示例代码 以下是一个简单的Java方法示例,展示了方法调用时栈帧的变化过程: ```java public class StackExample { public static void main(String[] args) { int result = add(5, 10); System.out.println("Result: " + result); } public static int add(int a, int b) { int c = (a + b) * 10; return c; } } ``` 在这个例子中,当`main`方法调用`add`方法时,JVM会在当前线程的栈中创建一个新的栈帧,并将`a=5`和`b=10`存入局部变量表。接着,`a+b`的结果会被计算出来并乘以10,最终将结果`c=150`返回给`main`方法。整个过程中,栈帧的局部变量表和操作数栈都被用来存储和处理数据[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值