【问题标题】:Is there difference in compilers - java编译器有区别吗 - java
【发布时间】:2013-10-08 13:42:30
【问题描述】:

相同版本的代码优化是否有任何差异: Oracle Java 编译器 Apache Java 编译器 IBM Java 编译器 OpenJDK Java 编译器。 如果有什么代码会展示不同的优化?还是他们使用相同的编译器?如果没有已知的优化差异,那么我在哪里可以找到有关如何测试编译器以进行不同优化的资源?

【问题讨论】:

  • 顺便说一句 - 据我所知,主要优化实际上是由 JIT 编译器完成的,而不是编译器本身
  • @EelLee JIT 编译器是 Java 中唯一真正的编译器。我不知道从源代码到字节码的转换称为“编译”的任何其他语言。
  • @MarkoTopolnik 似乎被广泛使用。我不知道您为什么认为“编译”仅适用于最终结果是本机代码的情况。
  • 我非常同意 Eel Lee 的说法。 Hotspot 是一个优秀的 JIT 编译器,它可以使用运行时信息来做出更好的优化决策。此外,它甚至可以在认为当前安全的情况下执行非法操作(例如删除同步或内联非最终方法),并在需要原始表单时退出这些优化。在这种情况下,我无法想象编译器编写者有太多理由专注于字节码的性能优化——毕竟它甚至可能会损害性能(如果在运行时更难优化的话)。
  • @MarkoTopolnik - 我认为这更像是语义问题,但是我自己还是个初学者,所以我认为没有必要争论 :)

标签: java optimization compiler-construction bytecode


【解决方案1】:

相同版本的代码优化是否有差异:Oracle Java 编译器 Apache Java 编译器 IBM Java 编译器 OpenJDK Java 编译器。

虽然编译器可能非常不同,但javac 几乎没有进行优化。主要优化是常量内联,这是在 JLS 中指定的,因此是标准的(任何错误除外)

如果有什么代码会演示不同的优化?

你可以这样做。

final String w = "world";
String a = "hello " + w;
String b = "hello world";
String c = w;
String d = "hello " + c;
System.out.prinlnt(a == b); // these are the same String
System.out.prinlnt(c == b); // these are NOT the same String

在第一种情况下,常量被内联并在编译时连接字符串。在第二种情况下,连接是在运行时执行的,并创建了一个新的字符串。

或者他们使用相同的编译器?

否,但 99% 的优化是由 JIT 在运行时执行的,因此对于给定版本的 JVM,这些优化是相同的。

如果没有已知的优化差异,那么我在哪里可以找到有关如何测试编译器以进行不同优化的资源?

如果有一个,我会感到惊讶,因为这听起来不是很有用。问题是 JIT 优化了字节码的预构建模板,如果您尝试优化字节码,您最终可能会混淆 JIT 并获得较慢的代码。即,如果不考虑将在其上运行的 JVM,就无法评估优化。

【讨论】:

    【解决方案2】:

    不,它们不使用相同的编译器。我不能对优化和东西发表太多评论,但这里有一个例子,编译器在工作中的不同之处。

    public class Test {
        public static void main(String[] args) {
            int x = 1L;  // <- this cannot compile
        }
    }
    

    如果你使用标准的java编译器,会抛出编译错误,并且类文件不会被创建。

    但是如果你用eclipse编译javaECJ,它不仅会抛出同样的编译错误,而且还会创建一个类文件(YES,一个类文件用于不可编译的代码,这使得 ECJ,我不会说错,但有点棘手),看起来像这样。

    public static void main(String[] paramArrayOfString)
    {
        throw new Error("Unresolved compilation problem: \n\tType mismatch: cannot convert from long to int.\n");
    }
    

    话虽如此,这只是在 2 个编译器之间。其他编译器可能有自己的工作方式。

    P.S:这个例子是从here.

    【讨论】:

    • 这是一个有效的观察,但值得一提的是,ECJ 不是一个“正确”的编译器,因为它故意违反了一些规则。在此示例中(如您所述),该类无法编译,但 ECJ 无论如何都会生成字节码。更有趣的比较是在给定相同输入的情况下生成不同形式的合法/正确输出的编译器之间的比较。但这很难举出例子。
    • @AndrzejDoyle - 在大多数情况下我都同意你的看法,但我真的不想称 ECJ 为“不正确的编译器”,因为它故意违反了一些规则。这是我的观点,每个人都有自己的观点,我尊重你的观点!
    【解决方案3】:

    我花费大量时间使用的唯一编译器是 javac(正如其他人所指出的,它在急切优化方面做得很少)和 Eclipse 编译器。

    在编写 Java 反编译器时,我观察到 Eclipse 编译代码的方式有一些(通常是令人沮丧的)差异,但不是很多。其中一些可以被认为是优化。其中:

    1. Eclipse 编译器似乎至少执行了一些重复代码分析。如果两个(或更多?)代码块都分支到单独但等效的代码块,则等效的目标块可能会被展平为具有多个条目跳转的单个块。我从未见过javac 执行过这种优化;始终会发出等效的块。我能回忆起的所有例子都发生在switch 语句中。这种优化减少了方法的大小(因此也减少了类文件的大小),这可能会提高加载和验证时间。它甚至可能会提高解释模式下的性能(特别是如果有问题的解释器执行内联),但我想这样的改进会很小。我怀疑一旦方法被 JIT 编译,它会有所作为。它还使反编译更加困难(grrr)。

    2. 基本块的发出顺序通常与javac 完全不同。这可能只是编译器内部设计的副作用,也可能是编译器试图优化代码布局以减少跳转次数。这是我通常会留给 JIT 的优化,这种理念似乎适用于 javac

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-29
      • 2011-04-19
      • 1970-01-01
      • 2011-03-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多