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

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

04、对象的创建和访问指令


Java 是一门面向对象的编程语言,那么 Java 虚拟机是如何从字节码层面进行支持的呢?


1)创建指令


数组也是一种对象,但它创建的字节码指令和普通的对象不同。创建数组的指令有三种:


newarray:创建基本数据类型的数组

anewarray:创建引用类型的数组

multianewarray:创建多维数组

普通对象的创建指令只有一个,就是 new,它会接收一个操作数,指向常量池中的一个索引,表示要创建的类型。


举例来说。


public void newObject() {
    String name = new String("沉默王二");
    File file = new File("无愁河的浪荡汉子.book");
    int [] ages = {};
}


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


image.png


new #13 <java/lang/String>,创建一个 String 对象。

new #15 <java/io/File>,创建一个 File 对象。

newarray 10 (int),创建一个 int 类型的数组。

2)字段访问指令


字段可以分为两类,一类是成员变量,一类是静态变量(static 关键字修饰的),所以字段访问指令可以分为两类:


访问静态变量:getstatic、putstatic。

访问成员变量:getfield、putfield,需要创建对象后才能访问。

举例来说。


public class Writer {
    private String name;
    static String mark = "作者";
    public static void main(String[] args) {
        print(mark);
        Writer w = new Writer();
        print(w.name);
    }
    public static void print(String arg) {
        System.out.println(arg);
    }
}



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


image.png


getstatic #2 <com/itwanger/jvm/Writer.mark>,访问静态变量 mark

getfield #6 <com/itwanger/jvm/Writer.name>,访问成员变量 name


05、方法调用和返回指令


方法调用指令有 5 个,分别用于不同的场景:


invokevirtual:用于调用对象的成员方法,根据对象的实际类型进行分派,支持多态。

invokeinterface:用于调用接口方法,会在运行时搜索由特定对象实现的接口方法进行调用。

invokespecial:用于调用一些需要特殊处理的方法,包括构造方法、私有方法和父类方法。

invokestatic:用于调用静态方法。

invokedynamic:用于在运行时动态解析出调用点限定符所引用的方法,并执行。

举例来说。


public class InvokeExamples {
    private void run() {
        List ls = new ArrayList();
        ls.add("难顶");
        ArrayList als = new ArrayList();
        als.add("学不动了");
    }
    public static void print() {
        System.out.println("invokestatic");
    }
    public static void main(String[] args) {
        print();
        InvokeExamples invoke = new InvokeExamples();
        invoke.run();
    }
}



我们用 javap -c InvokeExamples.class 来反编译一下。


Compiled from "InvokeExamples.java"
public class com.itwanger.jvm.InvokeExamples {
  public com.itwanger.jvm.InvokeExamples();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
  private void run();
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #4                  // String 难顶
      11: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      16: pop
      17: new           #2                  // class java/util/ArrayList
      20: dup
      21: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
      24: astore_2
      25: aload_2
      26: ldc           #6                  // String 学不动了
      28: invokevirtual #7                  // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
      31: pop
      32: return
  public static void print();
    Code:
       0: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #9                  // String invokestatic
       5: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #11                 // Method print:()V
       3: new           #12                 // class com/itwanger/jvm/InvokeExamples
       6: dup
       7: invokespecial #13                 // Method "<init>":()V
      10: astore_1
      11: aload_1
      12: invokevirtual #14                 // Method run:()V
      15: return
}



InvokeExamples 类有 4 个方法,包括缺省的构造方法在内。


1)InvokeExamples() 构造方法中


缺省的构造方法内部会调用超类 Object 的初始化构造方法:


`invokespecial #1 // Method java/lang/Object."<init>":()V`


2)成员方法 run() 中


invokeinterface #5,  2  // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z


由于 ls 变量的引用类型为接口 List,所以 ls.add() 调用的是 invokeinterface 指令,等运行时再确定是不是接口 List 的实现对象 ArrayList 的 add() 方法。


invokevirtual #7 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z


由于 als 变量的引用类型已经确定为 ArrayList,所以 als.add() 方法调用的是 invokevirtual 指令。


3)main() 方法中


invokestatic  #11 // Method print:()V


print() 方法是静态的,所以调用的是 invokestatic 指令。


方法返回指令根据方法的返回值类型进行区分,常见的返回指令见下图。


image.png



相关文章
|
3月前
|
自然语言处理 前端开发 算法
Java编译器优化秘籍:字节码背后的IR魔法与常见技巧
编译器将源代码转换为机器码的过程中,会经历多个中间表达形式(IR)的转换与优化。前端生成高级IR(HIR),后端将其转为低级IR(LIR)并进行机器相关优化。Java编译流程包括源码到字节码、再由即时编译器转换为内部HIR(如SSA图)、优化后生成LIR,最终编译为机器码。常见优化技术包括常量折叠、值编号、死代码消除、公共子表达式消除等,旨在提升程序性能与执行效率。
147 0
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
631 6
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
206 5
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
424 5
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
252 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
如何从Java字节码角度分析问题|8月更文挑战
如何从Java字节码角度分析问题|8月更文挑战
|
Java API 开发者
【Java字节码的掌控者】JDK 22类文件API:解锁Java深层次的奥秘,赋能开发者无限可能!
【9月更文挑战第8天】JDK 22类文件API的引入,为Java开发者们打开了一扇通往Java字节码操控新世界的大门。通过这个API,我们可以更加深入地理解Java程序的底层行为,实现更加高效、可靠和创新的Java应用。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来,并积极探索类文件API带来的无限可能!
|
Java API 开发者
【Java字节码操控新篇章】JDK 22类文件API预览:解锁Java底层的无限可能!
【9月更文挑战第6天】JDK 22的类文件API为Java开发者们打开了一扇通往Java底层世界的大门。通过这个API,我们可以更加深入地理解Java程序的工作原理,实现更加灵活和强大的功能。虽然目前它还处于预览版阶段,但我们已经可以预见其在未来Java开发中的重要地位。让我们共同期待Java字节码操控新篇章的到来!
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
缓存 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法
【8月更文挑战第27天】在Java的演进历程中,invokedynamic指令的引入和Lambda表达式的出现无疑是两大重要里程碑。它们不仅深刻改变了Java的开发模式和性能表现,还极大地推动了Java在函数式编程和动态语言支持方面的进步。本文将从技术角度浅析JVM中的invokedynamic指令及其与Java Lambda语法的紧密联系。
214 0
下一篇
oss云网关配置