All Products
Search
Document Center

Application Real-Time Monitoring Service:Connect a GraalVM application to ARMS

Last Updated:Jun 21, 2026

GraalVM technology uses static compilation for Java applications to help solve issues such as slow cold starts and high runtime memory usage. For GraalVM applications, ARMS provides a static instrumentation solution. This solution moves the bytecode modification, typically performed at runtime by a Java agent, into the static compilation phase. This process, known as static enhancement, provides out-of-the-box observability.

Important

GraalVM is a cutting-edge technology. If you have not used it in a production environment, we recommend that you thoroughly validate it in a test environment before you consider production use. Additionally, if you have any questions when you use this solution to integrate with ARMS, feel free to contact us through the DingTalk support group (Group number: 80805000690).

Limitations

  • Your application must be adapted for GraalVM static compilation. For Spring Boot applications, refer to the relevant documentation for instructions.

  • Use the specific GraalVM JDK version provided by ARMS.

  • GraalVM static compilation has specific environment prerequisites. For more information, see the official GraalVM documentation.

  • For GraalVM applications, ARMS currently supports only the core Traces and Metrics features. Features such as Arthas, Continuous Profiling, and memory snapshots are not supported. In addition, the memory structure of a GraalVM application differs from a standard JVM application. Therefore, JVM monitoring shows no data for Metaspace, non-heap memory, and direct buffers.

Procedure

Step 1: Install dependencies

In a GraalVM environment, first install the following dependencies:

  1. Download the ARMS agent for GraalVM that corresponds to your application's region.

    Currently, only the following regions are supported. To request support for other regions, contact us in the DingTalk support group (ID: 80805000690).

    Region

    Public URL

    VPC address

    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

    After you decompress the file, navigate to the ArmsAgentNative directory and run the following command to install the agent locally.

    sh install.sh
  2. Download the ARMS-enhanced GraalVM JDK version with observability capabilities: graalvm-java17-23.0.4-ali-1.2b.tar.gz.

    After you decompress the file, run the following command in the directory:

    graalvm-java17-23.0.4-ali-1.2b/bin/native-image --version

    A successful installation returns the following output:

    native-image 17.0.9 2023-10-17 ali-1.2b
    OpenJDK Runtime Environment GraalVM CE 17.0.9-dev+9.1 (build 17.0.9+9-jvmci-23.0-b22)
    OpenJDK 64-Bit Server VM GraalVM CE 17.0.9-dev+9.1 (build 17.0.9+9-jvmci-23.0-b22, mixed mode, sharing)
  3. Download Maven if it is not already installed in your environment: apache-maven-3.8.4-bin.tar.gz.

    After you decompress the file, set the JAVA_HOME and MAVEN_HOME environment variables to their respective paths.

    Replace /xxx/ with the actual path.

    export MAVEN_HOME=/xxx/apache-maven-3.8.4
    export PATH=$PATH:$MAVEN_HOME/bin
    export JAVA_HOME=/xxx/graalvm-java17-23.0.4-ali-1.2b
    export PATH=$PATH:$JAVA_HOME/bin

Step 2: Add dependencies

Add the following dependency to your application:

In the code, replace /xxx/dynamic-configs with the path to your application's original dynamic configuration files.

<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,/xxx/dynamic-configs</arg>
            </buildArgs>
          </configuration>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

Step 3: Add the access-filter-file.json file

In your application's root directory, create a file named access-filter-file.json with the following content:

{ "rules": [
  {"excludeClasses": "sun.launcher.LauncherHelper"}
]
}

The access-filter-file.json file ensures that the agent that GraalVM uses to collect information about dynamic features does not collect reflection data from sun.launcher.LauncherHelper. The sun.launcher.LauncherHelper class is used during JVM startup, and a statically compiled Native Image does not require its reflection data. Including this data can cause compilation errors.

Step 4: Pre-run the application

To ensure that the dynamic enhancement code from the ARMS agent is compiled into the final Native Image, you must first run the application with the agent attached. This pre-run step allows the agent to collect configuration data by running the core code paths of your application. A script is provided to help with this process. Declare all your application's RESTful endpoints in the script to trigger the business logic by calling the relevant interfaces during execution.

  1. Use the following script as a reference and update the parameters based on the comments.

    ######## Modify the following parameters based on your environment.
    # ARMS connection parameters. You can obtain the LicenseKey by calling the DescribeTraceLicenseKey operation.
    # The AppName parameter specifies the application name in ARMS. In a distributed architecture, a single application can have multiple peer instances.
    export ARMS_LICENSEKEY=
    export ARMS_APPNAME=
    # List of application interfaces. Example: PS=(interface1 interface2 interface3 interface4)
    export PS=
    # Application port. Example: PORT="8080"
    export PORT=
    # Path to the Native Image file. After static compilation, a native image file is generated in the application's target directory.
    # Example: NATIVE_IMAGE_FILE="target/graalvm-demo"
    export NATIVE_IMAGE_FILE=
    # Command to run the ARMS Native Agent. Example: JAVA_CMD="-javaagent:./arms-native/aliyun-java-agent-native.jar -jar target/graalvm-demo-1.0.0.jar"
    export JAVA_CMD=
    ########
  2. Attach the ARMS Java agent to start the pre-run and collect the static compilation configuration.

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

