《深入理解Java虚拟机》 JAVA 字节码指令 基础

news/2024/10/18 5:47:48/

1.操作数栈

解释时,JVM会为方法分配一个栈帧,而栈帧又由 局部变量表,操作数帧,方法引用,动态链接 组成

方法中的每条指令执行时,要求该指令的操作数已经压入栈中;执行指令时会将操作数从栈中弹出,是否将操作数再次压入栈中取决与具体的命令。

new,dup指令

使用new关键字创建对象的时候出现的字节码指令,通常伴随着 dup 指令 ,dup指令将复制一份操作数栈顶的值

这里是因为 invokespecial 调用类的构造方法时,将会消耗new的结果引用,如果我们不复制一份 ,那么这个引用就丢掉了。

public class ByteCodeDemo {public static void main(String[] args) {Object b = new Object();}
}
0 new #2 <java/lang/Object>
3 dup
4 invokespecial #1 <java/lang/Object.<init> : ()V>
7 astore_1
8 return

pop 指令

    public static void main(String[] args) throws InterruptedException {ByteCodeDemo byteCodeDemo = new ByteCodeDemo();byteCodeDemo.methodOne();}public  String methodOne(){return "no";}
  public static void main(java.lang.String[]) throws java.lang.InterruptedException;Code:0: new           #2                  // class com/sz/jasyptdemo/ByteCodeDemo3: dup4: invokespecial #3                  // Method "<init>":()V7: astore_18: aload_19: invokevirtual #4                  // Method methodOne:()Ljava/lang/String;12: pop13: return

调用了methodOne()方法,这个方法是有返回结果的,但是我们并没有接受 因此JVM会调用 pop指令,将返回值抛弃掉

iconst,bipush,sipush,ldc

iconst 表示加载一个常量,常量的值范围在 -1 ~5 之间,bipush 加载一个字节所能表示的int值,sipush加载两个字节所能表示的int值,ldc 则能加载任意值

    public static void main(String[] args) throws InterruptedException {int a = 5;int b = 6;int c = 129;int d = 32768;}
         0: iconst_51: istore_12: bipush        64: istore_25: sipush        1298: istore_39: ldc           #2                  // int 3276811: istore        413: return

img

Throwable

如果抛出异常,会将操作数栈清空,然后将异常实例压入操作数栈

2. 局部变量表 (数组)

加载与存储 load,store

Java 方法栈桢的另外一个重要组成部分则是局部变量区,字节码程序可以将计算的结果缓存在局部变量区之中。

Java 虚拟机将局部变量区当成一个数组,如果是实例方法,那么局部变量表这个数组的0号下标位置就是就是this指针,1号下标位置就是 参数,后面依次存放局部变量。

    public static void main(String[] args) throws InterruptedException {ByteCodeDemo byteCodeDemo = new ByteCodeDemo();byteCodeDemo.method(3);}public void method(int i){int a = 5;int b = 6;int c = 129;int d = 32768;}

对应的局部变量表

image-20230518203540998

因为调用的是实例方法,所以本地变量表序号0的位置上是 this指针,1号上是 方法参数 i,后面依次是方法从上往下的局部变量。

JVM对局部变量的主要有两组命令 加载 命令 load, 存储命令 sotre

   public static void main(String[] args) throws InterruptedException {ByteCodeDemo byteCodeDemo = new ByteCodeDemo();byteCodeDemo.method(3);}public void method(int i){int a = 5;int b = 6;int c = 129;int d = 32768;if (d<300){System.out.println("...");}}
 0 iconst_51 istore_22 bipush 64 istore_35 sipush 1298 istore 4
10 ldc #5 <32768>
12 istore 5
14 iload 5
16 sipush 300
19 if_icmpge 30 (+11)
22 getstatic #6 <java/lang/System.out : Ljava/io/PrintStream;>
25 ldc #7 <...>
27 invokevirtual #8 <java/io/PrintStream.println : (Ljava/lang/String;)V>
30 return

如上的字节码指令所示,首先是加载常量5到栈顶,然后调用 istore 将栈顶元素存储到局部变量表下标为2的位置上;然后调用bispush,将6压到栈顶… 然后调用iload指令 加载局部变量表下标为5的内容,与栈顶元素 300 比较,如果满足条件,往下执行;如果不满足,跳到偏移量为30的地方执行return返回;

不同基本数据的类型,有不同的load和store命令,如下图所示

img

通常对局部变量的操作是首先加载值然后压到操作数栈中进行计算,如下所示

    public void method(int i){int a = 5;int b = a+10;int c = 129;int d = 32768;if (d<300){System.out.println("...");}}
 0 iconst_51 istore_22 iload_23 bipush 105 iadd6 istore_37 sipush 129
10 istore 4
12 ldc #5 <32768>
14 istore 5
16 iload 5
18 sipush 300
21 if_icmpge 32 (+11)
24 getstatic #6 <java/lang/System.out : Ljava/io/PrintStream;>
27 ldc #7 <...>
29 invokevirtual #8 <java/io/PrintStream.println : (Ljava/lang/String;)V>
32 return

如第2,3,5行指令所示;

