All Products
Search
Document Center

Application Real-Time Monitoring Service:Integrate a GraalVM application into ARMS

Last Updated:Mar 11, 2026

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.

Important

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:

  1. Install dependencies -- Download the ARMS agent, the ARMS-provided GraalVM JDK, and Maven.

  2. Add Maven dependencies -- Add the ARMS native agent dependency and the native-maven-plugin to your pom.xml.

  3. Add the access filter file -- Create access-filter-file.json to exclude JVM startup classes from reflection collection.

  4. Pre-execute the application -- Run the application with the ARMS agent attached so the agent can collect the dynamic configuration needed for static compilation.

  5. Compile the Native Image -- Build the statically compiled binary and run it.

Prerequisites

Before you begin, make sure that you have:

Step 1: Install dependencies

Download the ARMS agent

Download the ARMS agent package for the region where your application runs.

RegionPublic endpointVPC endpoint
China (Hangzhou)wget "http://arms-apm-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/ArmsAgentNative.zip" -O ArmsAgentNative.zipwget "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.zipwget "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.zipwget "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.zipwget "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.zipwget "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.sh

Download 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 --version

A successful installation returns output similar to the following:

native-image version output

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/bin

Step 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>
PlaceholderDescriptionExample
<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.

Important

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.

  1. Open the run script and configure the following parameters:

    PlaceholderDescriptionExample
    <your-license-key>ARMS LicenseKeyabl3f2xxx
    <your-app-name>Application name in ARMSgraalvm-demo
    <endpoint-1> ...RESTful API paths to exercise/api/health /api/users /api/orders
    <your-port>Application HTTP port8080
    <target-path>Output path of the compiled Native Imagetarget/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>
    ########
  2. Start pre-execution to collect the static compilation configuration:

    sh ArmsAgentNative/run.sh --collect --jvm --Carms

Step 5: Build and run the Native Image

  1. Run the static compilation:

    mvn -Pnative package
  2. Start 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.

UPX compression exampleSize comparison: Native Image, compressed Native Image, and Fat JAR

Use UPX

  1. Download and extract UPX.

  2. Run the compression command:

    ParameterDescription
    -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>