Step 5: Statically compile the application

After you add the dependency, follow these steps to statically compile your application:

  1. Start the static compilation.

    mvn -Pnative package
  2. Run the statically compiled project.

    sh ArmsAgentNative/run.sh --native --Carms

More operations

Build a Docker image

To build a Docker image for your statically compiled GraalVM application, you can treat the final Native Image file as a self-contained executable. Because the file already includes all necessary runtime information, you can copy it into the image and build it like any other executable.

Sample Dockerfile:

Note

Replace the values for the -Darms.licenseKey and -Darms.appName parameters with your actual license key and application name.

FROM centos:latest
WORKDIR /app
COPY ./target/graalvm-demo /app
CMD ["/app/graalvm-demo","-Darms.licenseKey=xxx","-Darms.appName=xxx"]

Compress the Native Image

When comparing the disk space of a Native Image to a traditional Java application, the Java application's size typically includes the JDK. This is because a Java application requires a JDK to run, whereas a Native Image is a self-contained executable that includes all its dependencies.

However, a Native Image consists of assembly code, which is less dense than Java bytecode. This means that a Native Image requires more space to express the same logic. As an application grows, the size of its Native Image can exceed the combined size of the Java application and its JDK, which increases deployment and transfer overhead. To mitigate this issue, you can use a tool like UPX to reduce the size of the Native Image. UPX compresses a binary executable into a smaller one that can be run directly without decompression and has minimal impact on runtime performance.

The following output shows the effect of compressing a Native Image:

$~/tools/upx-4.2.4-amd64_linux/upx -9 -o graalvm-demo-compressed graalvm-demo
                        Ultimate Packer for eXecutables
                           Copyright (C) 1996 - 2024
UPX 4.2.4       Markus Oberhumer, Laszlo Molnar & John Reiser   May 9th 2024
        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
 172061328 ->  48870860   28.40%   linux/amd64   graalvm-demo-compressed
Packed 1 file.

The original graalvm-demo file is compressed into the graalvm-demo-compressed file. The compressed file is only 28.4% of the original size.

Comparison with a Fat Jar:

$ls -lh
total 429M
drwxr-xr-x 5 cengfeng.lzy users 4.0K Aug 16 10:20 classes
drwxr-xr-x 3 cengfeng.lzy users 4.0K Aug 16 10:19 generated-sources
drwxr-xr-x 3 cengfeng.lzy users 4.0K Aug 16 10:19 generated-test-sources
-rwxr-xr-x 1 cengfeng.lzy users 165M Aug 16 10:22 graalvm-demo
-rw-r--r-- 1 cengfeng.lzy users 216M Aug 16 10:20 graalvm-demo-1.0.0.jar
-rwxr-xr-x 1 cengfeng.lzy users  47M Aug 16 10:22 graalvm-demo-compressed
drwxr-xr-x 3 cengfeng.lzy users 4.0K Aug 16 10:20 graalvm-reachability-metadata
-rw-r--r-- 1 cengfeng.lzy users  38K Aug 16 10:22 libawt_headless.so
-rw-r--r-- 1 cengfeng.lzy users 879K Aug 16 10:22 libawt.so
-rw-r--r-- 1 cengfeng.lzy users 463K Aug 16 10:22 libawt_xawt.so
-rwxr-xr-x 1 cengfeng.lzy users 7.2K Aug 16 10:22 libjava.so
-rwxr-xr-x 1 cengfeng.lzy users 7.2K Aug 16 10:22 libjvm.so
drwxr-xr-x 2 cengfeng.lzy users 4.0K Aug 16 10:19 maven-archiver
drwxr-xr-x 3 cengfeng.lzy users 4.0K Aug 16 10:19 maven-status
drwxr-xr-x 3 cengfeng.lzy users 4.0K Aug 16 10:20 spring-aot
drwxr-xr-x 2 cengfeng.lzy users 4.0K Aug 16 10:19 test-classes

The original Fat Jar, which includes all dependencies such as Spring Boot and RocketMQ, is 216 MB. The compressed Native Image is only 47 MB.

How to use UPX:

  1. Download and decompress the UPX tool.

  2. Assuming UPX is decompressed to the $UPX_HOME directory, run the following command to compress a file.

    $UPX_HOME/upx -9 -o path/to/output-file path/to/original-file

    • -9: Specifies the compression level, from 1 to 9. A higher value yields a better compression ratio but takes longer.

    • -o path/to/output-file: Specifies the path for the output file. Replace path/to/output-file with your desired file path.

    • path/to/original-file: Specifies the path to the file to compress. Replace path/to/original-file with the actual file path.