Oracle GraalVM初体验,性能不如Java9!

近期,Oracle发布了GraalVM 1.0版本。小编已经在《怒赞!Oracle发布通用虚拟机,支持多种编程语言互操作》一文中做过报道!

高性能、多种语言的虚拟机

GraalVM是一个通用虚拟机,支持多语言,而且高性能。用于运行使用JavaScript,Python 3,Ruby,R,基于JVM的语言,如Java,Scala,Kotlin,和基于LLVM的语言,如C和C ++编写的应用。

Oracle GraalVM初体验,性能不如Java9!

GraalVM消除了编程语言之间的隔离,并在共享运行时启用了互操作性。它可以独立运行,也可以在OpenJDK,Node.js,Oracle数据库或MySQL环境中运行。

有几个因素可能会让你想要从常规的JRE切换到GraalVM:

  • 其中之一可能是它声称的改进的性能

  • 另一个可能是多语言功能,以透明地混合匹配支持的语言

  • 还有,通过本地支持,可以将Java应用作为本机代码提供

当然,GraalVM的发布,也吸引了许多技术极客的目光,想要一探究竟。本文来自国外的一名超过15年经验的软件架构师Nicolas Fränkel,看看他眼中的GraalVM。

Oracle GraalVM初体验,性能不如Java9!

作者介绍

第一步是下载Graal VM。它有两种选择:

社区版(Community Edition)

  • 所有开源许可证

  • 免费供生产使用

企业版(Enterprise Edition)

  • 免费用于评估和其他非生产用途

  • 对于商业用途和支持选项,应联系销售团队

第一个体验感受:CE版仅支持Linux操作系统。对于OSX,需要使用EE版本。目前还没有Windows版本(那么,未来还有吗?)

GraalVM架构

该架构与传统的Java JDK 9之前的版本相似。

Oracle GraalVM初体验,性能不如Java9!

GraalVM JDK的架构

因此,GraalVM可以成为任何标准JDK的替代品。

运行java -version将返回以下的输出:

java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
GraalVM 1.0.0-rc1 (build 25.71-b01-internal-jvmci-0.42, mixed mode)

截至目前,GraalVM仅限于Java 8功能。

一些性能测试基准

下一步是检查性能是否有所改善。我使用了JMH框架:它专注于此。

我使用了下面的代码:

Oracle GraalVM初体验,性能不如Java9!

并使用java -jar target / benchmarks.jar命令行在3个不同的JRE上进行了测试。

GraalVM结果

# JMH version: 1.20
# VM version: JDK 1.8.0_161, VM 25.71-b01-internal-jvmci-0.42
# VM invoker: /usr/local/graalvm-1.0.0-rc1/Contents/Home/jre/bin/java
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: ch.frankel.blog.MyBenchmark.testMethod
Result "ch.frankel.blog.MyBenchmark.testMethod":
6.795 ±(99.9%) 0.016 ops/s [Average]
(min, avg, max) = (6.477, 6.795, 6.967), stdev = 0.068
CI (99.9%): [6.778, 6.811] (assumes normal distribution)
# Run complete. Total time: 00:06:59
Benchmark Mode Cnt Score Error Units
MyBenchmark.testMethod thrpt 200 6.795 ± 0.016 ops/s

Oracle JDK 8结果

# JMH version: 1.20
# VM version: JDK 1.8.0_92, VM 25.92-b14
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home/jre/bin/java
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: ch.frankel.blog.MyBenchmark.testMethod
Result "ch.frankel.blog.MyBenchmark.testMethod":
6.727 ±(99.9%) 0.017 ops/s [Average]
(min, avg, max) = (6.466, 6.727, 6.899), stdev = 0.070
CI (99.9%): [6.710, 6.743] (assumes normal distribution)
# Run complete. Total time: 00:07:00
Benchmark Mode Cnt Score Error Units
MyBenchmark.testMethod thrpt 200 6.727 ± 0.017 ops/s

Oracle JDK 9结果

