ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

浅谈 JVM 3:指令集及其执行

2022-01-22 23:31:11  阅读:224  来源: 互联网

标签:操作数 Java 浅谈 局部变量 指令 JVM 方法 指令集


上文我们聊了如何参考 JVM 规范 解读 Java 字节码。但对于字节码方法字段 Code 中的 JVM 指令 执行过程我们留下了疑问,这次我们来谈谈。

运行时区域

我们先来看一下更加详细一些的 JVM 逻辑区域划分。

可以看出,在 JVM 中主要划分为 5 块区域:

  • 方法区
  • Java 堆
  • 程序计数器
  • Java 栈
  • 本地方法栈

在执行字节码前,虚拟机需要先将 .class 文件加载进来。相关类的信息会存放在 方法区 中。Code 字段的信息就会放在此区域,后续配合 Java 方法栈 执行。

当我们通过 new Ojbect() 或反射等方式创建对象时,JVM 会在 Java 堆 中为对象分配内存。

方法区Java 方法栈 为线程公有内存区域,所以当我们多线程访问同一个对象时,需要进行加锁操作,来处理线程并发处理数据所引发的问题。锁操作的实现,也和指令集相关。

其他三个均为线程私有区域。

程序计数器 保存当前线程中,当前执行的字节码指令位置。

Java 方法栈 型数据结构,具有后进先出的特性。其组成元素为 栈帧。每当执行一个方法时,会根据该方法对应的 Code 属性生成对应栈帧,压入到栈中。方法执行完毕时,栈帧出栈。当前正在执行的方法处在栈顶的位置。本地方法栈Java 方法栈 类似,不过它处理的是 C++ 编写的 native 方法。

每个 栈帧 包括局部变量表、操作数栈、动态连接、返回值等内容。动态连接为指向运行时常量池中该栈帧所属方法的引用,目的是为支持方法调用的动态连接。动态连接是 Java 支持动态特性的关键所在,此部分我们之后专门花篇幅介绍,此处先浅尝辄止。返回值则代表当前方法计算结果,返回给上一级调用方法。

局部变量表 用于存放方法参数和内部定义的局部变量。在字节码中方法的 Code 属性定义了 max_locals 属性表示局部变量表的最大容量。局部变量表以 变量槽 为单位存储数据。其实际占用内存空间大小虚拟机规范并未规定,可由虚拟机各自实现。只要求一个变量槽可存储下 boolean、byte、char、short、int、float 等类型数据。所以对于 double、long 类型数据会占用两个变量槽的空间。boolean 在此被使用 0、1 存储。由此可以看出,JVM 中的类型和 Java 代码中的类型并不一致。

操作数栈 也是一个后进先出栈,其最大深度同样在 Code 中有所体现。这块区域供字节码指令调用时使用,保存指令调用所需参数和结果。

运行时栈帧结构和指令集

对于 JVM 指令集,可以参考官方文档

在此,我们结合一段简单代码,了解一下常见指令集。

Java 代码如下:

private int getThisIsInt() {
    int a = 2;
    return mThisIsInt * a;
}

编译后字节码如下:

0 iconst_2
1 istore_1
2 aload_0
3 getfield #7 <Demo.mThisIsInt : I>
6 iload_1
7 imul
8 ireturn
  • iconst_2 将 int 类型常量 2 压栈到操作数栈中
  • istore_1 将操作数栈的 int 值出栈,放入局部变量表槽位 1 的地方
  • aload_0 加载局部变量表槽位 0 处的引用到操作数栈中,此处为当前对象的引用 objectref
  • getfield #7 获取当前类的运行时常量池 #7 变量,此指令执行完后,objectref 出栈,获取到的值入栈
  • iload_1 从局部变量表槽位 1 处加载值到操作数栈中
  • imul 将操作数栈中靠近栈顶的两个值出栈,做乘法,将结果压栈
  • ireturn 将当前方法操作数栈中的值出栈,并压栈到调用者方法的操作数栈中

可对照下图梳理指令执行时,局部变量表和操作数栈的变化情况。

JVM 指令集较多,限于篇幅无法一一介绍。但其遵循一定规律,如下。

  • Tload_X:将局部变量表数据加载到操作数栈中,T 为类型,如 iload 为加载 int 类型,fload 加载 float 类型。X 为局部变量表槽位索引
  • Tstore_X:将操作数栈数据出栈,存入局部变量表。T、X 和上文含义一致
  • Tconst_X:将 T 类型常量 X 压入操作数栈
  • Tmul:将操作数栈的两个 T 类型常量相乘,结果放入操作数栈。类似的有除法 Tdiv、加法 Tadd 等
  • i2b:int 类型转换为 byte 数据,类似指令有 i2c、f2i 等
  • new:对象创建指令
  • ifeq:分支控制指令,类似指令有 iflt、ifnull、goto 等
  • invokevirtual:方法调用指令,类似指令有 invokespecial、invokestatic 等
  • monitorenter:同步指令,有加锁操作时会添加此指令,对应 monitorexit 代表退出同步

更加详细的指令介绍,请参考 官方文档

总结

本次我们了解了 JVM 运行时区域划分,指令集运行时所依赖的局部变量表和操作数栈等结构,和指令集的类型和操作影响。

指令集中包含了锁操作对应的 monitorentermonitorexit 指令,和方法调用对应的 invokevirtual 等指令。这些指令和 JVM 实现线程安全和方法的动态连接密切关联。线程安全在我们日常开发中至关重要。方法的继承和重写又是面向对象语言的重要特征。所以 在 JVM 层面,这些都是如何实现的呢?我们下次有时间再聊。

标签:操作数,Java,浅谈,局部变量,指令,JVM,方法,指令集
来源: https://blog.csdn.net/u013647453/article/details/122645753

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有