假设我们有一个类:
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/