Introduction

GraalVM is a high-performance JDK that supports Java/Python/JavaScript and other languages. It allows Java to be turned into a binary file for execution, making programs run faster anywhere. This may be a war between Java and Go?

GraalVM

Download and install GraalVM

Install GraalVM

First of all, go to the official website to download, I downloaded it directly from GitHub Release Page, please download the corresponding system package, I downloaded the following.

graalvm-ce-java11-darwin-amd64-22.3.0.tar.gz

Download and extract to some directory, mine is as follows.

/Users/larry/Software/graalvm-ce-java11-22.3.0

Then test if the corresponding program can be executed properly, such as java --version. On a Mac the following error will occur.

1
is damaged and can't be opened.

So the following statement needs to be executed.

1
$ sudo xattr -r -d com.apple.quarantine /Users/larry/Software/graalvm-ce-java11-22.3.0

Note the modification of the corresponding directory.

Then it will be ready for execution.

1
2
3
4
$ ./java --version
openjdk 11.0.17 2022-10-18
OpenJDK Runtime Environment GraalVM CE 22.3.0 (build 11.0.17+8-jvmci-22.3-b08)
OpenJDK 64-Bit Server VM GraalVM CE 22.3.0 (build 11.0.17+8-jvmci-22.3-b08, mixed mode, sharing)

Install native-image

This tool is used to convert Java programs into native binary packages and is installed as follows.

1
2
3
4
5
$ ./gu install native-image
Downloading: Component catalog from www.graalvm.org
Processing Component: Native Image
Downloading: Component native-image: Native Image from github.com
Installing new component: Native Image (org.graalvm.native-image, version 22.3.0)

Configure the environment

Configure environment variables

Since this GraalVM is not mature enough and I don’t want to use it all the time, I switch it by one command, configured as follows.

1
2
3
4
5
6
export GraalVM_HOME=/Users/larry/Software/graalvm-ce-java11-22.3.0/Contents/Home
alias switchOnGraalVM='export PATH=$GraalVM_HOME:$PATH'
alias switchOnGraalVMJavaHome='export JAVA_HOME=/Users/larry/Software/graalvm-ce-java11-22.3.0/Contents/Home'

alias switchOffGraalVM='export PATH=`echo $PATH | tr ":" "\n" | grep -v "graalvm" | tr "\n" ":"`'
alias switchOffGraalVMJavaHome='export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_212.jdk/Contents/Home'

Configuring IDEA

You can configure the corresponding JDK on IDEA so that you can use it when developing.

Configure the corresponding JDK on IDEA

Integrating Spring Native with Spring Boot

Common Spring Boot application

New to create a common Spring Boot web application, the main Java code is as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@SpringBootApplication
@RestController
@RequestMapping("/")
public class SpringbootNativeGraalVMMain {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootNativeGraalVMMain.class, args);
    }

    @GetMapping("/hi-graalvm")
    public String hi() {
        return "This message is from Spring Boot built/run by GraalVM/Spring Native";
    }
}

The boot time is 1.193 seconds, which is not bad. My computer is not bad.

Spring Boot application enablement takes 1.193 seconds

Integrating Spring Native

Adding dependencies.

1
2
3
4
5
<dependency>
  <groupId>org.springframework.experimental</groupId>
  <artifactId>spring-native</artifactId>
  <version>${spring-native.version}</version>
</dependency>

Add the plug-in, which is very important, otherwise various errors will occur.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.experimental</groupId>
      <artifactId>spring-aot-maven-plugin</artifactId>
      <version>0.11.5</version>
      <executions>
        <execution>
          <id>test-generate</id>
          <goals>
            <goal>test-generate</goal>
          </goals>
        </execution>
        <execution>
          <id>generate</id>
          <goals>
            <goal>generate</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Add the following plugins to build executable programs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<profiles>
  <profile>
    <id>native</id>
    <properties>
      <repackage.classifier>exec</repackage.classifier>
      <native-buildtools.version>0.9.11</native-buildtools.version>
    </properties>
    <dependencies>
      <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-launcher</artifactId>
        <scope>test</scope>
      </dependency>
    </dependencies>
    <build>
      <plugins>
        <plugin>
          <groupId>org.graalvm.buildtools</groupId>
          <artifactId>native-maven-plugin</artifactId>
          <version>${native-buildtools.version}</version>
          <extensions>true</extensions>
          <executions>
            <execution>
              <id>test-native</id>
              <phase>test</phase>
              <goals>
                <goal>test</goal>
              </goals>
            </execution>
            <execution>
              <id>build-native</id>
              <phase>package</phase>
              <goals>
                <goal>build</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

Then build the executable with the following command, which will take a bit longer.

1
$ mvn clean package -Pnative

The build completed in about two minutes or so, and an executable was generated. Run it with the following command.

1
$ target/spring-boot-native-graalvm

It turned out to take only 0.066 seconds, or 66 milliseconds to do so, which is too fast.

Spring-Native application starts in 66 milliseconds

The http service is working fine.

The http service is working fine.

Running with Docker

Run the local Docker first and then add the dependencies as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <classifier>${repackage.classifier}</classifier>
    <image>
      <builder>paketobuildpacks/builder:tiny</builder>
      <env>
        <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
      </env>
    </image>
  </configuration>
</plugin>

Build the Docker image with the following command.

1
mvn spring-boot:build-image

It may take a long time, you need to download some tools and images.

After a successful build, you can see that the new image has been added.

1
2
$ docker images | grep graalvm
spring-boot-native-graalvm                               1.0-SNAPSHOT                                     d2c8d5c52a3c        42 years ago        85.8MB 

Use the following command to run the image.

1
$ docker run --rm spring-boot-native-graalvm:1.0-SNAPSHOT -p 8080:8080

Run the spring-boot-native-graalvm image

The startup time is 59ms, which is even shorter.

Some notes

  • Directly convert jar package to executable by native-image command, if you encounter various problems, you are advised to give up trying, which is the value of Spring Native existence.
  • Be careful to switch the corresponding Java program and Java Home, otherwise the build will be abnormal.
  • From the package name experimental of Spring Native, it is still experimental and there should be some time before it is applied to production environment, so don’t use it easily on production.

Complete Code

The code can be found on GitHub: https://github.com/LarryDpk/pkslow-samples

Reference: https://www.cnblogs.com/larrydpk/p/17073451.html