Are you running your applications on the cloud? If yes, then you already know that your cloud provider charges you according to the resources (CPU, memory, disk) used by your applications. The more resources your applications consume, the higher your cloud bill will be! What can you do to reduce the resources being used by your Java applications, and thus save money from your cloud bill?
Use a Virtual Machine that consumes less memory
The first option would be to run your Java applications on a Virtual Machine that consumes less memory. If you attempt to download the latest release of Java from AdoptOpenJDK, you will notice that 2 different flavors are available, with each one of them using a different Virtual Machine (VM):
Hotspot: this is the VM that has always been shipped with Oracle JDK; it is the most widely used VM today.
OpenJ9: a VM that was originally released with IBM JDK, claiming to be designed for low-memory usage and fast startup.
“Immediately after startup OpenJDK 8 with OpenJ9 showed a footprint size about 66% less than OpenJDK 8 with HotSpot.” -OpenJ9 official page-
This claim sounds rather promising, so I thought of conducting an experiment by running the well-known application Spring-PetClinic with OpenJ9 and HotSpot, aiming to measure its memory footprint with each VM. For this experiment, I have built my own Docker images, which are based on AdoptOpenJDK’s images of Alpine Linux.
As shown in the following chart, my experiment with Spring-PetClinic confirmed that OpenJ9 consumes significantly less memory than HotSpot. Particularly, in OpenJDK 8 and 11, OpenJ9 has a footprint size about 71% and 54% less, respectively, than HotSpot.
It is noteworthy that the reference application (Spring-PetClinic) running on OpenJ9 has the same memory footprint for both OpenJDK 8 and 11. That is probably explained by the fact that the same release of OpenJ9 is shipped with both OpenJDK 8 and 11.
Clearly, the memory savings achieved by OpenJ9 are impressive. But, if you have got only one instance of your Java application running on the cloud, then migrating from HotSpot to OpenJ9 may not result in any noticeable savings in your cloud bill. Only if you have tens or hundreds of application instances on the cloud, should you consider migrating to OpenJ9 to achieve significant cost savings.
If for any reason you wish to keep running your applications on HotSpot, and are still stuck with Java 8, then it is likely worth investing the development effort to migrate your application to Java 11. The expected, reduced memory footprint of your application in OpenJDK 11 with HotSpot will yield cost savings that can pay off for the migration effort from Java 8 to Java 11.
Configure your Virtual Machine to use Class-Data Sharing
Class-Data Sharing (CDS) is a feature available in both HotSpot and OpenJ9 that can improve the startup time of your Java application and reduce its memory footprint.
If you have multiple instances of your application (or similar applications) running on the same host, then during the first execution of the application (also known as a “cold run”), its Java Virtual Machine will load the bootstrap & application classes from the file system and write them to the CDS cache. All consecutive executions of the same (or similar) applications will not have to load the bootstrap & application classes from the file system but will read them from the CDS cache (which is a faster operation compared to reading from the file system).
Indeed, my experiment verified that when running Spring-PetClinic on OpenJDK 11-OpenJ9 with Class-Data Sharing (CDS) enabled:
- the memory footprint is reduced by ~15%, and
- the startup time is reduced by ~52%.
Clearly, if you have several instances of your Java application (or similar Java applications) running on the same host, and/or must frequently startup new instances, then you may benefit from the savings achieved in memory footprint and startup time by Class-Data Sharing. Remember that the more time it takes for your application to startup, the more you will be charged by your cloud provider for the consumed resources (CPU per hour).
Minimize the Docker images of your application
Before Java 9, the Java Runtime Environment (JRE) was a monolithic application, which had to be deployed in its entirety with every Java application. With the advent of Java 9, the Java Runtime Environment became modular, allowing us to deploy only the parts (known as modules) that our Java application truly needs.
The reference application (Spring-PetClinic 2.1.0) used in the experiments needs only 14 modules from a total of 74 modules that comprise OpenJDK 11. By using the Java Linker (jlink), the Java Runtime Environment was customized for the given Java application (Spring-PetClinic), thus enabling us to minimize the size of the resulting Docker image, as shown below:
Clearly, this customization of the Java Runtime Environment allowed us to reduce the size of the resulting Docker images by ~60%. This significant reduction in the size of the Docker images has the potential to help you reduce your cloud bills, by consuming:
- less storage in Container Registry to store the Docker images,
- less network bandwidth to upload/download Docker images.
Moreover, the reduced size of Docker images will allow for faster startup of new instances, as you scale out your Java application.
Keep an eye on the costs of your application
Finally, the hardest part is to continuously track your cloud costs, being able to reason about the individual costs of your cloud application:
- What is the cost incurred by each component in your application? Until recently, we have been choosing among similar third-party components based on their features, support, license, documentation, etc.; but now, we must also consider their cost in terms of cloud resources being consumed and prefer to use cloud-friendly components in our applications.
- What will be the cost of a new feature in your application? An overly costly feature should be promptly detected, before being deployed to the cloud. Ideally, your CI/CD pipelines may have to be improved to identify new features that require a lot of resources and will thus significantly increase your cloud costs.
Should you wish to run this experiment yourself, feel free to use my Docker images: https://hub.docker.com/r/ikolaxis/evaluate-java-virtual-machines
Alternatively, you can reuse my Docker files, and adapt them for your experiments: https://github.com/ikolaxis/evaluate-java-virtual-machines