Java 编译器优化

最近,我在读这本 文章

根据这篇文章,JavaCompiler 即 javac 在生成字节码时不执行任何优化。这是真的吗?如果是这样,那么它是否可以被实现为一个中间代码生成器来消除冗余并生成最优代码?

25775 次浏览

如果有的话,javac只会做很少的优化。

关键在于 JIT 编译器完成了大部分优化——如果它有很多信息,那么它的工作效果最好,如果 javac也执行了优化,那么其中一些信息可能会丢失。如果 javac执行某种形式的循环展开,那么 JIT 本身就很难以一般方式做到这一点——而且它有更多关于哪些优化将使 事实上工作的信息,因为它知道目标平台。

当我读到这一部分的时候,我停止了阅读:

更重要的是,javac 编译器 不执行简单的优化 像循环展开,代数 简化,强度降低, 为了得到这些好处 其他简单的优化 程序员必须在 Java 源代码,并且不依赖 Javac 编译器来执行它们。

首先,在 Java 源代码上进行循环展开几乎不是一个好主意。javac在优化方面做得不多的原因是,它是由 JVM 中的 JIT 编译器完成的,它可以比编译器做出更好的决策,因为它可以准确地看到哪些代码运行得最多。

我曾经研究过输出的 Java 字节码(使用一个名为 FrontEnd 的应用程序)。它基本上不做任何优化,除了内联常量(静态 final)和预先计算固定表达式(如2 * 5和“ ab”+ “ cd”)。这就是为什么如此容易反汇编(使用一个名为 JAD 的应用程序)的部分原因

我还发现了一些有趣的地方来优化 Java 代码。它帮助我提高了2.5倍的内循环速度。

一个方法有5个快速访问变量。当调用这些变量时,它们比所有其他变量都快(可能是因为堆栈维护)。方法的参数也计算到这5。因此,如果在 for 循环中有执行了上百万次的代码,那么在方法的开始分配这些变量,并且没有参数。

局部变量也比字段快,所以如果在内部循环中使用字段,可以通过在方法开始时将这些变量分配给局部变量来缓存这些变量。缓存引用而不是内容。(比如: int [] px = this. px;)

javac编译器曾经支持通过在命令行上传递 -o来生成优化的字节码的选项。

然而,从 J2SE1.3开始,HotSpot JVM 随平台一起发布引入了动态技术,如即时编译和通用执行路径的自适应优化。因此,启动此版本的 Java 编译器忽略了 -o

在阅读 Ant javac任务及其 optimize属性时,我偶然发现了这个标志:

指示是否应使用优化编译源代码; 默认值为 off。从 JDK 1.3开始的 Sun 的 javac忽略了这个标志(因为编译时优化是不必要的)。

HotSpot JVM 的动态优化相对于编译时优化的优势在 这一页中提到:

Server VM 包含一个高级的自适应编译器,它支持通过优化 C + + 编译器执行的许多相同类型的优化,以及一些传统编译器无法完成的优化,比如跨虚拟方法调用的主动内联。与静态编译器相比,这是一个竞争优势和性能优势。自适应优化技术的方法非常灵活,其性能通常优于甚至高级的静态分析和编译技术。

要优化字节码,可以使用 前卫

正如其他人所指出的,主流 JVM 中的 JIT 将在编译代码时优化代码。它的性能可能会超过 ProGuard,因为它可以访问更多的上下文。但在更简单的虚拟机中,情况可能并非如此。在 Android 世界中,针对 Dalvik (在 Lollipop 之前 Android 就有的虚拟机)使用 ProGuard 优化是常见的做法。

ProGuard 还会收缩和混淆字节码,这在发布客户端应用程序时是必须的(即使您没有使用优化)。

编译器不优化字节码,因为它是在运行时由 JIT 优化器优化的。

如果您所针对的运行时类型没有一个 JIT 优化器(即使它有一个 JIT 编译器) ,或者您正在进行 AOT 编译,我建议使用一个优化模糊处理器,如 ProGuard 或 Allatori。