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

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

举例来说。


public void store(int age, String name) {
    int temp = age + 2;
    String str = name;
}



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


image.png


istore_3:从操作数中弹出一个整数,并把它赋值给局部变量表中索引为 3 的变量。

astore 4:从操作数中弹出一个引用数据类型,并把它赋值给局部变量表中索引为 4 的变量。

通过查看局部变量表就能关联上了。


image.png


02、算术指令


算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新压入操作数栈。可以分为两类:整型数据的运算指令和浮点数据的运算指令。


需要注意的是,数据运算可能会导致溢出,比如两个很大的正整数相加,很可能会得到一个负数。但 Java 虚拟机规范中并没有对这种情况给出具体结果,因此程序是不会显式报错的。所以,大家在开发过程中,如果涉及到较大的数据进行加法、乘法运算的时候,一定要注意!


当发生溢出时,将会使用有符号的无穷大 Infinity 来表示;如果某个操作结果没有明确的数学定义的话,将会使用 NaN 值来表示。而且所有使用 NaN 作为操作数的算术操作,结果都会返回 NaN。


举例来说。


public void infinityNaN() {
    int i = 10;
    double j = i / 0.0;
    System.out.println(j); // Infinity
    double d1 = 0.0;
    double d2 = d1 / 0.0;
    System.out.println(d2); // NaN
}



任何一个非零的数除以浮点数 0(注意不是 int 类型),可以想象结果是无穷大 Infinity 的。

把这个非零的数换成 0 的时候,结果又不太好定义,就用 NaN 值来表示。

Java 虚拟机提供了两种运算模式:


向最接近数舍入:在进行浮点数运算时,所有的结果都必须舍入到一个适当的精度,不是特别精确的结果必须舍入为可被表示的最接近的精确值,如果有两种可表示的形式与该值接近,将优先选择最低有效位为零的(类似四舍五入)。

向零舍入:将浮点数转换为整数时,采用该模式,该模式将在目标数值类型中选择一个最接近但是不大于原值的数字作为最精确的舍入结果(类似取整)。

我把所有的算术指令列一下:


加法指令:iadd、ladd、fadd、dadd

减法指令:isub、lsub、fsub、dsub

乘法指令:imul、lmul、fmul、dmul

除法指令:idiv、ldiv、fdiv、ddiv

求余指令:irem、lrem、frem、drem

自增指令:iinc

举例来说。


public void calculate(int age) {
    int add = age + 1;
    int sub = age - 1;
    int mul = age * 2;
    int div = age / 3;
    int rem = age % 4;
    age++;
    age--;
}



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


image.png


iadd,加法

isub,减法

imul,乘法

idiv,除法

irem,取余

iinc,自增的时候 +1,自减的时候 -1

03、类型转换指令


可以分为两种:


1)宽化,小类型向大类型转换,比如 int–>long–>float–>double,对应的指令有:i2l、i2f、i2d、l2f、l2d、f2d。


从 int 到 long,或者从 int 到 double,是不会有精度丢失的;

从 int、long 到 float,或者 long 到 double 时,可能会发生精度丢失;

从 byte、char 和 short 到 int 的宽化类型转换实际上是隐式发生的,这样可以减少字节码指令,毕竟字节码指令只有 256 个,占一个字节。

2)窄化,大类型向小类型转换,比如从 int 类型到 byte、short 或者 char,对应的指令有:i2b、i2s、i2c;从 long 到 int,对应的指令有:l2i;从 float 到 int 或者 long,对应的指令有:f2i、f2l;从 double 到 int、long 或者 float,对应的指令有:d2i、d2l、d2f。


窄化很可能会发生精度丢失,毕竟是不同的数量级;

但 Java 虚拟机并不会因此抛出运行时异常。

举例来说。


public void updown() {
    int i = 10;
    double d = i;
    float f = 10f;
    long ong = (long)f;
}


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


image.png


i2d,int 宽化为 double

f2l, float 窄化为 long


相关文章
|
29天前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
28天前
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
25天前
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
5月前
|
设计模式 Java 程序员
【设计模式】JAVA Design Patterns——Bytecode(字节码模式)
【设计模式】JAVA Design Patterns——Bytecode(字节码模式)
|
1月前
|
安全 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法的深度融合
在Java的演进历程中,Lambda表达式无疑是Java 8引入的一项革命性特性,它极大地简化了函数式编程在Java中的应用,使得代码更加简洁、易于阅读和维护。而这一切的背后,JVM的invokedynamic指令功不可没。本文将深入探讨invokedynamic指令的工作原理及其与Java Lambda语法的紧密联系,带您领略这一技术背后的奥秘。
19 1
|
2月前
|
缓存 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法
【8月更文挑战第27天】在Java的演进历程中,invokedynamic指令的引入和Lambda表达式的出现无疑是两大重要里程碑。它们不仅深刻改变了Java的开发模式和性能表现,还极大地推动了Java在函数式编程和动态语言支持方面的进步。本文将从技术角度浅析JVM中的invokedynamic指令及其与Java Lambda语法的紧密联系。
41 0
|
2月前
|
监控 Java API
分布式链路监控系统问题之对Java应用实现字节码增强的方式的问题如何解决
分布式链路监控系统问题之对Java应用实现字节码增强的方式的问题如何解决
|
3月前
|
存储 Java
JAVA程序运行问题之编译生成的字节码在不同的平台上是否相同如何解决
JAVA程序运行问题之编译生成的字节码在不同的平台上是否相同如何解决
|
4月前
|
Java 大数据 API
【大数据】HDFS、HBase操作教程(含指令和JAVA API)
【大数据】HDFS、HBase操作教程(含指令和JAVA API)
120 0
【大数据】HDFS、HBase操作教程(含指令和JAVA API)
|
3月前
|
存储 运维 Java
Java中的字节码与JVM指令集详解
Java中的字节码与JVM指令集详解
下一篇
无影云桌面