AWS Developer Tools Blog
GraalVM Native Image Support in the AWS SDK for Java 2.x
We are excited to announce that AWS SDK for Java 2.x (version 2.16.1 or later) now has out-of-the-box support for GraalVM Native Image compilation.
GraalVM is a universal virtual machine that supports JVM-based languages (e.g. Java, Scala, Kotlin), dynamic languages (e.g. Python, JavaScript), and LLVM-based languages (e.g. C, C++). GraalVM Native Image is one of the most popular features that GraalVM offers. It allows Ahead-of-Time (AoT) compilation of Java programs into a self-contained native executable, called a native image. The executable is optimized and contains everything needed to run the application, and it has faster startup time and smaller heap memory footprint compared with a JVM. Therefore, native images are well-suited for serverless applications where startup latency is critical.
We compared the startup performance of the executable created from GraalVM native image with the same application running on JVM. The test application creates an S3Client with ApacheHttpClient and sends a simple GetObjectRequest. In our testing, we measured the SDK 2.x startup time and the first request latency as well as the memory usage. The results indicate that the native image has significantly faster startup and lower memory consumption.
Due to the nature of AoT, features that rely on dynamic class loading, proxies, or reflections need to be known at build time and require special support. For example, the classes use reflections need to be registered in the native image configuration files. The latest Java SDK 2.x contains such configurations required for SDK classes, and it’s ready to be built into a native image.
The latest release also includes a new Maven Archetype, archetype-app-quickstart
, that enables you to quickly bootstrap a native-image compatible Java application configured with the AWS SDK for Java 2.x as a dependency. In this blog, we will show you how to use the archetype to create a new application with GraalVM Native image support.
Getting Started
Prerequisites
This post assumes you have some familiarity with Java programming, the Maven build system, and the GraalVM runtime. To use this tutorial, you need to have the following software installed:
Java 8+
Apache Maven
GraalVM 21.0.0+
GraalVM Native Image
Generating the application
To generate a project, open a terminal (command line) window, run mvn archetype:generate with archetype-app-quickstart
. The version of the archetype is the same as the Java SDK 2.x version, and you can use the latest version. There are two modes you can choose: interactive mode and batch mode.
- Interactive mode
In interactive mode, you’ll be prompted to provide required properties, one at a time.
mvn archetype:generate \ -DarchetypeGroupId=software.amazon.awssdk \ -DarchetypeArtifactId=archetype-app-quickstart \ -DarchetypeVersion=2.16.1
- Batch mode
In batch mode, you need to provide all required properties at once. Below is the list of the required properties and you can check out all available options here.
Required Properties
-
- service: the service client to be used in the lambda function, eg: s3, dynamodb. Only one service should be provided.
- groupId: the Maven group ID of the application
- artifactId: the Maven artifact ID of you application
- httpClient: specifies the http client to be used by the SDK client. Available options are
url-connection-client
(sync),apache-client
(sync) andnetty-nio-client
(async). - nativeImage: specifies whether GraalVM Native Image configuration should be included
mvn archetype:generate \ -DarchetypeGroupId=software.amazon.awssdk \ -DarchetypeArtifactId=archetype-app-quickstart \ -DarchetypeVersion=2.16.1 \ -DgroupId=com.test \ -DartifactId=sample-project \ -Dservice=s3 \ -DhttpClient=apache-client \ -DnativeImage=true \ -Dregion=us-west-2 \ -DinteractiveMode=false
The generated application has a dependency of AWS SDK for Java 2.16.1. The output will be something like this
[INFO] ---------------------------------------------------------------------------- [INFO] Using following parameters for creating project from Archetype: archetype-app-quickstart:2.16.1 [INFO] ---------------------------------------------------------------------------- [INFO] Parameter: groupId, Value: com.test [INFO] Parameter: artifactId, Value: sample-project [INFO] Parameter: version, Value: 1.0-SNAPSHOT [INFO] Parameter: package, Value: com.test [INFO] Parameter: packageInPathFormat, Value: com/test [INFO] Parameter: service, Value: s3 [INFO] Parameter: version, Value: 1.0-SNAPSHOT [INFO] Parameter: package, Value: com.test [INFO] Parameter: nativeImage, Value: true [INFO] Parameter: httpClient, Value: apache-client [INFO] Parameter: groupId, Value: com.test [INFO] Parameter: javaSdkVersion, Value: 2.16.1 [INFO] Parameter: artifactId, Value: sample-project [INFO] Project created from Archetype in dir: /tmp/sample-project [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.004 s [INFO] Finished at: 2021-02-17T15:07:45-08:00 [INFO] ------------------------------------------------------------------------
When the archetype:generate
goal completes, you will have a Maven project with the structure as follows
sample-project ├── README.md ├── pom.xml └── src ├── main │ ├── java │ │ └── com │ │ └── test │ │ ├── App.java │ │ ├── DependencyFactory.java │ │ └── Handler.java │ └── resources │ └── simplelogger.properties └── test └── java └── com └── test └── HandlerTest.java
The App.java
class is the entry point for the application. The DependencyFactory.java
class contains the configured SDK client.
Building the application
To build the application, run the following commands:
# Go to project directory created. # The name should be the same as the artifactId provided cd sample-project # Build the application mvn clean package
Creating a native executable
You can run the following command to create a native image:
mvn clean package -P native-image
Once it completes, it will generate an executable in the target folder. The name should be the same as the artifactId provided.
-H:ReflectionConfigurationResources=META-INF/native-image/software.amazon.awssdk/s3/reflect-config.json \ -H:EnableURLProtocols=https \ -H:+ReportUnsupportedElementsAtRuntime \ -H:ReflectionConfigurationResources=META-INF/native-image/software.amazon.awssdk/sdk-core/reflect-config.json \ -H:ResourceConfigurationResources=META-INF/native-image/software.amazon.awssdk/sdk-core/resource-config.json \ -H:ReflectionConfigurationResources=META-INF/native-image/software.amazon.awssdk/apache-client/reflect-config.json \ -H:ResourceConfigurationResources=META-INF/native-image/software.amazon.awssdk/apache-client/resource-config.json \ -H:DynamicProxyConfigurationResources=META-INF/native-image/software.amazon.awssdk/apache-client/proxy-config.json \ -H:FallbackThreshold=0 \ -H:ClassInitialization=org.slf4j:build_time \ -H:Class=com.test.App \ -H:Name=sample-project \ -H:CLibraryPath=/Library/Java/JavaVirtualMachines/graalvm-ce-java8-21.0.0/Contents/Home/jre/lib/svm/clibraries/darwin-amd64 \ ] [sample-project:33940] classlist: 6,671.68 ms, 1.36 GB [sample-project:33940] (cap): 4,022.10 ms, 1.36 GB [sample-project:33940] setup: 6,922.21 ms, 1.36 GB [sample-project:33940] (clinit): 1,352.10 ms, 2.80 GB [sample-project:33940] (typeflow): 33,266.42 ms, 2.80 GB [sample-project:33940] (objects): 16,911.48 ms, 2.80 GB [sample-project:33940] (features): 1,717.77 ms, 2.80 GB [sample-project:33940] analysis: 54,700.08 ms, 2.80 GB [sample-project:33940] universe: 4,559.62 ms, 2.81 GB [sample-project:33940] (parse): 13,027.95 ms, 3.06 GB [sample-project:33940] (inline): 9,664.36 ms, 3.70 GB [sample-project:33940] (compile): 73,425.18 ms, 4.75 GB [sample-project:33940] compile: 100,632.59 ms, 4.75 GB [sample-project:33940] image: 10,399.21 ms, 4.82 GB [sample-project:33940] write: 1,834.74 ms, 4.82 GB [sample-project:33940] [total]: 186,280.64 ms, 4.82 GB [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 03:12 min [INFO] Finished at: 2021-02-17T15:12:31-08:00 [INFO] ------------------------------------------------------------------------
Running the native executable
To execute the native image, you can run the following command
target/sample-project
The output should be similar to the following:
2021-02-17 15:13:18:071 -0800 [main] INFO com.test.App - Application starts 2021-02-17 15:13:18:086 -0800 [main] INFO com.test.App - Application ends
Conclusion
In this blog post we showed you the benefits of using native image compiled of applications using the AWS SDK for Java 2x. We also showed you how quick it is to use the new archetype to get start with native-image compatible applications using AWS SDK for Java 2.x. If you want to learn more about the archetype, you can check out the archetype-app-quickstart source code on GitHub. Please give this new archetype a try and let us know what you think via the GitHub issues page.