Oracle GraalVM初体验,性能不如Java9!
近期,Oracle发布了GraalVM 1.0版本。小编已经在《怒赞!Oracle发布通用虚拟机,支持多种编程语言互操作》一文中做过报道!
高性能、多种语言的虚拟机
GraalVM是一个通用虚拟机,支持多语言,而且高性能。用于运行使用JavaScript,Python 3,Ruby,R,基于JVM的语言,如Java,Scala,Kotlin,和基于LLVM的语言,如C和C ++编写的应用。
GraalVM消除了编程语言之间的隔离,并在共享运行时启用了互操作性。它可以独立运行,也可以在OpenJDK,Node.js,Oracle数据库或MySQL环境中运行。
有几个因素可能会让你想要从常规的JRE切换到GraalVM:
其中之一可能是它声称的改进的性能
另一个可能是多语言功能,以透明地混合匹配支持的语言
还有,通过本地支持,可以将Java应用作为本机代码提供
当然,GraalVM的发布,也吸引了许多技术极客的目光,想要一探究竟。本文来自国外的一名超过15年经验的软件架构师Nicolas Fränkel,看看他眼中的GraalVM。
第一步是下载Graal VM。它有两种选择:
社区版(Community Edition)
所有开源许可证
免费供生产使用
企业版(Enterprise Edition)
免费用于评估和其他非生产用途
对于商业用途和支持选项,应联系销售团队
第一个体验感受:CE版仅支持Linux操作系统。对于OSX,需要使用EE版本。目前还没有Windows版本(那么,未来还有吗?)
GraalVM架构
该架构与传统的Java JDK 9之前的版本相似。
因此,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框架:它专注于此。
我使用了下面的代码:
并使用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
测试比较如下:
性能方面,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状态。希望在下一个版本中能够改进上述问题。