Loading

运行时数据区和指令

1.JVM运行时数据区(JVM Runtime Data Area)

image

image

image

1.1 Program Counter (程序计数器)

Each Java Virtual Machine thread has its own pc (program counter) register

每个java虚拟机线程都有自己的PC(程序计数器)寄存器

At any point, each Java Virtual Machine thread is executing the code of a single method, namely the current method for that thread

在任何时候,每个java虚拟机线程都在执行单一的方法代码,既该线程的当前方法

If that method is not native , the pc register contains the address of the Java Virtual Machine instruction currently being executed

如果该方法不是本地方法,则PC(程序计数器)寄存器包含当前正在执行的java虚拟机指令的地址

1.2 JVM Stack(线程私有)

Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same time as the thread

每个java虚拟机线程都有一个私有java虚拟机栈,与线程同时创建

A Java Virtual Machine stack stores frames

java虚拟机栈里存储帧

image

  1. Frame - 每个方法对应一个栈帧,栈桢结构:
  • 本地变量表(Local Variable Table)

    在编译程序代码的时候就可以确定栈帧中需要多大的局部变量表,具体大小可在编译后的 Class 文件中看到。

    局部变量表的容量以 Variable Slot(变量槽)为最小单位,每个变量槽都可以存储 32 位长度的内存空间。

    在方法执行时,虚拟机使用局部变量表完成参数值到参数变量列表的传递过程的,如果执行的是实例方法,那局部变量表中第 0 位索引的 Slot 默认是用于传递方法所属对象实例的引用(在方法中可以通过关键字 this 来访问到这个隐含的参数)。其余参数则按照参数表顺序排列,占用从 1 开始的局部变量 Slot。

    基本类型数据以及引用和 returnAddress(返回地址)占用一个变量槽,long 和 double 需要两个。

  • 操作数栈(Operand Stack)

    同样也可以在编译期确定大小

    Frame 被创建时,操作栈是空的。操作栈的每个项可以存放 JVM 的各种类型数据,其中 long 和 double 类型(64位数据)占用两个栈深。

    方法执行的过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作

    操作栈调用其它有返回结果的方法时,会把结果 push 到栈上(通过操作数栈来进行参数传递)

  • 动态链接(Dynamic Linking)

    每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接。

    在类加载阶段中的解析阶段会将符号引用转为直接引用,这种转化也称为静态解析。另外的一部分将在运行时转化为直接引用,这部分称为动态链接。

    https://blog.csdn.net/qq_41813060/article/details/88379473

  • 返回地址(Return Address)

    方法开始执行后,只有 2 种方式可以退出 :方法返回指令,异常退出。

    a() -> b(),方法a调用了方法b, b方法的返回值放在什么地方

  1. 栈帧操作详解:
   public class TestIPulsPlus {
       public static void main(String[] args) {
           int i = 8;
           i = i++;
           System.out.println(i); // 输出结果为8,个人最开始觉得是9
       }
   }
  • 对应的本地变量表:

image

  • 对应的指令:

    image

  • 执行过程分析:

    bipush 8 将8压栈

    istore_1 将栈顶数据出栈并把值设置给本地变量表中index为1的变量,就是将i=8

    iload_1 将本地变量表中index为1的变量值压栈

    iinc 1 by 1将本地变量表中index为1的变量值+1

    istore_1 将栈顶数据出栈并把值设置给本地变量表中index为1的变量,这个操作会把上一步+1操作的值又覆盖为8

1.3 Heap(线程共享)

The Java Virtual Machine has a heap that is shared among all Java Virtual Machine threads.

Java虚拟机有一个堆,在所有Java虚拟机线程之间共享。

The heap is the run-time data area from which memory for all class instances and arrays is allocated.

堆是运行时数据区域,所有类实例和数组都从这里分配内存。

1.4 Method Area(线程共享)

The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads.

Java虚拟机有一个在所有Java虚拟机线程之间共享的方法区域。

It stores per-class structures

它存储每个类的结构,比如T.Class信息

Runtime Constant Pool 在Method Area中(A run-time constant pool is a per-class or per-interface run-time representation of the constant_pool table in a class file

每个类或每个接口的运行时常量池是类文件中constant_pool表所表示

JDK < 1.8

方法区的实现叫Perm Space

字符串常量位于Perm Space

FGC不会清理

大小启动的时候指定,不能变

JDK >= 1.8

方法区的实现叫Meta Space

会触发FGC清理

不设定的话,最大就是物理内存

1.5 Native Method Stack (线程私有)

An implementation of the Java Virtual Machine may use conventional stacks called native method stacks

Java虚拟机的实现可以使用称为本机方法堆栈的传统堆栈

1.6 Direct Memory

JVM可以直接访问的内核空间的内存 (OS 管理的内存)

NIO , 提高效率,实现zero copy

2.JVM指令

store 出栈

load 压栈

pop 将栈顶数值弹出 (数值不能是long或double类型的)

mul 将栈顶两数值相乘并将结果压入栈顶

sub 将栈顶两数值相减并将结果压入栈顶

invokeStatic 调用静态方法

invokeVirtual 普通方法调用

invokeInterface List list = new ArrayList<>(); list.add()

inovkeSpecial 可以直接定位,不需要多态的方法private 方法 , 构造方法

invokeDynamic JVM最难的指令,lambda表达式或者反射或者其他动态语言scala kotlin,或者CGLib ASM,动态产生的class,会用到的指令

posted @ 2021-12-25 00:06  ZT丶  阅读(53)  评论(0编辑  收藏  举报