GraalVM compiles Java applications into standalone Native Image binaries, eliminating cold start delays and reducing runtime memory usage. Because Native Images bypass the JVM, traditional Java agents that modify bytecode at runtime cannot work.
Application Real-Time Monitoring Service (ARMS) addresses this with static instrumentation: the bytecode modifications normally applied at runtime are instead applied during the static compilation step. The result is a Native Image binary with built-in tracing and metric collection -- no runtime agent required.
GraalVM support in ARMS is a cutting-edge feature. Deploy to a test environment first. For technical support, join the DingTalk group (ID: 80805000690).
Limitations
Your application must be compatible with static compilation. For Spring Boot applications, see the Spring Boot Native Image guide.
Static compilation must use the GraalVM JDK provided by ARMS.
GraalVM has specific environment requirements for static compilation. See the GraalVM prerequisites.
ARMS provides tracing and metric monitoring for GraalVM applications. The following features are not supported:
Arthas diagnostics
Continuous diagnostics
Memory snapshots
JVM monitoring data for metadata details, non-heap memory, and direct buffer is unavailable because GraalVM Native Images use a different memory model than standard JVM applications.
How it works
The integration follows five steps:
Install dependencies -- Download the ARMS agent, the ARMS-provided GraalVM JDK, and Maven.
Add Maven dependencies -- Add the ARMS native agent dependency and the native-maven-plugin to your
pom.xml.Add the access filter file -- Create
access-filter-file.jsonto exclude JVM startup classes from reflection collection.Pre-execute the application -- Run the application with the ARMS agent attached so the agent can collect the dynamic configuration needed for static compilation.
Compile the Native Image -- Build the statically compiled binary and run it.
Prerequisites
Before you begin, make sure that you have:
A Linux environment that meets the GraalVM native-image prerequisites
A Java application configured for GraalVM static compilation
An ARMS LicenseKey (retrieve it by calling the DescribeTraceLicenseKey API operation)
Step 1: Install dependencies
Download the ARMS agent
Download the ARMS agent package for the region where your application runs.
| Region | Public endpoint | VPC endpoint |
|---|---|---|
| China (Hangzhou) | wget "http://arms-apm-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip | wget "http://arms-apm-cn-hangzhou.oss-cn-hangzhou-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip |
| China (Shanghai) | wget "http://arms-apm-cn-shanghai.oss-cn-shanghai.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip | wget "http://arms-apm-cn-shanghai.oss-cn-shanghai-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip |
| China (Beijing) | wget "http://arms-apm-cn-beijing.oss-cn-beijing.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip | wget "http://arms-apm-cn-beijing.oss-cn-beijing-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip |
| China (Zhangjiakou) | wget "http://arms-apm-cn-zhangjiakou.oss-cn-zhangjiakou.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip | wget "http://arms-apm-cn-zhangjiakou.oss-cn-zhangjiakou-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip |
| China (Shenzhen) | wget "http://arms-apm-cn-shenzhen.oss-cn-shenzhen.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip | wget "http://arms-apm-cn-shenzhen.oss-cn-shenzhen-internal.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zip |
For technical support in other regions, join the DingTalk group (ID: 80805000690).
Extract the package and install the agent:
unzip ArmsAgentNative.zip
cd ArmsAgentNative
sh install.shDownload the ARMS GraalVM JDK
Download graalvm-java17-23.0.4-ali-1.2b.tar.gz, extract it, and verify the installation:
tar -xzf graalvm-java17-23.0.4-ali-1.2b.tar.gz
graalvm-java17-23.0.4-ali-1.2b/bin/native-image --versionA successful installation returns output similar to the following:

Install Maven (optional)
Skip this step if Maven is already installed. Download apache-maven-3.8.4-bin.tar.gz and extract it.
Set environment variables
Set JAVA_HOME to the ARMS GraalVM JDK and MAVEN_HOME to your Maven installation. Replace <graalvm-path> and <maven-path> with the actual directory paths.
export JAVA_HOME=<graalvm-path>/graalvm-java17-23.0.4-ali-1.2b
export PATH=$PATH:$JAVA_HOME/bin
export MAVEN_HOME=<maven-path>/apache-maven-3.8.4
export PATH=$PATH:$MAVEN_HOME/binStep 2: Add Maven dependencies
Add the ARMS native agent dependency and the native-maven-plugin configuration to your pom.xml. Replace <dynamic-configs-path> with the absolute path to your application's dynamic configuration directory.
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>arms-javaagent-native</artifactId>
<version>4.1.11</version>
<type>pom</type>
</dependency>
</dependencies>
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<fallback>false</fallback>
<buildArgs>
<arg>-H:ConfigurationFileDirectories=native-configs,<dynamic-configs-path></arg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>| Placeholder | Description | Example |
|---|---|---|
<dynamic-configs-path> | Absolute path to the dynamic configuration directory collected during pre-execution | /opt/app/dynamic-configs |
Step 3: Add the access filter file
Create a file named access-filter-file.json in your application's root directory with the following content:
{ "rules": [
{"excludeClasses": "sun.launcher.LauncherHelper"}
]
}This file prevents the GraalVM agent from collecting reflection metadata for sun.launcher.LauncherHelper -- a JVM startup class whose reflections are unnecessary for Native Images. Without this filter, the collected reflections cause static compilation errors.
Step 4: Pre-execute the application
Before static compilation, the ARMS agent must run alongside your application to collect dynamic configuration. During pre-execution, the agent instruments bytecode and records the modifications that will later be compiled into the Native Image.
All core code paths must be exercised during pre-execution. Declare all RESTful endpoints in the run script so the agent can trigger and instrument each one.
Open the run script and configure the following parameters:
Placeholder Description Example <your-license-key>ARMS LicenseKey abl3f2xxx<your-app-name>Application name in ARMS graalvm-demo<endpoint-1>...RESTful API paths to exercise /api/health /api/users /api/orders<your-port>Application HTTP port 8080<target-path>Output path of the compiled Native Image target/graalvm-demo<java-command>Full java startup command -javaagent:./arms-native/aliyun-java-agent-native.jar -jar target/graalvm-demo-1.0.0.jar######## Configure these parameters # ARMS license key. Get it by calling the DescribeTraceLicenseKey API operation. export ARMS_LICENSEKEY=<your-license-key> # Application name. In a distributed architecture, multiple peer instances share the same name. export ARMS_APPNAME=<your-app-name> # Space-separated list of RESTful endpoints to call during pre-execution. export PS=(<endpoint-1> <endpoint-2> <endpoint-3>) # Application port. export PORT=<your-port> # Path to the Native Image binary in the target directory. export NATIVE_IMAGE_FILE=<target-path> # Java command to start the application with the ARMS agent. export JAVA_CMD=<java-command> ########Start pre-execution to collect the static compilation configuration:
sh ArmsAgentNative/run.sh --collect --jvm --Carms
Step 5: Build and run the Native Image
Run the static compilation:
mvn -Pnative packageStart the compiled application:
sh ArmsAgentNative/run.sh --native --Carms
After the application starts, trace data and metrics are automatically reported to ARMS. Log in to the ARMS console to verify that your application appears and trace data is being collected.
Build a Docker image
A statically compiled Native Image binary is self-contained -- it includes all runtime dependencies, including the JDK. Place the binary directly in a Docker image as an executable.
Replace <your-license-key> and <your-app-name> with your ARMS LicenseKey and application name.
FROM centos:latest
WORKDIR /app
COPY ./target/graalvm-demo /app
CMD ["/app/graalvm-demo","-Darms.licenseKey=<your-license-key>","-Darms.appName=<your-app-name>"]Compress a Native Image with UPX
When comparing the sizes of a Native Image and a Java program, the JDK is generally considered part of the Java program because Java programs require JDK support, whereas Native Images are self-contained and already include all dependencies.
However, as an application grows, its Native Image binary can become larger than the combined size of the original JAR and JDK. This happens because assembly code (in Native Images) has lower information density than Java bytecode -- more code is needed to express the same logic. This increases pressure on deployment and transmission.
UPX compresses executable binaries into smaller executables that run directly without manual decompression, with minimal impact on runtime performance.
Example: The statically compiled graalvm-demo file is compressed into graalvm-demo-compressed. A Spring Boot + RocketMQ Fat JAR of 216 MB produces a Native Image that compresses to 47 MB (28.4% of the uncompressed size) with UPX.


Use UPX
Download and extract UPX.
Run the compression command:
Parameter Description -9Compression level (1--9). Higher values produce smaller files but take longer. -o <output-file>Path for the compressed output binary. <input-file>Path to the original Native Image binary. $UPX_HOME/upx -9 -o <output-file> <input-file>