The public,

In addition, future articles will also be published in the public website (Coding Insight)

preface

GitHub address: LjyYano/Thinking_in_Java_MindMapping

In April 2018, Oracle Labs unveiled a new dark technology: Graal VM.

This is a cross-language full stack virtual machine enhanced on top of the HotSpot VIRTUAL machine, which can be used as a platform for “any language”.

There’s not a lot of information on Graal VM on the web right now, but you have to look at the official documentation. This article aims to briefly introduce:

  • What is Graal VM?
  • What are the benefits of Graal VM?
  • What are the disadvantages of Graal VM?
  • How does Graal VM work?
  • Install Graal VM on macOS
  • Compile a Spring Boot-based Java application intoNative application

Mind mapping

Here is a brief mind map of Graal VM.

An easy-to-read article: GraalVM: Java in the Age of Microservices.

What is Graal VM

Officially known as the “Universal VM” and “Polyglot VM”, the Graal VM is a cross-language full-stack virtual machine enhanced on top of the HotSpot VIRTUAL machine with the slogan “Run Programs Faster Anywhere”. Graal VM can run in “any language”, including:

  • Java virtual Machine based languages: Java, Scala, Groovy, Kotlin, etc.
  • Llvm-based languages: C, C++, Rust;
  • Other languages: JavaScript, Ruby, Python, R, etc.

Graal VM can mix and match these programming languages without additional overhead, support the mixing of interfaces and objects from different languages, and support the use of native library files already written for these languages.

Benefits of Graal VM

Please refer to the official documentation: Why GraalVM?

I think the most important feature is ahead-of-time Compilation. Substrate VM is a minimal runtime environment in version 0.20 of Graal VM that includes stand-alone exception handling, synchronous scheduling, thread management, memory management (garbage collection), and JNI access components. Substrate VM also contains a Native Image Generator, through which the user can construct an executable based on the construction machine.

The constructor uses points-to-analysis techniques To search for all reachable code from the user-provided program entry. Along with the search, it also executes the initialization code and saves the initialized heap into a heap snapshot when the executable is finally generated.

The Substrate VM can then be run directly from the target program without having to repeat the Java VIRTUAL machine initialization process. However, it is also determined in principle that Substrate VM must require the target program to be completely closed, that is, other code and class libraries unknown at compile time cannot be dynamically loaded. Based on this assumption, Substrate VM can explore the entire compilation space and deduce the target method of all virtual method calls through static analysis.

Adapt Java to native

In the past, a single service required 7*24 hours of uninterrupted operation and high availability on a single machine. At this time, Java service is very suitable. But Java applications need to run on hundreds of megabytes of JRE, which is not appropriate for microservices.

At the same time, in microservices, applications can be split at any time. Each application does not require a lot of memory, but needs to be started quickly, updated at any time, and may not need to run for a long time. Java applications are slow to start and need to be fully warmed up to achieve high performance.

GraalVM ahead of schedule provides a solution, with a 50-fold improvement in startup time and a 5-fold reduction in memory.

Disadvantages of Graal VM

The Java language is inherently at a disadvantage when it comes to microservices, because Java’s early slogan was “Write once, run anywhere.” The slogan is embedded in the Java DNA. If you want to change this (and really compare Java’s disadvantages with other languages’ advantages), there are many difficulties:

  • The reflection mechanism of the Java language makes it difficult to generate an executable at compile time. Because the API interface can be invoked dynamically at run time through reflection, these are not perceived at compile time. Unless reflection is abandoned or configuration files are provided for reflection calls at compile time.
  • ASM, CGLIB, and the Javassist bytecode library generate and modify bytecode at run time, which also cannot be compiled into native code via AOT. Spring’s dependency injection, for example, uses CGLIB enhancements. Spring has adapted GraalVM in the new version and can turn CGLIB off.
  • Discard the internal borrowing of the HotSpot VIRTUAL machine itself, because even HotSpot itself is wiped out in the local mirror.
  • Startup time and memory usage are indeed significantly optimized, but for large applications running for long periods of time, Java applications are not necessarily as fast as HotSpot.

How Graal VM works

The basic working principle of Graal VM is to convert the source code of these languages (such as JavaScript) or the compiled Intermediate format of the source code (such as LLVM bytecode) through the interpreter into Intermediate Representation (IR) that can be accepted by Graal VM. For example, designing an interpreter to convert LLVM output bytecode to support C and C++ is a process called Specialized (also known as Partial Evaluation).

