运行时数据区和指令

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 @   ZT丶  阅读(61)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
点击右上角即可分享
微信分享提示
主题色彩