6

我正在寻找一种方法来为条件提供最快的(我的意思是零时间- 编译/类加载/JIT时间解析)可能的开/关标志if当然,每次应用程序运行时,此条件只会更改一次 - 在启动时。

我知道可以有条件地编译“条件编译时常量”,并且可以从代码中删除整个条件。但是,无需重新编译源代码的最快(并且可能简单)的替代方案是什么?

我可以将条件与.jar带有条件的单个类和方法分开,我会在其中生成两个版本,.jar并在应用程序启动时在类路径中切换这些版本?如果发现该方法为空, JIT 会单独删除对方法的调用吗?.jar

我可以通过在实现“ClassWithMyCondition”的类路径中提供两个类来做到这一点,其中一个类将具有真正的实现,而第二个类将只有空方法并通过 and 实例化其中一个Class.forName.newInstance()JIT 会从我的主要中删除对空方法的调用吗?非常循环嵌套的方法?

这个问题的最简单的字节码操作解决方案是什么?

4

5 回答 5

11

执行此类逻辑的标准方法是为您想要的功能创建一个接口,然后为该功能创建两个(或更多)实现。只有一个实现将在您的运行时加载,并且该实现可以做出它需要的假设以if完全避免这种情况。

这样做的好处是每个实现都是互斥的,并且像 JIT 编译器这样的东西可以忽略这个特定运行的所有无用代码。

于 2013-10-24T19:19:23.927 回答
7

最简单的解决方案在这里有效。不要为自己把事情复杂化。

只需将一个不是final static boolean编译时常量(如 JLS 中定义)放在某处,并在您想要“条件”编译的任何地方引用它。JVM 将在第一次看到它时对其进行评估,并且当代码被 JIT 化时,JVM 将知道该值不会更改,然后可以删除检查,如果值为 ,则删除块。false

一些资料来源:Oracle 有一个关于性能技术的 wiki 页面,它说尽可能使用常量(请注意,在这种情况下,编译器是 JVM/JIT,因此final即使不是编译器,字段也算作常量 - JLS 标准的时间常数)。该页面链接到JIT 采用的性能策略索引,其中提到了诸如常量折叠和流敏感重写等技术,包括死代码删除。

于 2013-10-24T19:20:37.743 回答
3

您可以在命令行中传递自定义值,然后检查该值一次。所以在你的代码中,有这样的东西:

final static boolean customProp = "true".equalsIgnoreCase(System.getProperty("customProp"));

根据您的命令行参数,该static final值会发生变化。这会将值设置为true

java -DcustomProp="true" -jar app.jar

虽然这会将值设置为false

java -jar app.jar

这为您提供了 a 的好处static final boolean,但允许在不重新编译的情况下更改值。


[编辑]

正如评论中所指出的,这种方法不允许在编译时进行优化。的值static final boolean是在类加载时设置的,并且从那里保持不变。字节码的“正常”执行可能需要评估每个if (customProp). 但是,JIT 发生在运行时,将字节码编译为本机代码。此时,由于字节码具有运行时值,因此可以进行更积极的优化,例如内联或排除代码。但请注意,您无法准确预测 JIT 是否或何时启动。

于 2013-10-24T19:30:02.140 回答
1

您应该从属性文件中加载值,这样您就可以避免每次更改时都必须重新编译。只需更新文本文件,并在下次程序运行时,它使用新值。这是我很久以前写的一个例子:

https://github.com/SnakeDoc/JUtils/blob/master/src/net/snakedoc/jutils/Config.java

于 2013-10-24T19:32:37.260 回答
0

每次运行时,JIT 都会重新编译代码。不管你是否知道,你已经在这样做了。这意味着如果您有一个 JIT 认为没有更改的字段(它甚至不必是最终的),它将被内联并且检查和代码优化掉。

随着时间的推移,尝试智能 JIT 变得越来越难。

于 2013-10-24T21:13:35.830 回答