3年前 (2021-07-10)  相关技术 |   抢沙发  6492 
文章评分 2 次,平均分 4.5

Red Hat提供的示例的启动速度和内存消耗给我留下了深刻的印象。这些令人印象深刻的数字的主要原因之一是,代码是用GraalVM(Oracle开发的Java虚拟机的扩展)提前编译到本机映像的(AOT)。为了帮助您更好地理解经典热点JVM和GraalVM之间的区别,我将在本文中向您介绍GraalVM及其特性和历史。

GraalVM是用纯Java编写的JVM的扩展,支持Oracle开发的多语言编程和提前编译。

HotSpot Java虚拟机的历史

多年来,HotSpot一直是Oracle维护和分发的运行Java程序的主要Java虚拟机。javahotspot性能引擎于1999年发布,最初由Animorphic开发,Animorphic是一家被sunmicrosystems收购的公司,现在归Oracle所有。这个虚拟机主要是用C/C++编写的,而且越来越复杂(2007年估计有25万行代码)。

HotSpot JVM的主要目的是运行Java字节码(.class文件),持续分析程序中经常执行的所谓热点的性能,并将它们编译为本机代码(机器代码)以提高性能。这是在运行时完成的,而不是在Java程序执行之前,因此是及时的。

在HotSpot JVM中运行Java代码的工作流如下(简化):

GraalVM-下一代JVM简介

HotSpot虚拟机主要解释提供的Java字节码,但在分析程序运行时发现应用程序的适当部分时,也会及时将字节码编译为本机代码。

当使用JIT编译器编译一个方法时,JVM将直接为任何进一步的方法调用执行机器代码,而不是解释它并为此改进性能。由于编译本机代码需要CPU时间和内存,JVM必须决定在运行时编译哪些方法,因为将所有方法直接编译为本机代码会影响性能。

Java9发布后的变化

使用Java9,特别是JEP295,JDK获得了一个提前编译器jaotc。此编译器使用OpenJDK项目Graal生成后端代码。这一步骤背后的动机如下:

JIT编译器速度很快,但是Java程序可能会变得非常大,以至于JIT需要很长时间才能完全预热。很少使用的Java方法可能根本就不会被编译,因为重复的解释调用可能会导致性能下降

–JEP 295的动机

Graal OpenJDK项目演示了用纯Java编写的编译器可以生成高度优化的代码。使用这个AOT编译器和java9,您可以提前手动编译Java代码。这意味着在执行之前生成机器代码,而不是像JIT编译器那样在运行时生成机器代码,这是第一种实验方法。

# using the new AOT compiler (jaotc is bundeled within JDK 9 and above)
jaotc --output libHelloWorld.so HelloWorld.class
jaotc --output libjava.base.so --module java.base
 
# with Java 9 you have to manually specify the location of the native code
java -XX:AOTLibrary=./libHelloWorld.so,./libjava.base.so HelloWorld

这将提高启动时间,因为JIT编译器不必拦截程序的执行。这种方法的主要缺点是依赖于平台的本机代码。这可能会导致此AOT编译代码的平台锁定。

GraalVM-下一代JVM简介

GraalVM的架构

基于Graal编译器,Oracle开始开发GraalVM,不仅可以处理HotSpot JVM庞大而复杂的C/C++代码库,还可以用Java编写的虚拟机来处理当前的多语言运动。

GraalVM的架构如下所示:

GraalVM-下一代JVM简介

GraalVM-下一代JVM简介

首先,您可能会注意到存在几种非JVM语言。现在,您可以在这个通用虚拟机中使用Ruby、R或JavaScript运行代码。为此,我们使用了块菌框架。Truffle是一个开放源码库,用于构建编程语言实现,作为自我修改抽象语法树的解释器(请阅读此处以获得更详细的解释)。有了这个特性,您现在可以在Java代码库中编写和执行JavaScript代码。

此外,GraalVM还提供了以下功能,用于提前编译本机可执行文件:

GraalVM允许您提前将程序编译成本机可执行文件。生成的程序不在javahotspot虚拟机上运行,而是使用必要的组件,如内存管理、来自虚拟机的不同实现(称为substratevm)的线程调度。底层VM是用Java编写的,并编译成本机可执行文件。与javavm相比,生成的程序具有更快的启动时间和更低的运行时内存开销。

–AOT上的GRAALVM

用GraalVM编译并运行第一个Java程序

GraalVM有两个版本:communityedition(CE)和enterpriseedition(EE),这两个版本都只适用于macosx和Linux。要在开发过程中在Windows上使用GraalVM,可以使用Oracle的官方Docker映像,下面的示例中使用了该映像。

想象一下以下简单的HelloWorld类:

public class HelloWorld {
 
  public static void main(String[] args) {
    System.out.println("Hello World!");
  }
 
}

在使用GraalVM和Java 9的AOT编译器之前,您执行以下代码:

$ javac HelloWorld.java
$ java HelloWorld
Hello World!

使用GraalVM,您现在可以选择使用现有方式(热点JVM)运行应用程序,或者使用GraalVM AOT编译器创建本机映像并运行可执行文件:

$ javac HelloWorld
$ native-image HelloWorld
$ ./helloworld
HelloWorld!

在这个HelloWorld示例中,性能的改进是微不足道的,但是在更大和更现实的应用程序中,性能的改进是显著的。

一个简单的polyglot应用程序的好例子可以在官方的GraalVM入门指南中找到:

import java.io.*;
import java.util.stream.*;
import org.graalvm.polyglot.*;
 
public class PrettyPrintJSON {
  public static void main(String[] args) throws java.io.IOException {
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
      String input = reader.lines().collect(Collectors.joining(System.lineSeparator()));
      try (Context context = Context.create("js")) {
        Value parse = context.eval("js", "JSON.parse");
        Value stringify = context.eval("js", "JSON.stringify");
        Value result = stringify.execute(parse.execute(input));
        System.out.println(result.asString());
      }
    }
}

这个Java类负责漂亮地打印JSON,并使用JavaScript方法JSON.parse()JSON.stringify()。此类的本机映像可以按如下方式构建:

$ javac PrettyPrintJSON.java
$ native-image --language:js PrettyPrintJSON
$ ./prettyprintjson < prettyMe.json
{
  "GraalVM": {
    "description": "Language Abstraction Platform",
    "supports": [
      "combining languages",
      "embedding languages",
      "creating native images"
    ],
    "languages": [
      "Java",
      "JavaScript",
      "Node.js",
      "Python",
      "Ruby",
      "R",
      "LLVM"
    ]
  }
}

运行本机映像和在热点中运行应用程序之间的性能差异现在可以测量:

$ time bin/java PrettyPrintJSON < prettyMe.json > /dev/null
real    0m1.101s
user    0m2.471s
sys 0m0.237s
 
$ time ./prettyprintjson < prettyMe.json > /dev/null
real    0m0.037s
user    0m0.015s
sys 0m0.016s

在我看来,Oracle在GraalVM方面做得非常出色,使Java成为一种编程语言。此外,这一举措提高了Java语言本身的可持续性和特性开发。有了多语言体系结构,这也增加了对其他编程语言的采用。

您可以在我的GitHub存储库:https://github.com/rieckpil/blog-tutorials/tree/master/graalvm-intro

中找到这些示例,并在您的计算机上直接使用GraalVM(Mac和Linux)或使用Windows上的Docker(确保为Docker提供至少6gb的RAM和2-4核)。

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2101.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册