Build microservice applications based on static compilation

Author: Rao Zihao (Cheng Pu)

Limitations of Java

A traditional Java application can be roughly divided into the following steps from code writing to startup and operation:

  1. First, write the .java source code program.
  2. Then, use the javac tool to translate the .java file into .class bytecode. Bytecode is one of the very important contents in Java. It is precisely because of its appearance that Java can shield the underlying environment and achieve Write once , the effect of run anywhere!
  3. The .class file based on step 2 will be packaged into a jar package or war package for deployment and execution. During the deployment process, the application is loaded through the Java virtual machine and then the bytecode is interpreted to run the business logic.

The whole process is shown in the figure below:

picture

Figure 1: Java program running process

The above process not only brings advantages to Java programs that other programming languages ​​do not have, such as cross-platform, easy to use, etc. But it also brings some performance problems to Java programs, such as slow startup speed and high memory usage during runtime.

Cold start problem

The detailed process of starting and running the Java program in Figure 1 is shown in Figure 2 below:

picture

*Figure 2: Analysis of the startup process of Java program [ 1] *

The startup process of a Java application first needs to load the JVM virtual machine software program corresponding to the application into the memory, as shown in the red part of the above figure. The JVM virtual machine then loads the corresponding application into the memory. This process corresponds to the light blue Class Load (CL) part in the figure above. During the class loading process, the application will begin to be interpreted and executed, corresponding to the light green part in the above figure. Explain the execution process. The JVM recycles garbage objects, corresponding to the yellow part in the above figure. As the program runs deeper, the JVM will use just-in-time compilation (Just In Time, JIT) technology to compile and optimize code with a high execution frequency in order to improve the running speed of the application. The JIT process corresponds to the white part in the figure above. The code after JIT compilation and optimization corresponds to the dark green part in the figure.

After the above analysis, it is not difficult to see that a Java program will go through several stages of VM init, App init and App active from startup to being dynamically compiled and optimized by JIT. Compared with some other compiled languages, its cold start problem is more serious.

High memory usage problem at runtime

In addition to the cold start problem, it is not difficult to see from the above analysis that when a Java program is running, it first needs to load a JVM virtual machine without doing anything. This operation generally takes up a certain amount of memory. In addition, because the Java program first interprets and executes the bytecode, and then performs JIT compilation and optimization.

Because compared to some compiled languages, the compilation and optimization actions are postponed to the runtime, it is very easy to actually load much more code than the code that actually needs to be run, resulting in some invalid memory usage. To sum up, these are the main reasons why many people often criticize the high memory usage of Java programs.

Lighter Java programs

static compilation technology

Since the traditional Java program running method that first interprets and executes and then dynamically compiles has many of the above problems, is there any way to make Java programs, like other programming languages, such as C/C++, compile first and then execute to solve the above problems?

The answer is yes. Ahead-of-Time Compilation (AOT Compilation) or static compilation has been proposed very early in the Java field. The core idea is to advance the compilation phase of Java programs to before program startup, and then perform code compilation and optimization during the compilation phase to maximize program startup, eliminate cold starts, and reduce runtime memory overhead.

There are many implementation technologies for static compilation in the Java field, the most representative of which is the GraalVM open source high-performance multi-language runtime platform launched by Oracle [ 2] . Some readers who see this may ask: "What is a high-performance multi-language runtime platform? What does it have to do with static compilation itself?".

picture

Figure 3: GraalVM polyglot runtime platform

As shown in Figure 3 above, GraalVM provides a Truffle interpreter implementation framework, allowing developers to use the API provided by Truffle to quickly implement an interpreter for a specific language, so that programs written in various programming languages ​​​​in the figure above can be compiled. The effect of running, thus becoming a multi-language runtime platform. GraalVM's compiler that implements static compilation capabilities is GraalVM JIT Compiler. The static compilation framework and runtime are implemented by the Substrate VM sub-project and are compatible with the OpenJDK runtime implementation. It provides exception handling, synchronous scheduling, thread management, memory management and other functions when the native mirror program is running.

Therefore, GraalVM can not only serve as a multi-language runtime platform, but also can be used to statically compile Java programs thanks to the GraalVM JIT Compiler static compiler provided in it.

After talking about the relationship between static compilation and GraalVM, some readers may be curious, what are the differences between static compilation based on GraalVM and conventional JVM interpretation and execution methods? Compared with the currently widely used JVM runtime compiled Java program, the difference from code writing to compilation and execution of Java programs based on static compilation is as shown in Figure 4 below:

picture

Figure 4: Comparison between static compilation and traditional JVM running process

Compared with the JVM runtime method, static compilation will first parse and compile the program before running, and then generate a native image executable file that is closely related to the runtime environment. Finally, execute the file directly to start the program for execution.

At this point, some readers may be curious, what exactly will the static compilation process in Figure 4 above do to the Java program? How to solve the garbage collection problem of statically compiled executable programs? As shown in Figure 5 below, it describes the input and output contents of the compilation process in the implementation of GraalVM static compilation technology.

picture

Figure 5: Static compilation input and output

