我要悄悄学习 Java 字节码指令,在成为技术大佬的路上一去不复返(4)

简介: 我要悄悄学习 Java 字节码指令,在成为技术大佬的路上一去不复返

06、操作数栈管理指令


常见的操作数栈管理指令有 pop、dup 和 swap。


将一个或两个元素从栈顶弹出,并且直接废弃,比如 pop,pop2;

复制栈顶的一个或两个数值并将其重新压入栈顶,比如 dup,dup2,dup_×1,dup2_×1,dup_×2,dup2_×2;

将栈最顶端的两个槽中的数值交换位置,比如 swap。

这些指令不需要指明数据类型,因为是按照位置压入和弹出的。


举例来说。


public class Dup {
    int age;
    public int incAndGet() {
        return ++age;
    }
}



通过 jclasslib 看一下 incAndGet() 方法的字节码指令。


image.png


aload_0:将 this 入栈。

dup:复制栈顶的 this。

getfield #2:将常量池中下标为 2 的常量加载到栈上,同时将一个 this 出栈。

iconst_1:将常量 1 入栈。

iadd:将栈顶的两个值相加后出栈,并将结果放回栈上。

dup_x1:复制栈顶的元素,并将其插入 this 下面。

putfield #2: 将栈顶的两个元素出栈,并将其赋值给字段 age。

ireturn:将栈顶的元素出栈返回。


07、控制转移指令


控制转移指令包括:


比较指令,比较栈顶的两个元素的大小,并将比较结果入栈。

条件跳转指令,通常和比较指令一块使用,在条件跳转指令执行前,一般先用比较指令进行栈顶元素的比较,然后进行条件跳转。

比较条件转指令,类似于比较指令和条件跳转指令的结合体,它将比较和跳转两个步骤合二为一。

多条件分支跳转指令,专为 switch-case 语句设计的。

无条件跳转指令,目前主要是 goto 指令。

1)比较指令


比较指令有:dcmpg,dcmpl、fcmpg、fcmpl、lcmp,指令的第一个字母代表的含义分别是 double、float、long。注意,没有 int 类型。


对于 double 和 float 来说,由于 NaN 的存在,有两个版本的比较指令。拿 float 来说,有 fcmpg 和 fcmpl,区别在于,如果遇到 NaN,fcmpg 会将 1 压入栈,fcmpl 会将 -1 压入栈。


举例来说。


public void lcmp(long a, long b) {

   if(a > b){}

}



通过 jclasslib 看一下 lcmp() 方法的字节码指令。


image.png


lcmp 用于两个 long 型的数据进行比较。


2)条件跳转指令


image.png


这些指令都会接收两个字节的操作数,它们的统一含义是,弹出栈顶元素,测试它是否满足某一条件,满足的话,跳转到对应位置。


对于 long、float 和 double 类型的条件分支比较,会先执行比较指令返回一个整形值到操作数栈中后再执行 int 类型的条件跳转指令。


对于 boolean、byte、char、short,以及 int,则直接使用条件跳转指令来完成。


举例来说。


public void fi() {
    int a = 0;
    if (a == 0) {
        a = 10;
    } else {
        a = 20;
    }
}



通过 jclasslib 看一下 fi() 方法的字节码指令。


image.png


3 ifne 12 (+9) 的意思是,如果栈顶的元素不等于 0,跳转到第 12(3+9)行 12 bipush 20。


3)比较条件转指令


image.png


前缀“if_”后,以字符“i”开头的指令针对 int 型整数进行操作,以字符“a”开头的指令表示对象的比较。


举例来说。


public void compare() {
    int i = 10;
    int j = 20;
    System.out.println(i > j);
}


通过 jclasslib 看一下 compare() 方法的字节码指令。


image.png


11 if_icmple 18 (+7) 的意思是,如果栈顶的两个 int 类型的数值比较的话,如果前者小于后者时跳转到第 18 行(11+7)。


4)多条件分支跳转指令


主要有 tableswitch 和 lookupswitch,前者要求多个条件分支值是连续的,它内部只存放起始值和终止值,以及若干个跳转偏移量,通过给定的操作数 index,可以立即定位到跳转偏移量位置,因此效率比较高;后者内部存放着各个离散的 case-offset 对,每次执行都要搜索全部的 case-offset 对,找到匹配的 case 值,并根据对应的 offset 计算跳转地址,因此效率较低。


举例来说。


public void switchTest(int select) {
    int num;
    switch (select) {
        case 1:
            num = 10;
            break;
        case 2:
        case 3:
            num = 30;
            break;
        default:
            num = 40;
    }
}



通过 jclasslib 看一下 switchTest() 方法的字节码指令。


image.png


case 2 的时候没有 break,所以 case 2 和 case 3 是连续的,用的是 tableswitch。如果等于 1,跳转到 28 行;如果等于 2 和 3,跳转到 34 行,如果是 default,跳转到 40 行。


5)无条件跳转指令


goto 指令接收两个字节的操作数,共同组成一个带符号的整数,用于指定指令的偏移量,指令执行的目的就是跳转到偏移量给定的位置处。


前面的例子里都出现了 goto 的身影,也很好理解。如果指令的偏移量特别大,超出了两个字节的范围,可以使用指令 goto_w,接收 4 个字节的操作数。


巨人的肩膀:


https://segmentfault.com/a/1190000037628881

除了以上这些指令,还有异常处理指令和同步控制指令,我打算吊一吊大家的胃口,大家可以期待一波~~


(骚操作)


路漫漫其修远兮,吾将上下而求索

想要走得更远,Java 字节码这块就必须得硬碰硬地吃透,希望二哥的这些分享可以帮助到大家~


叨逼叨


二哥在 CSDN 上写了很多 Java 方面的系列文章,有 Java 核心语法、Java 集合框架、Java IO、Java 并发编程、Java 虚拟机等,也算是体系完整了。


相关文章
|
10天前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
60 6
|
1月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
31 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
23天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
23天前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
|
1月前
|
Java
如何从Java字节码角度分析问题|8月更文挑战
如何从Java字节码角度分析问题|8月更文挑战
|
2月前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
2月前
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
2月前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
3月前
|
Java
Java常见JVM虚拟机指令(47个)
Java常见JVM虚拟机指令(47个)
69 3
Java常见JVM虚拟机指令(47个)
|
2月前
|
安全 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法的深度融合
在Java的演进历程中,Lambda表达式无疑是Java 8引入的一项革命性特性,它极大地简化了函数式编程在Java中的应用,使得代码更加简洁、易于阅读和维护。而这一切的背后,JVM的invokedynamic指令功不可没。本文将深入探讨invokedynamic指令的工作原理及其与Java Lambda语法的紧密联系,带您领略这一技术背后的奥秘。
30 1