Spring Boot 3.4 application on AWS Lambda- Part 8 Optimization strategies for the cold and warm starts

Introduction In the previous articles of the series about how to develop, run and optimize Spring Boot 3 application on AWS Lambda with : AWS Serverless Java Container AWS Lambda Web Adapter Spring Cloud Function (and its AWS Adapter) Deployed as a Lambda Docker Container Image Based on GraalVM Native Image deployed as Lambda Custom Runtime We also did Lambda performance (cold and warm starts) measurements with the following settings: Lambda functions used 1024 MB memory Java compilation option "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" and the default tiered compilation have been used for the managed Java Lambda runtime and Lambda Docker Container Image Lambda x86_64 architecture was used Default Apache HTTP Client was used to connect to the DynamoDB In this part of the series we'll introduce some Lambda additional performance (cold and warm starts) optimization techniques that you can apply for your Spring Boot 3 application on AWS Lambda. You'll need to measure the performance by yourself to figure out whether they will provide desired Lambda performance improvements. Optimization strategies for the cold and warm starts To find a good balance between cold and warm start times you can try out the optimization techniques introduced below. I have not done any measurements with the Spring Boot 3 application on AWS Lambda, but have done them using pure Java on AWS Lambda without the usage of any frameworks on top. I'll provide references to my relevant articles. The following approaches can be applied to all approaches: managed Java on Lambda including enabling SnapStart (and priming techniques on top), Lambda Docker Container Image and GraalVM Native Image deployed as Lambda Custom Runtime: Try out different Lambda memory settings. All measurements until now have been performed with 1024 MB memory for the Lambda function. With different memory settings you might become better at the price-performance trade-off. See my article Measuring cold and warm starts and deployment time with Java 21 using different Lambda memory settings for further examples, performance measurements and conclusions. Try out setting Lambda arm64 architecture (which supports SnapStart since July 18, 2024) instead of default one x86 which can provide a better cost-performance trade-off comparing to x86 architecture. See my article AWS Lambda performance with Java 21: x86 vs arm64 - Initial measurements for some insights. Try out different synchronous HTTP clients to establish HTTP connection to the DynamoDB. All measurements until now have been performed with the default synchronous HTTP Client which is Apache. There are other options like UrlConnection and AWS CRT HTTP clients which provide different performance trade offs for the cold and warm starts. See my article Measuring cold and warm starts with Java 21 using different synchronous HTTP clients for further examples, performance measurements and conclusions. GraalVM Native Image also supports AWS CRT HTTP Client and I did some measurements with using pure Java Lambda function iny my article Measuring cold and warm starts with GraalVM 23 and AWS CRT HTTP Client. Explore whether an asynchronous HTTP client for DynamoDB is an option for your use case. The default asynchronous HTTP Client is NettyNio. There is another option AWS CRT async HTTP client which provides different performance trade offs for the cold and warm starts. See my article Measuring cold and warm starts with Java 21 using different asynchronous HTTP clients for further examples, performance measurements and conclusions. The following approaches can be applied primary to the managed Java on Lambda including enabling SnapStart (and priming techniques on top) : Try out different Java compilation options for the Lambda function. All measurements until now have been performed with the compilation option "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" and default tiered compilation for the Lambda function. There are other compilation options that can be provided to the Lambda function using environment variable called JAVA_TOOL_OPTIONS which can have different cold and warm starts trade offs. See my article Measuring cold and warm starts with Java 21 using different compilation options for further examples, performance measurements and conclusions. In case of GraalVM Native Image Java compilation method (and optimizations during runtime) doesn't have much impact on the Lambda performance (well, because it's already compiled as a native image). Further exclude unused dependencies. With that especially cold start times can be reduced (also for SnapStart), see my article Measuring cold starts with Java 21 using different deployment artifact sizes. In case of GraalVM Native Image only reachable Java classes, function and methods will become a part of the Native Image, so including unsued dependencies may not hear that much. Search for the further Lambda SnapStart priming potential

Apr 22, 2025 - 15:34
 0
Spring Boot 3.4 application on AWS Lambda- Part 8 Optimization strategies for the cold and warm starts

Introduction

In the previous articles of the series about how to develop, run and optimize Spring Boot 3 application on AWS Lambda with :

  • AWS Serverless Java Container
  • AWS Lambda Web Adapter
  • Spring Cloud Function (and its AWS Adapter)
  • Deployed as a Lambda Docker Container Image
  • Based on GraalVM Native Image deployed as Lambda Custom Runtime

We also did Lambda performance (cold and warm starts) measurements with the following settings:

  • Lambda functions used 1024 MB memory
  • Java compilation option "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" and the default tiered compilation have been used for the managed Java Lambda runtime and Lambda Docker Container Image
  • Lambda x86_64 architecture was used
  • Default Apache HTTP Client was used to connect to the DynamoDB

In this part of the series we'll introduce some Lambda additional performance (cold and warm starts) optimization techniques that you can apply for your Spring Boot 3 application on AWS Lambda. You'll need to measure the performance by yourself to figure out whether they will provide desired Lambda performance improvements.

Optimization strategies for the cold and warm starts

To find a good balance between cold and warm start times you can try out the optimization techniques introduced below. I have not done any measurements with the Spring Boot 3 application on AWS Lambda, but have done them using pure Java on AWS Lambda without the usage of any frameworks on top. I'll provide references to my relevant articles.

The following approaches can be applied to all approaches: managed Java on Lambda including enabling SnapStart (and priming techniques on top), Lambda Docker Container Image and GraalVM Native Image deployed as Lambda Custom Runtime:

The following approaches can be applied primary to the managed Java on Lambda including enabling SnapStart (and priming techniques on top) :

  • Try out different Java compilation options for the Lambda function. All measurements until now have been performed with the compilation option "-XX:+TieredCompilation -XX:TieredStopAtLevel=1" and default tiered compilation for the Lambda function. There are other compilation options that can be provided to the Lambda function using environment variable called JAVA_TOOL_OPTIONS which can have different cold and warm starts trade offs. See my article Measuring cold and warm starts with Java 21 using different compilation options for further examples, performance measurements and conclusions. In case of GraalVM Native Image Java compilation method (and optimizations during runtime) doesn't have much impact on the Lambda performance (well, because it's already compiled as a native image).
  • Further exclude unused dependencies. With that especially cold start times can be reduced (also for SnapStart), see my article Measuring cold starts with Java 21 using different deployment artifact sizes. In case of GraalVM Native Image only reachable Java classes, function and methods will become a part of the Native Image, so including unsued dependencies may not hear that much.
  • Search for the further Lambda SnapStart priming potentials in addition to one introduced in the first 3 parts of this series. For this you can use AWS Lambda Profiler Extension for Java like desribed in my article Improving Lambda performance with Lambda SnapStart and priming .

The following approach can be applied primary to the GraalVM Native Image :

  • Try out Profile-Guided Optimizations to see whether you can further improve Lambda performance. The difficulty of trying out this technique is that you'll need to do some additional semi-automated steps to run your application either with the Lambda emulator locally or on the extra environment to obtain the profile of your application which you'll then need to generate the optimized Native Image. You can use Lambda extension for it, but it still requires a lot of additional work. The same work is done by AWS for us in case Lambda SnapStart is activated and I really appreciate that I don't need to care about generating, encrypting, storing and restoring the snapshots/profiles.

Conclusion

In this part of the series, we introduced some additional performance (cold and warm start times) optimization strategies to be used for the Spring Boot 3 application on AWS Lambda. Try them out on your own to figure out whether they will provide desired Lambda performance improvements.