The first three input contents on the left in Figure 5: Application, Libraries and JDK are the three necessary parts for compiling and running a Java program. Needless to say. Substrate VM is the core part of GraalVM that implements static compilation, and plays an important role in the entire static compilation process.

During the static analysis process, as drawn in the middle part of Figure 5 above, Substrate VM performs static analysis on the application through context-insensitive Points-to Analysis, which can perform static analysis on the application without running the program. Based on the source program analysis, a list of all possible reachable functions is given and then used as input to the subsequent compilation stage to statically compile the program. Due to the limitations of static analysis, this process cannot cover dynamic features such as reflection, dynamic proxy, and JNI calls in Java. This has also caused many Java frameworks to use a large number of the above features in the implementation process. Therefore, it is difficult to complete static analysis of all their own codes directly based on Substrate VM, and requires additional external configuration [3] to solve static analysis . its own shortcomings.

For example, the Spring community has developed AOT Engine [ 4] as shown in Figure 6 below to help solve the problem. The Spring project performs static analysis on reflection, dynamic proxy and other contents and converts them into content that can be recognized by Substrate VM during the compilation phase. , ensuring that Spring applications can be successfully statically compiled based on Substrate VM.

picture

Figure 6: Spring AOT Engine

After the static analysis is completed, based on the reachable function list of the static analysis results, the GraalVM JIT Compiler in GraalVM introduced above is called to compile the application into local code that is strongly related to the target platform to complete the compilation process.

After compilation is completed, it will enter the Native executable file generation stage on the right side of Figure 5 above. During this process, Substrate VM will save the content determined and initialized in the static compilation phase, as well as the data in the Substrate VM runtime and JDK library, into the Image Heap of the final executable file. The Substrate VM runtime provides the final executable file with the garbage collection, exception handling and other capabilities required during operation. For garbage collection, only Serial GC was provided in the initial GraalVM Community Edition. The more capable G1 GC is provided in the Enterprise Edition. However, in the latest community version, the GraalVM team also introduced G1 GC [ 5] to provide developers with more powerful static compilation capabilities.

Adapt to GraalVM static compilation

In the previous section, after briefly introducing static compilation technology and its own limitations, many external community developers may wonder at this time, how can a Java open source project quickly perform static compilation adaptation? For this problem, in fact, the core and essential problem to be solved is to convert dynamic content that cannot be recognized and processed by GraalVM in the open source framework into identifiable content. Therefore, since this problem is different in different frameworks, the solution will also have some differences. For example, in Spring, the AOT Engine developed for its own framework can solve the problem that the initialization process of registered classes provided by its framework through @Configuration annotation cannot be recognized in the static compilation stage, and generate dynamic agents in the static compilation stage in advance that are originally generated during the run stage. This class solves the problem that direct static compilation proxy classes cannot be effectively generated [ 6] to achieve static compilation adaptation of Spring applications.

For many open source frameworks implemented based on Spring, if the dynamic characteristics that cannot be recognized by GraalVM are caused by the Spring standard usage, since they belong to the Spring system, the static compilation process will definitely require the participation of the Spring AOT Engine. Therefore, , the framework itself does not need to provide any adaptation to have static compilation capabilities.

For non-Spring system projects or if you use some native reflection or other Java dynamic features in the JDK, you need to provide the corresponding static configuration file in the project for the dynamic usage of Java in your own code so that the compiler can identify it during the static compilation process. The dynamic characteristics of the project must be compiled and constructed to achieve smooth compilation and execution of the project. In response to this situation, GraalVM provides a Tracing Agent called native-image-agent to help you collect metadata and prepare configuration files more conveniently. The Agent automatically collects dynamic feature usage from applications running on regular Java VMs and converts them into configuration files that GraalVM can recognize. Finally, store the dynamic configuration file of the framework itself generated by Agent in the project's META-INF/native-image/<group.id>/<artifact.id> directory, and you can use these configurations during the static compilation process. Content,identifying dynamic features in project packages.

All middleware clients included in the Spring Cloud Alibaba 2022.0.0.0 version have now completed the adaptation for building GraalVM native applications. Due to the specific nature of the project itself, there are a large number of dynamic feature usages that cannot be recognized by GraalVM due to Spring syntax in the overall implementation of the project. This content is directly solved by Spring AOT Engine, and the community does not do any additional adaptation work.

In addition to the Spring system syntax, the project itself also has some other Java dynamic usages. This community uses native-image-agent to perform parsing and dynamic configuration generation.

Build microservices based on static compilation

All middleware clients included in Spring Cloud Alibaba 2022.0.0.0 version have completed the adaptation for building GraalVM native applications. Provides users with out-of-the-box static compilation capabilities. The related function experience process is as follows:

Environmental preparation

First you need to install the GraalVM distribution on your machine. You can download it manually on the Liberica Native Image Kit page or use a download manager like SDKMAN!. The demonstration environment of this article is MacOS. If it is Windows, please refer to the corresponding document [ 7] for operation. Execute the following command to install the GraalVM environment:

$ sdk install java 22.3.r17-nik
$ sdk use java 22.3.r17-nik

