JVM运行机制
- 方法只定义,不调用,是不会执行的,并且在
JVM
中也不会给该方法分配 “ 运行所属 ” 的内存空间。只有在调用这个方法的时候,才会动态的给这个方法分配所属的内存空间 - 在
JVM
中有三块主要的内存空间:- 方法区内存【存储
.class
代码】 - 堆内存【存储 对象/实例】
- 栈内存【存储局部变量,方法执行区域】
- 方法区内存【存储
- 关于 “ 栈 ” 数据结构:
- 栈:
Stack
,是一种数据结构 - 数据结构反映的是数据的存储形态
- 数据结构是独立的学科
- 需要精通:数据结构 + 算法
- 常用的数据结构:数组、队列、栈、链表、二叉树、哈希表 / 散列表…
- 栈:
- 方法代码片段存在哪里?执行过程中的内存在哪里分配?
- 方法代码片属于
.class
字节码文件的一部分,字节码文件在类加载的时候,将其放到了方法区当中,所以JVM
中的三块主要内存空间中方法区最先由数据,存放了代码片段 - 代码片段虽然在方法区内存当中只有一部分,但是可以被重复调用
- 方法代码片属于
- 方法在调用的瞬间,会给该方法分配内存空间,会在栈中发送压栈动作。方法执行结束之后,给该方法分配的内存空间全部释放,此时发送弹栈动作
- 压栈
push
:给方法分配内存 - 弹栈
pop
:释放该方法的内存空间
- 压栈
- 局部变量 在 “ 方法体 ” 中声明。运行阶段内存在栈中分配
代码运行时内存分配分析
详见原博客
以下程序在JVM
中如何执行?
public class testMethod(){
public static void main(String[] args){
int a = 10;
int b = 20;
int retValue = sumInt(a, b);
System.out.println("retValue = " + retValue);
}
public static int sumInt(int i, int j){
int result = i + j;
int num = 3;
int retValue = divide(result, num);
return retValue;
}
public static int divide(int x, int y){
int z = x / y;
return z;
}
}
.java
文件通过编译后,Class Loader
(类加载器)将testMethod.class
放到了方法区内存中。【方法区内存存放的是.class
代码】
JVM 会默认执行入口函数main
,代码执行时 JVM 在栈内存开辟一块空间供main
执行。 JVM 将main
放到了栈内存当中执行,期间发生了压栈(push)动作。栈帧永远指向栈顶元素,栈顶元素是活跃的。
代码一步一步执行后,逐渐为局部变量开辟内存空间。为main
栈开辟了两个名为a、b
的内存空间
当执行到第 5 行的时候也就是int retValue = sumInt(a, b);
,调用了sumInt
方法。调用的这一瞬间 JVM 又给sumInt
开辟内存。而这时栈帧发生改变,指向了sumInt
栈。由于栈帧改变,所以main
已暂停执行、被阻塞,现在执行的是栈顶元素,由于栈帧永远指向的是栈顶元素,所以栈顶元素永远处于活跃状态。
在main
调用sumInt
的时候,在参数传递的时候,实际上传递的是变量中保存的值。将a
和b
变量的值给到sumInt
而不是a
和b
。所以sumInt
无法操作main里面的局部变量【无法得到内存地址】。参数传递的时候是按顺序传递。
执行到第10行的时候int result = i + j;
,这时候需要计算i + j
的结果给result
栈储存。而计算是由中央处理器也就是 CPU 来执行,CPU 将处理后的结果给到result
程序继续执行,当执行到第12行的时候int retValue = divide(result, num);
,又调用 divide
,JVM 给divide
开辟了一块内存空间,发生了压栈动作。逐步开辟x、y、z
栈,而z
的结果需要通过 CPU 计算得到。
而程序遇到return
语句后就会强制弹栈【释放内存空间】,继续栈顶元素的执行,这时候栈顶元素以及变成sumInt
,弹栈后代表第12行int retValue = divide(result, num);
结束。
将divide
的结果给到retValue
而往下走遇到return
强制弹栈return retValue;
。sumInt
弹栈之后代表着第5行int retValue = sumInt(a, b);
结束。
往下走遇到了 System 类,这个类实际与其它 class (包含testMethod.class)一起被加载到代码区。调用 System类 里面的 println 方法后又压栈,执行完成后弹栈,最后main
执行完成弹栈。资源全部释放。
理论
- 方法区存的是字节码
.class
代码 - 方法调用时,参数传递的时候,实际传递的是变量中保存的字面值,而不是变量本身
- 画图时,必须遵循 “ 方法自上而下的顺序依次执行 ” 这个原则
- 代码编译期不会执行任何计算,JVM执行时计算
- 栈结构遵循 先进后出,后进先出 的规则
- 栈内存主要存储的是局部变量
- 栈帧永远在栈顶,栈顶元素永远处于活跃状态,其他元素静止