如何在javac编译Java代码时禁用constant folding(常量折叠)?
如何禁用javac的常量折叠以保留分步相加的字节码
核心结论
javac本身没有官方编译选项可以直接禁用常量折叠——这是Java语言规范(JLS)要求的编译期行为,对于编译期可确定的常量表达式,javac必须在编译阶段完成求值。
绕过方案
要得到你想要的分步相加字节码,需要将代码中的字面量转换为非编译期常量,让javac无法在编译阶段直接计算出最终结果,具体有两种常用方式:
方式1:使用局部变量存储字面量
修改代码为:
public class App{ public int sum(){ int a = 1; int b = 2; int c = 3; int d = 4; return (a + b) + (c + d); } }
编译后用javap -v App查看sum方法的字节码,会得到包含分步相加的指令:
0: iconst_1 1: istore_1 2: iconst_2 3: istore_2 4: iconst_3 5: istore_3 6: iconst_4 7: istore 4 9: iload_1 10: iload_2 11: iadd 12: iload_3 13: iload 4 15: iadd 16: iadd 17: ireturn
注:这里多了istore和iload指令用于变量的存储与加载,但核心的三次iadd操作完全保留。
方式2:使用方法返回字面量
如果希望避免局部变量的存储指令,可以通过方法调用返回字面量,让javac无法在编译期确定结果:
public class App{ private static int getOne() { return 1; } private static int getTwo() { return 2; } private static int getThree() { return 3; } private static int getFour() { return 4; } public int sum(){ return (getOne() + getTwo()) + (getThree() + getFour()); } }
对应的字节码会包含方法调用指令和后续的分步相加操作。
补充说明
常量折叠是javac遵循JLS规范的必然行为,JLS明确要求编译期对所有常量表达式进行求值并替换为最终常量值,因此官方没有提供禁用该优化的开关。上述方案本质是通过破坏"编译期常量表达式"的条件,让javac只能生成分步执行的字节码。
内容的提问来源于stack exchange,提问作者Volodya Lombrozo
相关产品推荐
相关产品推荐