Verify that the correct version is configured by checking the output of java -version:

$ java -version
openjdk version "17.0.5" 2022-10-18 LTS
OpenJDK Runtime Environment GraalVM 22.3.0 (build 17.0.5+8-LTS)
OpenJDK 64-Bit Server VM GraalVM 22.3.0 (build 17.0.5+8-LTS, mixed mode)

app build

To use GraalVM's static compilation capability to build microservices, first ensure that the Spring Boot version of your project is 3.0.0 or above and the Spring Cloud version is 2022.0.0 or above. Then introduce the required module dependencies of Spring Cloud Alibaba 2022.0.0.0 version into the project.

Use the following command to generate the Hints configuration file required for reflection, serialization and dynamic proxy in the application, provided that the spring-boot-starter-parent parent module is introduced in the application:

$ mvn -Pnative spring-boot:run

After that, the application will be started and pre-executed. All functions of the application need to be tested as completely as possible to ensure that most of the application code is covered by test cases. This process will be based on GraalVM's native-image-agent to collect dynamic characteristics in the program. , which ensures that all necessary dynamic attributes during application running are completely generated. After running all test cases, we found that the following hints files will be generated in the resource/META-INF/native-image directory:

  • resource-config.json: resource hint file in the application
  • reflect-config.json: Reflection definition hint file in the application
  • serialization-config.json: serialization content hint file in the application
  • proxy-config.json: Java proxy related content hint file in the application
  • jni-config.json: Java Native Interface (JNI) content hint file in the application

Note: All core modules of the official version of Spring Cloud Alibaba 2022.0.0.0 have by default included the configuration content required for the dynamic characteristics of their own components in their dependencies. Therefore, the above pre-execution process is mainly to scan the application's own business code and other third-party The dynamic features in the third-party package ensure that the subsequent static compilation process can proceed smoothly and the application can start normally.

static compilation

After the above steps are ready, run the following command to build the native image:

$ mvn -Pnative native:compile

After successful execution, we can see the generated executable file in the /target directory.

Program running

It is no different from an ordinary executable file. When you start the application through target/xxx, you can observe output similar to the following:

2023-08-01T17:21:21.006+08:00  INFO 65431 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-08-01T17:21:21.008+08:00  INFO 65431 --- [           main] c.a.cloud.imports.examples.Application   : Started Application in 0.553 seconds (process running for 0.562)

With the new version of Spring Cloud Alibaba application using GraalVM static compilation technology, all core capabilities have been significantly improved in terms of startup speed and memory usage, as shown in the table below.

picture

Note: The above test code sample comes from the examples module in the Spring Cloud Alibaba project, 4c16g Mac environment, each set of data is tested 3 times and averaged. The specific data may vary depending on the machine.

Other community dynamics

New official website online

From the open source in 2018 to the release of the first GA version in 2019, the community has gone through 5 years. The project has released 35 versions and the cumulative number of stars exceeds 26k. Spring Cloud Alibaba microservice solutions are widely used in various industries. In the process of digital transformation in various industries.

Since the Spring Cloud Alibaba project is a standard-based Spring Cloud microservice solution jointly built with Spring, it is implemented as a set of microservice technology solutions that incorporate Alibaba's decades of experience in microservice technology. The early documentation and other related content of the project have always been microservices on the spring.io official website. But slowly, the community also discovered that due to the inability to add Chinese content in spring.io, there were too many projects, resulting in problems such as less project or community content that a single project could carry.

Therefore, the community communicated with Spring officials, and with their support, based on the habits of domestic developers, the community's new official website was officially launched. The relevant domain name is: sca.aliyun.com, where sca is the acronym of the project's Spring Cloud Alibaba.

picture

New Committer Introduction

The Spring Cloud Alibaba community has seen a number of external contributors actively participating in community maintenance iterations in recent months. I would like to express my gratitude to them! In addition, for Liu Ziming, who has been participating in community activities and making important contributions to the feature, the community officially nominated him as a community committer in accordance with the new committer nomination and voting system and voted for it. He was successfully elected. I would like to express my congratulations to him! More external students are welcome to pay attention to the Spring Cloud Alibaba open source community and contribute to the open source community.

picture

Related Links:

[1] Analysis of the startup process of Java programs
https://shipilev.net/talks/j1-Oct2011-21682-benchmarking.pdf

[2] GraalVM open source high-performance multi-language runtime platform https://www.oracle.com/java/graalvm/

[3] External placement https://www.graalvm.org/latest/reference-manual/native-image/metadata/

[4] AOT Engine
https://spring.io/blog/2021/12/09/new-aot-engine-brings-spring-native-to-the-next-level

[5] G1 GC
https://medium.com/graalvm/a-new-graalvm-release-and-new-free-license-4aab483692f5

[6] Issues such as dynamic proxy classes
https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.introducing-graalvm-native-images.understanding- aot-processing

[7] Corresponding document
https://medium.com/graalvm/using-graalvm-and-native-image-on-windows-10-9954dc071311

Guess you like

Origin blog.csdn.net/alisystemsoftware/article/details/132476577