Java 字节码 : local variables table vs on-stack calculation

标签 java jvm jvm-bytecode

假设我们有一个类:

final class Impl implements Gateway3 {
    private final Sensor sensor1;
    private final Sensor sensor2;
    private final Sensor sensor3;

    private final Alarm alarm;

    public Impl(Sensor sensor1, Sensor sensor2, Sensor sensor3, Alarm alarm) {
        this.sensor1 = sensor1;
        this.sensor2 = sensor2;
        this.sensor3 = sensor3;
        this.alarm = alarm;
    }

    @Override
    public Temperature averageTemp() {
        final Temperature temp1 = sensor1.temperature();
        final Temperature temp2 = sensor2.temperature();
        final Temperature temp3 = sensor3.temperature();

        final Average tempAvg = new Average.Impl(temp1, temp2, temp3);
        final Temperature result = tempAvg.result();
        return result;
    }

    @Override
    public void poll() {
        final Temperature avgTemp = this.averageTemp();
        this.alarm.trigger(avgTemp);
    }

这个类广泛使用局部变量,而且都是final。

如果我们查看为 averageTemp 方法生成的字节码,我们将看到以下字节码:

   0: aload_0
   1: getfield      #2                  // Field sensor1:Lru/mera/avral/script/bytecode/demo/Sensor;
   4: invokeinterface #6,  1            // InterfaceMethod ru/mera/avral/script/bytecode/demo/Sensor.temperature:()Lru/mera/avral/script/bytecode/demo/Temperature;
   9: astore_1
  10: aload_0
  11: getfield      #3                  // Field sensor2:Lru/mera/avral/script/bytecode/demo/Sensor;
  14: invokeinterface #6,  1            // InterfaceMethod ru/mera/avral/script/bytecode/demo/Sensor.temperature:()Lru/mera/avral/script/bytecode/demo/Temperature;
  19: astore_2
  20: aload_0
  21: getfield      #4                  // Field sensor3:Lru/mera/avral/script/bytecode/demo/Sensor;
  24: invokeinterface #6,  1            // InterfaceMethod ru/mera/avral/script/bytecode/demo/Sensor.temperature:()Lru/mera/avral/script/bytecode/demo/Temperature;
  29: astore_3
  30: new           #7                  // class ru/mera/avral/script/bytecode/demo/Average$Impl
  33: dup
  34: aload_1
  35: aload_2
  36: aload_3
  37: invokespecial #8                  // Method ru/mera/avral/script/bytecode/demo/Average$Impl."<init>":(Lru/mera/avral/script/bytecode/demo/Temperature;Lru/mera/avral/script/bytecode/demo/Temperature;Lru/mera/avral/script/bytecode/demo/Temperature;)V
  40: astore        4
  42: aload         4
  44: invokeinterface #9,  1            // InterfaceMethod ru/mera/avral/script/bytecode/demo/Average.result:()Lru/mera/avral/script/bytecode/demo/Temperature;
  49: astore        5
  51: aload         5
  53: areturn

有很多 astore 操作码。

现在,假设使用字节码生成库,我为相同的方法生成了以下字节码:

   0: new           #18                 // class ru/mera/avral/script/bytecode/demo/Average$Impl
   3: dup
   4: aload_0
   5: getfield      #20                 // Field sensor1:Lru/mera/avral/script/bytecode/demo/Sensor;
   8: invokeinterface #25,  1           // InterfaceMethod ru/mera/avral/script/bytecode/demo/Sensor.temperature:()Lru/mera/avral/script/bytecode/demo/Temperature;
  13: aload_0
  14: getfield      #27                 // Field sensor2:Lru/mera/avral/script/bytecode/demo/Sensor;
  17: invokeinterface #25,  1           // InterfaceMethod ru/mera/avral/script/bytecode/demo/Sensor.temperature:()Lru/mera/avral/script/bytecode/demo/Temperature;
  22: aload_0
  23: getfield      #29                 // Field sensor3:Lru/mera/avral/script/bytecode/demo/Sensor;
  26: invokeinterface #25,  1           // InterfaceMethod ru/mera/avral/script/bytecode/demo/Sensor.temperature:()Lru/mera/avral/script/bytecode/demo/Temperature;
  31: invokespecial #33                 // Method ru/mera/avral/script/bytecode/demo/Average$Impl."<init>":(Lru/mera/avral/script/bytecode/demo/Temperature;Lru/mera/avral/script/bytecode/demo/Temperature;Lru/mera/avral/script/bytecode/demo/Temperature;)V
  34: invokevirtual #36                 // Method ru/mera/avral/script/bytecode/demo/Average$Impl.result:()Lru/mera/avral/script/bytecode/demo/Temperature;
  37: areturn

从语义上讲,与旧方法相比,这个新方法实现具有相同的含义 - 它仍然从三个传感器获取温度值,从中取平均值并返回它。但是它不是将中间值赋给变量,而是在堆栈上进行所有计算。我可以那样重写它,因为我所有的局部变量和字段都是最终的。

现在有一个问题:如果我正在做一些与字节码生成相关的魔术并在任何地方都遵循这种“堆栈上的所有计算”方法(假设我所有的变量和字段都是最终的),我可能会面临哪些潜在的陷阱?

注意:我无意按照我描述的方式为现有 Java 类重写字节码。这里给出示例类只是为了展示我想在我的字节码中实现的方法语义。

最佳答案

最大的陷阱:您可能会不小心阻止 JIT 执行其工作。

从而实现与您的目标完全相反的结果:降低运行时性能。

JIT(在某种程度上)是为众所周知的、经常使用的编码模式创建最佳结果而编写的。如果你让它的工作变得更难,它很可能会做得不太理想。

重点是:与其他语言相比,java 编译器没有做很多优化步骤。真正的魔法发生在以后……当 JIT 启动时。因此:您必须非常详细地研究 JIT 正在做什么,以了解如何创建更好的字节码,这些字节码也可以在以后很好地“JITed”。

关于Java 字节码 : local variables table vs on-stack calculation,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43241734/

相关文章:

java - 长度超过 65535 字节的 Java 字符串文字的字节码

java - 如何设置Tomcat的编码类型?

java - 在 Eclipse 中导出可运行的 JAR,仅包含导入的类

java - 在 Linux 中运行时如何在 Java 中创建符号链接(symbolic link)?

python - 减少由于 azure databricks 中 GC 分配失败而导致的时间延迟的步骤

java - ASM : visitLabel generates too many labels and nop instructions

Java字节码——每条语句一个标签

java - 如何在 maven 中使用单个 groupId 和多个 artifactId?

java - JUnit 异常测试

java - 当文件确实存在时抛出 FileNotFoundException