JIT编译器,内联和转义分析

即时(JIT)

即时(JIT)编译器是Java虚拟机的大脑。 JVM中对JIT编译器的影响最大。

一会儿,让我们退后一步,看看已编译和未编译语言的示例。

诸如Go,C和C ++之类的语言之所以称为编译语言,是因为它们的程序以二进制(编译)代码的形式分发,针对特定的CPU。

另一方面,将解释 PHP和Perl之类的语言。 只要机器具有解释器,就可以在任何CPU上运行相同的程序源代码。 解释器在执行该行时将程序的每一行转换为二进制代码。

Java试图在这里找到中间立场。 Java应用程序已编译,但没有被编译为用于特定CPU的特定二进制文件,而是被编译为bytecode 。 这为Java提供了一种解释语言的平台独立性。 但是Java不止于此。

在典型的程序中,仅一小部分代码会频繁执行,而应用程序的性能主要取决于这些代码部分的执行速度。 这些关键部分被称为应用程序的热点

JVM执行特定代码段的次数越多,有关它的信息就越多。 这使JVM可以做出明智/优化的决策,并将小的热代码编译为CPU特定的二进制文件。 该过程称为即时编译(JIT)

现在,我们运行一个小程序并观察JIT编译。

public class App {
  public static void main(String[] args) {
    long sumOfEvens = 0;
    for(int i = 0; i < 100000; i++) {
      if(isEven(i)) {
        sumOfEvens += i;
      }
    }
    System.out.println(sumOfEvens);
  }

  public static boolean isEven(int number) {
    return number % 2 == 0;
  }
}


#### Run
javac App.java && \
java -server \
     -XX:-TieredCompilation \
     -XX:+PrintCompilation \
              - XX:CompileThreshold=100000 App


#### Output
87    1             App::isEven (16 bytes)
2499950000

输出告诉我们isEven方法已编译。 我故意禁用了TieredCompilation,以仅获取最常编译的代码。

JIT编译的代码将极大地提高您的应用程序的性能。 要检查吗? 编写一个简单的基准代码。

内联

内联是JIT编译器进行的最重要的优化之一。 内联将方法调用替换为方法的主体,以避免方法调用的开销。

让我们再次运行相同的程序,这次观察内联。

#### Run
javac App.java && \
java -server \
     -XX:+UnlockDiagnosticVMOptions \
     -XX:+PrintInlining \
     -XX:-TieredCompilation App

#### Output
@ 12   App::isEven (16 bytes)   inline (hot)
2499950000

再次进行内联将大大提高您的应用程序的性能。

转义分析

转义分析是一种技术,通过该技术JIT编译器可以分析新对象的使用范围,并决定是将其分配在Java堆上还是在方法堆栈上。 它还消除了对所有非全局转义对象的锁定

让我们运行一个小程序,观察垃圾回收。

public class App {
  public static void main(String[] args) {
    long sumOfArea = 0;
    for(int i = 0; i < 10000000; i++) {
      Rectangle rect = new Rectangle(i+5, i+10);
      sumOfArea += rect.getArea();
    }
    System.out.println(sumOfArea);
  }

  static class Rectangle {
    private int height;
    private int width;

    public Rectangle(int height, int width) {
      this.height = height;
      this.width = width;
    }

    public int getArea() {
      return height * width;
    }
  }
}

在此示例中,矩形对象被创建并且仅在循环内可用,它们的特征是NoEscape,并且将在堆栈而不是堆上分配它们。 具体来说,这意味着不会发生垃圾回收。

让我们在不使用EscapeAnalysis的情况下运行该程序。

#### Run
javac App.java && \
java -server \
     -verbose:gc \
     -XX:-DoEscapeAnalysis App

#### Output
[GC (Allocation Failure)  65536K->472K(251392K), 0.0007449 secs]
[GC (Allocation Failure)  66008K->440K(251392K), 0.0008727 secs]
[GC (Allocation Failure)  65976K->424K(251392K), 0.0005484 secs]
16818403770368

如您所见,GC已启动。 分配失败意味着年轻一代中没有剩余空间来分配对象。 因此,这是年轻GC的正常原因。

这次让我们使用EscapeAnalysis运行它。

#### Run
javac App.java && \
java -server \
    -verbose:gc \
    -XX:+DoEscapeAnalysis App

#### Output
16818403770368

这次没有发生GC。 从根本上讲,这意味着创建寿命短且作用域狭窄的对象不一定会引入垃圾。

默认情况下启用DoEscapeAnalysis选项。 请注意,只有Java HotSpot Server VM支持此选项。

因此,我们所有人都应避免过早的优化,而应专注于编写更具可读性/可维护性的代码,并让JVM发挥作用。

翻译自: https://www.javacodegeeks.com/2015/12/jit-compiler-inlining-escape-analysis.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值