Graal VM provides the Truffle toolset to quickly build an interpreter for a new language, and uses it to build a high-performance LLVM bytecode interpreter called Sulong.

Install Graal VM on macOS

For Linux, Windows, and other platforms, see Install GraalVM. Since I use macOS, this article describes how to install GraalVM, GraalVM Community Edition based on OpenJDK 11, on macOS.

Install the Graal VM

GraalVM Community Edition on macOS is a tar.gz file and the JDK installation directory is:

/Library/Java/JavaVirtualMachines/<graalvm>/Contents/Home
Copy the code

The installation steps for x86 64-bit macOS are as follows:

  1. inGraalVM Releases repository on GitHubFound on theGraalvm - ce - java11 - Darwin - amd64-20.1.0. Tar. GzDownload.
  2. unzip
The tar - XVF graalvm - ce - java11 - Darwin - amd64-20.1.0. Tar. GzCopy the code
  1. Move the folder to/Library/Java/JavaVirtualMachinesDirectory (sudo is required).
Sudo mv graalvm - ce - java11-20.1.0 / Library/Java/JavaVirtualMachinesCopy the code

To check whether the installation is successful, run the following command:

/usr/libexec/java_home -V
Copy the code

The running results are as follows:

Matching Java Virtual Machines (2):
    11.0.7, x86_64:	"GraalVM CE 20.1.0"/ Library/Java/JavaVirtualMachines/graalvm - ce - java11-20.1.0 / Contents/Home 1.8.0 comes with _201, x86_64:"Java SE 8"/ Library/Java/JavaVirtualMachines jdk1.8.0 _201. JDK/Contents/Home / Library/Java/JavaVirtualMachines/graalvm - ce - java11-20.1.0 / Contents/HomeCopy the code
  1. Because there may be multiple JDKS on the machine, you need to configure the runtime environment.

Add the GraalVM bin directory to the PATH environment variable.

exportPATH = / Library/Java/JavaVirtualMachines/graalvm - ce - java11-20.1.0 / Contents/Home/bin:$PATH
Copy the code

Set the JAVA_HOME environment variable.

exportJAVA_HOME = / Library/Java/JavaVirtualMachines/graalvm - ce - java11-20.1.0 / Contents/HomeCopy the code

Note: You may need to modify the BASHC configuration file.

Install GraalVM components

Through the above steps, you have installed the basic components of GraalVM. If you need additional support for Python, R, and other languages, you need to use the GU component.

gu install ruby
gu install r
gu install python
gu install wasm
Copy the code

Install GraalVM Native Image and run the following command:

gu install native-image
Copy the code

To install LLVM Toolchain, run the following command:

gu install llvm-toolchain
Copy the code

Java applications based on Spring Boot are compiled into native applications

Refer to GitHub’s Spring-Boot-GraalVM project, which details all the problems that graalVM can cause when compiling Spring Boot Java applications. It also compares the Java application startup and compilation cost of executable Java programs.

The Spring Graal Native project that Spring maintains with Graal VM already provides configuration information for most Of the Spring Boot components (and some patches that need to be handled at the code level), so we simply rely on this project. This allows Graal VM to capture compile-time configurations such as reflection, dynamic proxy, and so on. We simply rely on engineering.

Need to add dependencies in POM.xml:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-indexer</artifactId>
    <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.experimental</groupId> < artifactId > spring - graalvm - native < / artifactId > < version > 0.7.1 < / version > < / dependency >Copy the code

Specify the path to start the class:

<properties>
    <start-class>com.yano.workflow.WorkflowApplication</start-class>
</properties>
Copy the code

Configure a separate profile and compile it into a local executable at compile time via the native image-Maven-plugin.

<profiles> <profile> <id>native</id> <build> <plugins> <plugin> <groupId>org.graalvm.nativeimage</groupId> < artifactId > native - image - maven - plugin < / artifactId > < version > 20.1.0 < / version > < configuration > < buildArgs > - J - Xmx4G -H:+TraceClassInitialization -H:+ReportExceptionStackTraces -Dspring.graal.remove-unused-autoconfig=true
                            -Dspring.graal.remove-yaml-support=true
                        </buildArgs>
                        <imageName>${project.artifactId}</imageName> </configuration> <executions> <execution> <goals> <goal>native-image</goal> </goals> <phase>package</phase>  </execution> </executions> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </profile> </profiles>Copy the code

