Java编译器为什么不会优化掉静态常量字符串?

3

我在我的应用程序中有以下代码:

  public static final boolean DEBUG = true;
  public static final boolean REL = !DEBUG;

  private static final String DEBUG_OR_RELEASE = (REL) ? "RELEASE_VER" : "DEBUG_VER";

我原以为Java编译器会完全从通过Proguard导出的.apk文件中消除"DEBUG_VER"字符串,但是当我检查.apk文件时,我发现其中有"DEBUG_VER"字符串。

为什么?我错过了什么?我做错了什么?


如果你认为不应该看到“RELEASE_VER”,那么你可能是在进行调试编译,通常这种优化在调试模式下是被禁用的。 - Hot Licks
此外,即使该语句被优化掉,字符串字面量很可能仍然存在于常量池中。这基本上是无害的。 - Hot Licks
DEBUG_OR_RELEASE = (REL) ? "DEBUG_VER" : "RELEASE_VER"?真的吗?请发布您的真实代码。 - erickson
DEBUG_OR_RELEASE = (REL) ? "DEBUG_VER" : "DEBUG_VER" 无论 REL 是 true 还是 false,都会得到相同的字符串 DEBUG_VER。你有注意到吗? - Ravi Bhatt
@Ravi Bhatt 对不起,我匆忙发布了这个帖子,所以有一个愚蠢的拼写错误。现在已经更正了。 - ateiob
@ateiob,这里你的DEBUG是true,因此REL是false,所以DEBUG_OR_RELEASE = (REL) ? "RELEASE_VER" : "DEBUG_VER"将会把DEBUG_VER赋值给DEBUG_OR_RELEASE字符串。 - Ravi Bhatt
3个回答

3

将代码编译成Java字节码并不会进行优化(除了极少数例外情况)。

许多书籍为简单起见都声称“编译”阶段是进行优化的时候,但这是错误的。真正进行优化的时候是JVM处理字节码文件的时候。因此需要澄清的是:优化可能发生在将字节码编译成机器本地代码的过程中,由JVM工具完成。

有时根本没有进行优化(JVM在解释模式下工作)。有时JIT(即时编译器)会进行一些优化。有时自适应优化器负责优化(不仅优化而且还要根据额外操作的代码执行情况进行分析)。

所以,最终,您的文件没有问题。这就是Java世界的运作方式。

“但是为什么?”- 您可能会问。保留这些“无用”的信息在字节码中的原因是您永远无法确定可以消除多少代码,以便不同的JVM提供的优化仍能高效地工作。最好的方法就是不擦除任何信息,让优化器完成它们的工作。


当真正进行优化时,是在字节码文件加载到JVM时发生的 - 这有些误导,并且与您后来所说的不一致。JIT不会在加载方法时对其进行优化,而是在以解释模式执行一定次数后才进行优化。(这是可怕的简化) - Voo
进行了一些小的编辑以使其更加正确,但我仍然意识到在这里进行任何简化可能会产生误导。 - msi
@msi 谢谢。这很有道理,但在我的情况下,我确实想从发布的 APK 中删除 "DEBUG_VER" 字符串。我该怎么做呢? - ateiob
是的,用几百个单词来涵盖整个主题是不可能的,所以我们必须接受简化。但是更改措辞使其与其他论点一致,并且它确实触及了重要的要点,所以+1。 - Voo

1

即使变量被设置为true,它仍然在运行。 ?:语句是可执行和操作代码的一部分,由于Java没有预编译器,因此每次编译时都会运行

即使逻辑上每次迭代的操作相同,Java也不会自动推断和简化操作。


谢谢,但根据这个回答,应该是可能的。那个回答不正确吗? - ateiob
我认为他的意思是你会产生相同的结果,而不是从编译中删除它。但如果我错了,那么是的,那个答案是不正确的。 - user1131435
即使没有预处理器,Java编译器也会(在某些情况下必须)删除明显无法到达的代码,例如基于一个最终布尔类型变量且为false的条件。这在Java语言规范的第14.21节中有所讨论,并且可以很容易地在实践中验证。 - Eric Lafortune
已删除无法访问的代码,但我不确定可达的代码是否每次都产生相同的最终结果。虽然我很可能是错的。 - user1131435

1
根据您发布的内容,DEBUG为真,因此REL为假,所以(REL) ? "RELEASE_VER" : "DEBUG_VER"应该产生"DEBUG_VER"。
这正是您所观察到的,因此如果您希望看到"RELEASE_VER",则应设置:
public static final boolean DEBUG = false;

试一下,看看会发生什么。


谢谢,那个有效。这意味着我现在必须记得在导出发布版本时将 boolean DEBUG 更改为 false。哎呀。 - ateiob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接