TypechoJoeTheme

Clover 的博客

统计
登录
用户名
密码
/
注册
用户名
邮箱

CloverYou

日出于东却落于西,相识人海却散于席。

6、Java方法执行与内存分析

2022-01-02
/
0 评论
/
24 阅读
/
正在检测是否收录...
01/02

理论

  • 方法在执行过程中,在JVM中的内存是如何分配的呢,内存是如何变化的?

    1. 方法只定义,不调用,是不会执行的,并且在JVM也不会给该方法分配运行所属的内存空间。
      只有在调用这个方法的时候,才会动态的给这个方法分配所属的内存空间。
    2. JVM内存划分上有这样三块主要的内存空间(还有其它的内存空间):

      • 方法区内存
      • 栈内存
      • 堆内存
    3. 关于数据结构:

      • 栈: stack,是一种数据结构
      • 数据结构反应的是数据的存储形态。
      • 数据结构是独立的学科,不属于任何编程语言的范畴,只不过在大多数编程语言当中要使用数结构。
      • 作为程序员需要提前精通:数据结构 + 算法[计算机必修]
    4. 方法执行的时候代码片段存在哪里?方法执行的时候执行过程的内存在哪里分配?

      • 方法代码片段属于.class文件的一部分,字节码文件在类载(classLoader、类加载器)加载的时候,将其放到了方法区内存当中。所以JVM中的三块主要的内存空间中方法区内存最先有数据。
      • 代码片段虽然在方法区内存当中只有一份,但是可以被重复调用。每一次调用这个方法的时候,需要给该方法分配独立的活动场所,在栈内存中分配。【栈内存中分配方法运行的所属内存空间】
    5. 方法在调用瞬间,在栈内存中会给该方法分配独立的内存空间,此时发生压栈动作(push),方法执行结束之后,给该方法分配的内存空间全部释放,此时发生弹栈动作(pop)。

      • 压栈: 给该方法分配内存
      • 弹栈: 释放该方法的内存空间
    6. 局部变量在方法体中声明,局部变量在运行阶段内存在栈中分配。

      • 局部变量有形参也有在方法体定义的局部变量。
      • 局部变量生命周期最短,因为只要出了这个方法的大括号,那么就意味着这个方法结束了,方法结束了那么就再也没有机会去访问这个内存空间因为内存空间释放掉了,没有了。

JVM执行原理图

栈数据结构

  1. 栈帧永远指向栈顶元素。
  2. 栈顶元素处于活跃状态,其它元素静止。
  3. 术语:

    • 压栈/入栈/push
    • 弹栈/出栈/pop
  4. 栈数据结构的特点是:

    • 先进后出
    • 后进先出

内存分析

以下程序在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放到了方法区内存中。

JVM会默认执行入口函数main,代码执行时JVM在栈内存开辟一块空间供main执行。JVMmain放到了栈内存当中执行,期间发生了压栈(push)动作。栈帧永远指向栈顶元素,栈顶元素是活跃的。

代码一步一步执行后,逐渐为局部变量开辟内存空间。为main栈开辟了两个名为a、b的内存空间

当执行到第 5 行的时候也就是int retValue = sumInt(a, b);,调用了sumInt方法。调用的这一瞬间JVM又给sumInt开辟内存。而这时栈帧发生改变,指向了sumInt栈。由于栈帧改变,所以main已暂停执行、被阻塞,现在执行的是栈顶元素,由于栈帧永远指向的是栈顶元素,所以栈顶元素永远处于活跃状态。

main调用sumInt的时候,在参数传递的时候,实际上传递的是变量中保存的值。将ab变量的值给到sumInt而不是ab。所以sumInt无法操作main里面的局部变量(无法得到内存地址)。参数传递的时候是按顺序传递。

执行到第10行的时候 int result = i + j;,这时候需要计算 i + j 的结果给result栈储存。而计算是由中央处理器也就是CPU来执行,CPU将处理后的结果给到result

程序继续执行,当执行到第12行的时候int retValue = divide(result, num);,又调用 divide,JVMdivide开辟了一块内存空间,发生了压栈动作。逐步开辟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执行完成弹栈。资源全部释放。

代码是逐行执行,从上倒下。

代码编译期不会执行任何计算,JVM执行时计算

栈结构遵循 先进后出,后进先出 的规则

栈内存主要存储的是局部变量

方法调用的时候,在传参的时候,实际上传递的是变量保存的

栈帧永远在栈顶,栈顶元素永远处于活跃状态

JAVA学习笔记
朗读
赞(0)
版权属于:

Clover 的博客

本文链接:

https://www.ctong.top/index.php/archives/13/(转载时请注明本文出处及文章链接)

评论 (0)
CloverYou
日出于东却落于西,相识人海却散于席。
88 文章数
11 评论量
IP信息

人生倒计时

今日已经过去小时
这周已经过去
本月已经过去
今年已经过去个月

最新回复

  1. 缓存一致性 - 点击领取
    2022-01-06
  2. 宝宝
    2022-01-02

标签云