This plugin does not exist in Maven’s central repository. PluginRepositories and Repositories need to be specified:

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
    </repository>
</repositories>
<pluginRepositories>
    <pluginRepository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
    </pluginRepository>
</pluginRepositories>
Copy the code

Graal VM does not support CGLIB and can only use JDK dynamic proxies, so Spring’s Bean enhancements to normal classes should be turned off. The SpringBoot version must be greater than or equal to 2.2, and the SpringBootApplication annotation sets proxyBeanMethods to false.

@SpringBootApplication(proxyBeanMethods = false)
public class SpringBootHelloApplication {

	public static void main(String[] args) { SpringApplication.run(SpringBootHelloApplication.class, args); }}Copy the code

Package projects from the command line via Maven:

mvn -Pnative clean package
Copy the code

Finally, you can see the executable in the target directory, which is around 50M, compared to 17M for the Fat JAR.

Java - jar target/spring - the boot - graal - 0.0.1 - the SNAPSHOT. Jar. ____ _ __ _ _ / \ \ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \ / _ ` | \ \ \ \ \ \ / ___) | | _) | | | | | | | (_ | |))))' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.0. M4) 15:40:21 2020-04-30. 40149-187 the INFO [main] I.J.S.S pringBootHelloApplication: Starting SpringBootHelloApplication v0.0.1 - the SNAPSHOT on PikeBook. Fritz. Box with PID 40149 (/ Users/jonashecht/dev/spring - the boot/spring - the boot - graalvm/target/spring - the boot - graal - 0.0.1 - the SNAPSHOT. Jar started by jonashecht In/Users/jonashecht/dev/spring - the boot/spring - the boot - graalvm) 2020-04-30 15:40:21. 40149-190 the INFO [main] i.j.s.SpringBootHelloApplication : No active profile set, falling back to default profiles: Default 15:40:22 2020-04-30. 40149-280 the INFO [main] o.s.b.web.embedded.netty.Net tyWebServer: Netty started on the port (s) : 8080 2020-04-30 15:40:22. 40149-288 the INFO [main] I.J.S.S pringBootHelloApplication: Started SpringBootHelloApplication in 1.47 seconds (JVM running for 1.924)Copy the code

Can directly run the program through the command line, fast start. Compared to a normal Hello World Web application, the startup time is 1.47 seconds and the memory footprint is 491 MB.

The Spring Boot program, which compiles the local code, starts at 0.078s and occupies 30 MB of memory.

./spring-boot-graal

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \ / _ ` | \ \ \ \ \ \ / ___) | | _) | | | | | | | (_ | |))))' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: The 2020-05-01 10:25:31. 42231-200 the INFO [the main] I.J.S.S pringBootHelloApplication: Starting SpringBootHelloApplication on PikeBook.fritz.box with PID 42231 (/Users/jonashecht/dev/spring-boot/spring-boot-graalvm/target/native-image/spring-boot-graal started by jonashecht in / Users/jonashecht/dev/spring - the boot/spring - the boot - graalvm/target/native - 2020-05-01 10:25:31 image). 42231-200 the INFO [ main] i.j.s.SpringBootHelloApplication : No active profile set, falling back to default profiles: The default 2020-05-01 10:25:31. 241 WARN 42231 - [the main] io.net ty. Channel. DefaultChannelId: Failed to find the current process ID from ''; using a random value: 635087100 the 2020-05-01 10:25:31. 42231-245 the INFO [main] o.s.b.web.embedded.netty.Net tyWebServer: Netty started on the port (s) : 8080 2020-05-01 10:25:31. 42231-245 the INFO [main] I.J.S.S pringBootHelloApplication: Started SpringBootHelloApplication in 0.078 seconds (JVM running for 0.08)Copy the code

conclusion

  • This article focuses on the relationship between GraalVM and Java. Many languages can run on GraalVM (see Why GraalVM).
  • Note that Graal’s environment variables are configured incorrectly and will not compile, and JDK 11 requires a higher version of Maven.
  • GraalVM and GraalVM are the same thing, the official website is called GraalVM, but everywhere else is GraalVM…
  • The JDK itself is evolving to accommodate the native.
  • GraalVM compiled Native Java applications are only suitable for one-off, short-run scenarios, where traditional Java programs are more efficient for long runs.
  • The GitHub address for this article is LjyYano/Thinking_in_Java_MindMapping

The public,

In addition, future articles will also be published in the public website (Coding Insight)