In this entry, the function of "[Native Image]" (https://www.graalvm.org/docs/reference-manual/native-image/) of GraalVM Uses to deal with shortening the startup time of Java console programs.
After doing this and that, the program that took about 1 second to start in the JVM with the performance of a specific computer (equivalent to a micro instance of OCI) became a native binary, and the start became about 100 ms.
--Those who have basic knowledge about Java --Those who are interested in creating native binaries for GraalVM
--I have a console application written in Java. It was written by the author in another entry "Use JLine when you want to handle key input on the console character by character in Java" (https://qiita.com/hrkt/items/885f1c3526af03939c54). --I'm using JLine3 because I want to handle console keystroke events character by character. When JLine3 uses JNI to use platform-specific features, it uses the JANSI version of the terminal (so you need to do something like this entry). --I just want to see how fast Native Image can be
--You can use Linux (x86_64) or macOS. This entry uses the same one as the separate entry "Try running VS Code on cdr / code-server and Always Free Micro instances of OCI" (https://qiita.com/hrkt/items/44c78f71eb72f9d29f38).
Like this, I introduced it under / opt. At the time of writing this entry, 19.3.1 is the latest version of GraalVM.
cd /opt
sudo curl -L -O https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-19.3.1/graalvm-ce-java8-linux-amd64-19.3.1.tar.gz
sudo tar zxvf graalvm-ce-java8-linux-amd64-19.3.1.tar.gz
Use the GraalVM tool "gu" to introduce the tool for Native Image.
sudo /opt/graalvm-ce-java8-19.3.1/bin//gu install native-image
For native-image to work, you need a set of toolkits to build the binaries on your target platform. Since this entry uses CentOS7, install the packages as shown below.
sudo yum install -y glibc-devel zlib-devel gcc
The program used in this entry is a simple console calculator. Click here for the release of GitHub. https://github.com/hrkt/commandline-calculator/releases/tag/0.0.4
cd master
../gradlew :calculator-cli-without-spring:shadowJar
Then you will have calculator-cli-without-spring / build / libs / calculator-cli-without-spring-0.0.1-SNAPSHOT-all.jar. This Jar file is necessary for execution using the "Gradle Shadow" plugin that brings together the self-made part and the Jar that has been used for the first time. It is a collection of files into one JAR file. The resulting JAR file was about 760KB.
$ ls -la calculator-cli-without-spring/build/libs/calculator-cli-without-spring-0.0.1-SNAPSHOT-all.jar
-rw-rw-r--. 1 opc opc 757383 Jan 31 23:13 calculator-cli-without-spring/build/libs/calculator-cli-without-spring-0.0.1-SNAPSHOT-all.jar
Run it and try running "1 + 1".
[opc@instance-20200117-1627 commandline-calculator]$ java -jar calculator-cli-without-spring/build/libs/calculator-cli-without-spring-0.0.1-SNAPSHOT-all.jar
Calculator is running. Press ctrl-c to exit.(boot in 1272 msec.)
1+1=
1+1
=2
q
[opc@instance-20200117-1627 commandline-calculator]$
At this time, you can see that it took about 1.3 seconds to start the program. The time is measured by the following method.
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
long uptimeInMillis = runtimeMXBean.getUptime();
terminal.writer().println(String.format("Calculator is running. Press ctrl-c to exit.(boot in %d msec.)",
uptimeInMillis));
Unwind the JAR file created above and unzip it into your working space.
cd calculator-cli-without-spring/build/libs
mkdir inside
cd inside
jar xvf ../calculator-cli-without-spring-0.0.1-SNAPSHOT-all.jar
After that, create a directory to get the profile information for use in native-image.
mkdir -p META-INF/native-image
Before running native-image, run it as a normal Java program. By working with the agent, it collects information that cannot be obtained by static analysis, such as what kind of reflection is used at the time of operation and whether the JNI library is used.
/opt/graalvm-ce-java8-19.3.1/bin/java -agentlib:native-image-agent=config-output-dir=META-INF/native-image -cp . com.hrkt.commandlinecalculator.Main
When you execute "1 + 1 =" and enter "q" to exit the program in the same way as described above, the following information will be collected.
[opc@instance-20200117-1627 inside]$ ls -la META-INF/native-image/
total 16
drwxrwxr-x. 2 opc opc 109 Jan 31 23:15 .
drwxrwxr-x. 6 opc opc 88 Jan 31 23:15 ..
-rw-rw-r--. 1 opc opc 689 Jan 31 23:15 jni-config.json
-rw-rw-r--. 1 opc opc 4 Jan 31 23:15 proxy-config.json
-rw-rw-r--. 1 opc opc 366 Jan 31 23:15 reflect-config.json
-rw-rw-r--. 1 opc opc 472 Jan 31 23:15 resource-config.json
Build. The meaning of the option is explained on the site. ](Https://www.graalvm.org/docs/reference-manual/native-image/)
/opt/graalvm-ce-java8-19.3.1/bin/native-image --no-server --no-fallback --report-unsupported-elements-at-runtime -cp . com.hrkt.commandlinecalculator.Main
This takes time on the order of minutes. Let's wait patiently. The environment and target program for this entry will take approximately 5 minutes.
[com.hrkt.commandlinecalculator.main:13836] classlist: 11,879.57 ms
[com.hrkt.commandlinecalculator.main:13836] (cap): 3,604.46 ms
[com.hrkt.commandlinecalculator.main:13836] setup: 9,898.60 ms
[com.hrkt.commandlinecalculator.main:13836] (typeflow): 36,059.72 ms
[com.hrkt.commandlinecalculator.main:13836] (objects): 23,227.98 ms
[com.hrkt.commandlinecalculator.main:13836] (features): 3,516.18 ms
[com.hrkt.commandlinecalculator.main:13836] analysis: 63,682.08 ms
[com.hrkt.commandlinecalculator.main:13836] (clinit): 1,281.12 ms
[com.hrkt.commandlinecalculator.main:13836] universe: 3,092.99 ms
[com.hrkt.commandlinecalculator.main:13836] (parse): 10,531.88 ms
[com.hrkt.commandlinecalculator.main:13836] (inline): 28,108.04 ms
[com.hrkt.commandlinecalculator.main:13836] (compile): 176,012.21 ms
[com.hrkt.commandlinecalculator.main:13836] compile: 227,565.33 ms
[com.hrkt.commandlinecalculator.main:13836] image: 6,261.29 ms
[com.hrkt.commandlinecalculator.main:13836] write: 2,044.20 ms
[com.hrkt.commandlinecalculator.main:13836] [total]: 326,546.79 ms
Let's move the completed binary. Let's start it several times to see how it looks. You can see that it can be started from the first half of the 2-digit msec to about 100 msec at the latest. It starts up much faster than it does in the JVM.
[opc@instance-20200117-1627 inside]$ ./com.hrkt.commandlinecalculator.main
Calculator is running. Press ctrl-c to exit.(boot in 66 msec.)
^C
[opc@instance-20200117-1627 inside]$ ./com.hrkt.commandlinecalculator.main
Calculator is running. Press ctrl-c to exit.(boot in 14 msec.)
^C
[opc@instance-20200117-1627 inside]$ ./com.hrkt.commandlinecalculator.main
Calculator is running. Press ctrl-c to exit.(boot in 16 msec.)
^C
[opc@instance-20200117-1627 inside]$ ./com.hrkt.commandlinecalculator.main
Calculator is running. Press ctrl-c to exit.(boot in 24 msec.)
^C
[opc@instance-20200117-1627 inside]$ ./com.hrkt.commandlinecalculator.main
Calculator is running. Press ctrl-c to exit.(boot in 131 msec.)
^C
[opc@instance-20200117-1627 inside]$ ./com.hrkt.commandlinecalculator.main
Calculator is running. Press ctrl-c to exit.(boot in 27 msec.)
^C
First, let's check the type.
[opc@instance-20200117-1627 inside]$ file com.hrkt.commandlinecalculator.main
com.hrkt.commandlinecalculator.main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=b5ce8429a80c97c4e12c87c516a8b24b59c64086, not stripped
It's just an ordinary ELF binary. What kind of library are you using ...
[opc@instance-20200117-1627 inside]$ ldd com.hrkt.commandlinecalculator.main
linux-vdso.so.1 => (0x00007fff5abf9000)
libm.so.6 => /lib64/libm.so.6 (0x00007f74c3bf7000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f74c39db000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f74c37d7000)
libz.so.1 => /lib64/libz.so.1 (0x00007f74c35c1000)
librt.so.1 => /lib64/librt.so.1 (0x00007f74c33b9000)
libc.so.6 => /lib64/libc.so.6 (0x00007f74c2feb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f74c3ef9000)
I'm using a very standard library for my Linux distribution. And the size is ...
[opc@instance-20200117-1627 inside]$ ls -la com.hrkt.commandlinecalculator.main
-rwxrwxr-x. 1 opc opc 10066064 Feb 1 04:08 com.hrkt.commandlinecalculator.main
About 10MB. It swelled considerably.
This entry dealt with trying to speed up the launch of Java console programs with GraalVM's native-image.
I've looked up to "startup", but keep in mind that it doesn't mention speed when it's running continuously after that.
In order to use JNI, the program I wrote, I used JNA at first as I wrote in Another entry, but native -image could not be executed successfully. GitHub also had an exchange such as (Using JNA inside a native image does not work # 673) [https://github.com/oracle/graal/issues/673].
It seems that the Native Image side of GraalVM does not yet support the function using JNA used in this entry.
Recommended Posts