Java基础

JVM(一) 程序运行时内存分配

2020-11-07  本文已影响0人  Timmy_zzh
  1. 程序运行过程中,java文件的执行流程
  2. 内存分配区域:程序计数器,虚拟机栈,本地方法栈,堆,方法区
  3. 总结

java虚拟机在执行java程序的过程中,会把它所管理的内存划分为不同的数据区域,下图描述了一个.java文件被JVM加载到内存中的过程

java文件加载运行过程.png

1.程序计数器

本质:程序计数器 是虚拟机中一块较小的内存空间,主要用于记录当前线程执行的位置

2.虚拟机栈

栈帧

栈帧时用于支持虚拟机进行方法调用和方法执行的数据结构,线程中执行某一个方法时,方法内部可能会调用其他的方法,每调用一个方法,都会为这个方法创建一个栈帧

2.虚拟机栈结构.png
  1. 局部变量表

    局部变量表是变量值的存储空间,我们调用方法时传递的参数,以及在方法内部创建的局部变量都保存在局部变量表中。

    • 在Java文件编译成class文件时候,就会在方法的Code属性表中的max_locals数据项中,确定该方法需要分配的最大局部变量表的容器,代码如下
 public static int add(int k) {
     int a = 1;
     int b = 2;
     return k + a + b;
     }

先使用javac 编译成.class文件,再使用javap -v 反编译得到字节码如下:

 public static int add(int);
     descriptor: (I)I
     flags: ACC_PUBLIC, ACC_STATIC
     Code:
     stack=2, locals=3, args_size=1
     0: iconst_1
     1: istore_1
     2: iconst_2
     3: istore_2
     4: iload_0
     5: iload_1
     6: iadd
     7: iload_2
     8: iadd
     9: ireturn
     LineNumberTable:
     line 10: 0
     line 11: 2
     line 12: 4</pre>
  1. 操作数栈

    操作数栈也被称为操作栈,是一个后入先出栈,用于局部变量表中数据进行计算的场所。

    • 当一个方法刚刚开始执行的时候,这个方法的操作数栈式空的,当方法执行的过程中,会有各种字节码指令被压入和弹出操作数栈

    • 比如上面的 iadd指令就是将操作数栈中栈顶的两个元素弹出,执行加法运算,并将结果重新压入到操作数栈中

    • 同局部变量表一样,操作数栈的最大深度也在编译的时候写入方法的Code属性表中的max_stacks数据项中。栈中的元素可以式任意java数据类型,包括long和double

  2. 动态连接

    • 动态链接的主要目的是为了支持方法调用过程中的动态链接

    • 在一个class文件中,一个方法要调用其他方法,需要将这些方法的符号引用转化为其所在内存地址中的直接引用,而符号引用存在于方法区中

  3. 返回地址

    • 当一个方法开始执行后,只有两种方法可以退出这个方法:

      • 正常退出:指方法中的代码正常完成,或者遇到任意一个方法返回的字节码指令(如return)并退出,没有抛出任何异常

      • 异常退出:指方法执行过程中遇到异常,并且这个异常再方法体内部没有得到处理,导致方法退出

    • 无论当前方法采用何种方式退出,在方法退出后都需要返回到方法被调用的位置,程序才能继续执行。而虚拟机栈中的“返回地址”就是用来帮助当前方法恢复它的上层方法执行状态

    • 一般来说,方法正常退出时,调用者的PC计数值可以作为返回地址,栈帧中可能保存此计数值。而方法异常退出时,返回地址是通过异常处理器表确定的,栈帧中一般不会保存此部分信息。

实例讲解

public int add1() {
 int i = 1;
 int j = 2;
 int result = i + j;
 return result + 10;
 }

对应的字节码和解释:

 public int add1();
 descriptor: ()I
 flags: ACC_PUBLIC
 Code:
 stack=2, locals=4, args_size=1
 0: iconst_1      (把常量 1 压入操作数栈栈顶)
 1: istore_1      (把操作数栈栈顶元素出栈放入局部变量表索引为 1 的位置)
 2: iconst_2      (把常量 2 压入操作数栈栈顶)
 3: istore_2      (把操作数栈栈顶元素出栈放入局部变量表索引为 2 的位置)
 4: iload_1          (把局部变量表索引为 1 的值放入操作数栈栈顶)
 5: iload_2          (把局部变量表索引为 2 的值放入操作数栈栈顶)
 6: iadd          (将操作数栈出栈两个元素,进行加法运算后,结果放入栈顶)
 7: istore_3      (把操作数栈栈顶元素出栈放入局部变量表索引为 3 的位置)
 8: iload_3          (把局部变量表索引为 3 的值放入操作数栈栈顶)
 9: bipush        10  (把常量 10 压入操作数栈栈顶)
 11: iadd          (将操作数栈出栈两个元素,进行加法运算后,结果放入栈顶)
 12: ireturn          (结束返回)
 LineNumberTable:
 line 16: 0
 line 17: 2
 line 18: 4
 line 19: 8</pre>

3.本地方法栈

4.堆

3.堆内存分配图.png

5.方法区

6.常见异常

总结

4.总结图-程序运行时内存分配.png
上一篇下一篇

猜你喜欢

热点阅读