# JMH version: 1.20
# VM version: JDK 9.0.4, VM 9.0.4+11
# VM invoker: /Library/Java/JavaVirtualMachines/jdk-9.0.4.jdk/Contents/Home/bin/java
# VM options: <none>
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: ch.frankel.blog.MyBenchmark.testMethod
Result "ch.frankel.blog.MyBenchmark.testMethod":
7,136 ±(99.9%) 0,026 ops/s [Average]
(min, avg, max) = (6,464, 7,136, 7,443), stdev = 0,111
CI (99.9%): [7,110, 7,162] (assumes normal distribution)
# Run complete. Total time: 00:07:26
Benchmark Mode Cnt Score Error Units
MyBenchmark.testMethod thrpt 200 7,136 ± 0,026 ops/s

测试比较如下:

Oracle GraalVM初体验,性能不如Java9!

性能方面,GraalVM和Java 8之间的差距并不明显,而他们和Java 9之间标准差最高。

本地化

GraalVM能够通过native-image命令行将JAR转换为本机可执行文件。我试图用创建的JAR来完成它。

native-image -H:+JNI -jar target/benchmarks.jar

但是,当试图运行新创建的二进制文件时,我偶然发现了以下内容:

Exception in thread "main" java.lang.reflect.InvocationTargetException
at java.lang.Throwable.<init>(Throwable.java:310)
at java.lang.Exception.<init>(Exception.java:102)
at java.lang.ReflectiveOperationException.<init>(ReflectiveOperationException.java:89)
at java.lang.reflect.InvocationTargetException.<init>(InvocationTargetException.java:72)
at com.oracle.svm.reflect.proxies.Proxy_3_Main_main.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:199)
at Lcom/oracle/svm/core/code/CEntryPointCallStubs;
.com_002eoracle_002esvm_002ecore_002eJavaMainWrapper_002erun_0028int_002corg_002egraalvm_002enativeimage_002ec_002etype_002eCCharPointerPointer_0029(generated:0)
Caused by: java.lang.IllegalArgumentException: class org.openjdk.jmh.runner.options.TimeValue is not a value type
at java.lang.Throwable.<init>(Throwable.java:265)
at java.lang.Exception.<init>(Exception.java:66)
at java.lang.RuntimeException.<init>(RuntimeException.java:62)
at java.lang.IllegalArgumentException.<init>(IllegalArgumentException.java:52)
at joptsimple.internal.Reflection.findConverter(Reflection.java:66)
at joptsimple.ArgumentAcceptingOptionSpec.ofType(ArgumentAcceptingOptionSpec.java:106)
at org.openjdk.jmh.runner.options.CommandLineOptions.<init>(CommandLineOptions.java:109)
at org.openjdk.jmh.Main.main(Main.java:41)
... 4 more

更糟的是,试图为Spring Pet Clinic创建一个镜像失败,原因如下:

error: unsupported features in 3 methods
Detailed message:
Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException:
Unsupported constructor java.lang.ClassLoader.<init>(ClassLoader) is reachable:
The declaring class of this element has been substituted, but this element is not present in the substitution class
To diagnose the issue, you can add the option -H:+ReportUnsupportedElementsAtRuntime.
The unsupported element is then reported at run time when it is accessed the first time.
Trace:
at parsing java.security.SecureClassLoader.<init>(SecureClassLoader.java:76)
Call path from entry point to java.security.SecureClassLoader.<init>(ClassLoader):
at java.security.SecureClassLoader.<init>(SecureClassLoader.java:76)
at java.net.URLClassLoader.<init>(URLClassLoader.java:100)
at org.springframework.boot.loader.LaunchedURLClassLoader.<init>(LaunchedURLClassLoader.java:50)
at org.springframework.boot.loader.Launcher.createClassLoader(Launcher.java:74)
at org.springframework.boot.loader.Launcher.createClassLoader(Launcher.java:64)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:49)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
at com.oracle.svm.reflect.proxies.Proxy_1_JarLauncher_main.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:199)
at Lcom/oracle/svm/core/code/CEntryPointCallStubs;
.com_002eoracle_002esvm_002ecore_002eJavaMainWrapper_002erun_0028int_002corg_002egraalvm_002enativeimage_002ec_002etype_002eCCharPointerPointer_0029(generated:0)
Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException:
Unsupported field java.net.URL.handlers is reachable
To diagnose the issue, you can add the option -H:+ReportUnsupportedElementsAtRuntime.
The unsupported element is then reported at run time when it is accessed the first time.
Trace:
at parsing java.net.URL.setURLStreamHandlerFactory(URL.java:1118)
Call path from entry point to java.net.URL.setURLStreamHandlerFactory(URLStreamHandlerFactory):
at java.net.URL.setURLStreamHandlerFactory(URL.java:1110)
at org.springframework.boot.loader.jar.JarFile.resetCachedUrlHandlers(JarFile.java:383)
at org.springframework.boot.loader.jar.JarFile.registerUrlProtocolHandler(JarFile.java:373)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:48)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
at com.oracle.svm.reflect.proxies.Proxy_1_JarLauncher_main.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:199)
at Lcom/oracle/svm/core/code/CEntryPointCallStubs;
.com_002eoracle_002esvm_002ecore_002eJavaMainWrapper_002erun_0028int_002corg_002egraalvm_002enativeimage_002ec_002etype_002eCCharPointerPointer_0029(generated:0)
Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException:
Unsupported method java.security.ProtectionDomain.getCodeSource() is reachable:
The declaring class of this element has been substituted, but this element is not present in the substitution class
To diagnose the issue, you can add the option -H:+ReportUnsupportedElementsAtRuntime.
The unsupported element is then reported at run time when it is accessed the first time.
Trace:
at parsing org.springframework.boot.loader.Launcher.createArchive(Launcher.java:118)
Call path from entry point to org.springframework.boot.loader.Launcher.createArchive():
at org.springframework.boot.loader.Launcher.createArchive(Launcher.java:117)
at org.springframework.boot.loader.ExecutableArchiveLauncher.<init>(ExecutableArchiveLauncher.java:38)
at org.springframework.boot.loader.JarLauncher.<init>(JarLauncher.java:35)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
at com.oracle.svm.reflect.proxies.Proxy_1_JarLauncher_main.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:199)
at Lcom/oracle/svm/core/code/CEntryPointCallStubs;
.com_002eoracle_002esvm_002ecore_002eJavaMainWrapper_002erun_0028int_002corg_002egraalvm_002enativeimage_002ec_002etype_002eCCharPointerPointer_0029(generated:0)
Error: Processing image build request failed

动态类加载不适用于GraalVM。我尝试使用H2和HSQLDB独立JAR ......无济于事。在这两种情况下,我得到了以下堆栈:

java.lang.NullPointerException
at com.oracle.graal.pointsto.ObjectScanner.scanField(ObjectScanner.java:113)
at com.oracle.graal.pointsto.ObjectScanner.doScan(ObjectScanner.java:263)
at com.oracle.graal.pointsto.ObjectScanner.finish(ObjectScanner.java:307)
at com.oracle.graal.pointsto.ObjectScanner.scanBootImageHeapRoots(ObjectScanner.java:78)
at com.oracle.graal.pointsto.ObjectScanner.scanBootImageHeapRoots(ObjectScanner.java:60)
at com.oracle.graal.pointsto.BigBang.checkObjectGraph(BigBang.java:581)
at com.oracle.graal.pointsto.BigBang.finish(BigBang.java:552)
at com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:653)
at com.oracle.svm.hosted.NativeImageGenerator.lambda$run$0(NativeImageGenerator.java:381)
at java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1386)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)----

结论

GraalVM具有许多有前景远大的功能,包括多语言功能,尽管我现在对该功能没什么兴趣。但是,从我有限的测试后,它仍然有改善的余地。在性能方面,Java 9表现更出色.而且,本地镜像像创建在当前状态下的使用太有限。恕我直言,目前它处于MVP状态。希望在下一个版本中能够改进上述问题。

相关推荐