但是也有指令能直接对局部变量表上的数值进行运算: 自增,自减操作

直接操作局部变量表的指令 iinc

    public void method(int i){int a = 1;int b = 10;a++;b--;}
 0 iconst_11 istore_22 bipush 104 istore_35 iinc 2 by 18 iinc 3 by -1
11 return

如上字节码所示 iinc + 局部变量表的下标 + by + 增加的值

3. 其他指令

instanceof

后跟目标类,判断栈顶元素是否为目标类 / 接口的实例。是则压入 1,否则压入 0

    public void method(int i){Date date = new Date();if (date instanceof Object) {System.out.println("ofcause");}}
 0 new #5 <java/util/Date>3 dup4 invokespecial #6 <java/util/Date.<init> : ()V>7 astore_28 aload_29 instanceof #7 <java/lang/Object>
12 ifeq 23 (+11)
15 getstatic #8 <java/lang/System.out : Ljava/io/PrintStream;>
18 ldc #9 <ofcause>
20 invokevirtual #10 <java/io/PrintStream.println : (Ljava/lang/String;)V>
23 return

getstatic

访问静态字段,见上

monitorente monitorexit

为栈顶元素加锁和解锁

    public void method(int i){synchronized (new Object()){System.out.println("sync");}}
 0 new #5 <java/lang/Object>3 dup4 invokespecial #1 <java/lang/Object.<init> : ()V>7 dup8 astore_29 monitorenter
10 getstatic #6 <java/lang/System.out : Ljava/io/PrintStream;>
13 ldc #7 <sync>
15 invokevirtual #8 <java/io/PrintStream.println : (Ljava/lang/String;)V>
18 aload_2
19 monitorexit
20 goto 28 (+8)
23 astore_3
24 aload_2
25 monitorexit
26 aload_3
27 athrow
28 return

http://www.ppmy.cn/news/86579.html

相关文章

【算法】Letter Tile Possibilities 活字印刷

文章目录 Letter Tile Possibilities 活字印刷问题描述&#xff1a;分析代码回溯动态规划 Letter Tile Possibilities 活字印刷 问题描述&#xff1a; 你有一套活字字模 tiles&#xff0c;其中每个字模上都刻有一个字母 tiles[i]。返回你可以印出的非空字母序列的数目。 每个…

python爬取网页代码-python爬虫爬取网页所有数据详细教程

Python爬虫可通过查找一个或多个域的所有 URL 从 Web 收集数据。Python 有几个流行的网络爬虫库和框架。大家熟知的就是python爬取网页数据&#xff0c;对于没有编程技术的普通人来说&#xff0c;怎么才能快速的爬取网站数据呢&#xff1f;今天给大家分享的这款免费爬虫软件让您…

java面试题(MyBatis)

目录 1.什么是MyBatis&#xff1f; 2.MyBatis存在哪些优缺点&#xff1f; 3.MyBatis中#{}和${}的区别 4.MyBatis 有哪几种 SQL 编写形式 5.MyBatis 怎么实现分页 6.MyBatis 如何防止 SQL 注入 7.MyBatis 延迟加载 8.MyBatis 中的缓存机制有啥用&#xff1f; 9.MyBatis…

【数据结构】--单链表力扣面试题①移除链表元素

题述&#xff1a; 给你一个链表的头结点head和一个整数val,请你删除链表中所有满足Node.val val的节点&#xff0c;并返回新的头结点。 思考&#xff1a; 为什么说要返回新的头结点&#xff0c;因为你删除的可能存在把原来的头结点删除的情况&#xff0c;这时就需要有新的头结…

Spring-IOC是什么

Spring-IOC是什么 Spring-IOC是什么IOC是什么 Spring-IOC是什么 Spring-IOC是Spring框架的核心&#xff0c;是一个容器&#xff0c;它负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。 IOC是什么 控制反转&#xff0c;指的是将对象的控制权交给Spring容器&a…

自动构建之Makefile

链接: 自动构建之CMake Makefile Makefile是用于自动化构建软件项目的工具&#xff0c;Makefile的优点是简单、直接&#xff0c;可以直接使用make工具进行构建。但是&#xff0c;Makefile通常需要手动编写和维护&#xff0c;可能会导致跨平台和跨编译器的兼容性问题。 Makef…

adb 命令速查(下)

ADB 关于APP安装、调试和monkey压力测试 作者&#xff1a;炭烤毛蛋 &#xff0c;查看博主了解更多。 提示&#xff1a;承接上篇《adb 命令速查(中)》&#xff0c;本文将 文章目录 ADB 关于APP安装、调试和monkey压力测试7 adb 关于 apk 的相关操作7.1 安装 apk普通安装带有命…

虹科HiveMQ与MQTT:构建互联汽车的新架构

前言 随着汽车的互联程度越来越高&#xff0c;汽车制造商和互联汽车平台提供商通过使用物联网技术&#xff0c;提供新服务并从车辆收集有价值的遥测数据&#xff0c;以此来增加营收。从高效的车队管理和汽车共享到预测性维护和高级驾驶员辅助系统&#xff0c;未来移动出行的可…