Is Java Cloud Native?

Dmitry Chuyko

With the 30th anniversary just around the corner, Java is still going strong. Backward compatibility, active community, and numerous tools facilitate Java development and make the platform a natural choice for enterprise applications.

And yet, when it comes to the cloud, Go, JavaScript, Ruby, Kotlin, or Rust tend to be a preferred choice for their speed, concurrency, and ease-of-use. WebAssembly or Wasm is also gaining popularity when it comes to web development. But IT teams often take Java’s ability to produce cloud-native programs with a grain of salt.

The question arises: Is Java cloud native? The answer might not be obvious to those not watching closely the evolution of the platform. Java has come a long journey to become a rightful cloud dweller. This article aims to throw light on the key milestones on Java’s way to the cloud.

What is Cloud Native?

Cloud Native Pillars

Before starting the discussion on Java’s cloud-native capabilities, it will be helpful to define what cloud native computing means. 

Cloud native is an approach to building, deploying, and running applications in the cloud environment that enables high flexibility, scalability, and availability of applications. DevOps experts can rapidly scale cloud-native applications to meet the incoming traffic. Developers can introduce changes frequently without impacting the application availability to users.

For instance, Google defines four pillars of cloud-native applications: microservices, containers, DevOps, CI/CD. We can describe them as follows:

  • Microservices are small loosely coupled services that perform a specific function. Developers can change them independently of other services. They communicate with each other via APIs and provide greater flexibility and reliability of the cloud-native application. API or Application Programming Interface forms a connection between services so that they can exchange information. API can connect services written in different languages. 
  • Containers are software packages that contain everything the application needs to run in the cloud: application code, dependencies, libraries, framework, OS, runtime environment.
  • CI/CD or Continuous Integration/Continuous Deployment is the set of practices aimed at streamlining the development, testing, and deployment of software. CI/CD implies a high degree of automation and helps to build and release high-quality software more frequently.
  • DevOps is a methodology aimed at fostering tighter cooperation between developers and an operations team. DevOps practices help to shorten the release cycles, speed up time-to-market and provide users with high-quality software that meets their expectations. 

But there are two more indispensable parts of an efficient cloud-native infrastructure:

  • Container orchestration allows you to automate the deployment and scaling of containers based on the workload requirements. Kubernetes, the most popular platform for container orchestration, offers ready solutions for managing containerized workloads in the cloud efficiently. 
  • Observability enables tracking the state of distributed systems in the cloud, gathering data in the form of metrics or traces. Observability is crucial for cloud native workloads as it helps to navigate complex systems and identify issues quickly.

Cloud Native Challenges 

What are the main challenges of the cloud environment?

  • Management of distributed systems. Cloud-native microservices are often complex systems with elaborate interconnection that are harder to test, debug, and update than traditional monoliths. In this case, an extensive ecosystem around a chosen platform should include all necessary tools for building and maintaining such systems.
  • Increased operational and technology costs in case of lacking cost optimization strategy. A failure to control the usage of resources in the cloud environment may lead to resource underutilization and impact cloud bills.
  • The pipeline from developer’s machine to production must be smooth and fast, with mostly automated processes. So, the ecosystem around the language must include building blocks to create such a pipeline.
  • Lack of technical skills necessary to work with and integrate a more complex technology stack. Cloud infrastructure requires experience with different novel technologies. It would be helpful if there were many developers with different skill sets and experience.
  • Resistance to the cultural shifts needed to implement cloud-native technologies and DevOps best practices.
  • Difficulty communicating cloud-native concepts to gain support and buy-in from non-technical executives.

In summary, cloud-native applications are built and deployed using cloud-native tools and have some or all of the following qualities:

  • Broken into microservices,
  • Containerized,
  • Integrated into CI/CD pipeline,
  • Developed following best DevOps practices, and 
  • Can be conveniently scaled vertically or horizontally. 

The key is to be able to write, deploy, and run services fast and be sure that they don’t eat up cloud resources. 

So, how does Java fit into this paradigm?

How Java moved to the cloud

Java’s Evolution Towards Cloud-Native

Over the course of thirty years, Java managed to retain a position of one of the most popular programming languages for enterprise. The secret to its success is the efforts of platform developers. They constantly work on improving Java to meet user demands. Java today and Java 29 years ago are not the same. So, to answer the question whether we can consider Java cloud native, we should look at the key milestones of its journey towards the cloud.

The Java platform engineers introduced cloud-friendly features to Java gradually. Making Java cloud native was not the only goal of platform developers, but it became more relevant with the growing popularity of cloud deployments.

Considering challenges described above, Java was not a great fit for the cloud at first. But it had some great features right from the start. It has automatic memory management, high performance, build systems, and cross-platform capabilities. Container support was to be improved.

Key Milestones in Java’s Cloud Journey

JDK 9 included several enhancements, including compact Strings that optimized JVM memory consumption, a new HTTP client API that implements HTTP/2 and WebSocket,  and support for cgroups memory limits in containers. In addition, starting with JDK 9, it is possible to create custom runtime images that help to reduce the size of Docker container images.

JDK 10 came with improved Docker container detection and resource configuration usage. Also, new JVM flags were added to manually adjust the percentage of RAM available to JVM.

JDK 11 came with an internal API for extracting container specific configuration and runtime statistics. This API improved observability of containerized Java workloads.

Java’s garbage collector algorithms were also enhanced to fit the cloud environment better. JDK 11 included a new low-latency Z GC, JDK 12 brought low-latency Shenandoah GC, both made production ready in JDK 15. G1 GC started returning unused Java heap memory to the operating system when idle in JDK 12. The garbage collectors are being continuously improved. For instance, Z GC became generational in JDK 21 for lower garbage collection CPU memory overhead. Shenandoah GC will become generational in JDK 24. Also in JDK 24, G1 GC will include late barrier expansion for reduced JVM overhead during compilation.

It was important to optimize resource consumption in containers and reduce the overall size of Java container images. At some point, minimalistic Alpine Linux became the new black in the world of cloud native development. With a base image of only about three megabytes and musl libc support, it enabled the creation of lightweight container images. Java followed suit, and JDK 16 included Alpine Linux support. Small container images with Java programs became a reality.

JDK 16 also came with improved Metaspace elasticity, which dealt with the issues of excessive memory usage in some cases. So, it reduced maintenance costs of containerized workloads.

These are only the most essential improvements because listing all enhancements and fixes that made Java more efficient in the cloud would be too much for an article. To summarize, Java’s journey to the cloud was a success. But the journey never ends: new challenges arise and new solutions emerge, and so, Java keeps getting better.

New challenges, new solutions

Optimizing cloud costs and application efficiency

Running cloud-native workloads presents not only immense opportunities, but also certain challenges. Due to the not-well-thought-through cloud deployment strategy and cloud infrastructure design, the company runs the risk of breaking SLAs and/or receiving unexpectedly huge cloud bills.

One of the major contributing factors to cloud spending optimization is the ability to scale the resources up and down depending on the traffic. Your cloud infrastructure may have services with different load or alternating traffic spikes and down times. It would be more cost-efficient to pay only for the resources when the service is actually doing something.

There are solutions helping to achieve that. For instance, there are AWS Lambdas or Google functions that start the service only when there is incoming traffic. In addition, you can use auto-scaling features or Function-as-a-service for writing code executed in response to certain events.

So, the cloud is about speed, efficiency, and automation. A perfect service is easy to write, configure, containerize, and deploy. It doesn’t eat away at cloud resources, starts up fast, and can be conveniently scaled to meet the demand.

Building a Cloud Native Java Environment

Amazing fact: it is possible to get all of that with Java! The process of setting cloud-native Java environment is straightforward. All you need is:

  • A Java application,
  • Docker for building and publishing container images,
  • Kubernetes for orchestrating containers,
  • An ingress controller such as NGINX to access Kubernetes and expose services to users,
  • A framework such as Spring Boot,
  • Apache Maven or Gradle.

The secret ingredient is cloud native buildpacks. Buildpacks transform application source code into runnable artifacts with minimal configuration. They are a perfect match for the cloud because they eliminate the need to create and maintain the build environment for the application, promoting automation and standardization. 

As containers provide isolation, all you need to use buildpacks is Docker and minimal tools for starting the build process, such as pack CLI for Paketo buildpacks. In addition, Spring and other major Java frameworks support containerization with buildpacks via Maven and Gradle plugin.

What is more, buildpacks are easy to configure. Their modus operandi is based on stacks. There’s a build stack and run stack, which group together base images required for building or running an application. For instance, a build stack for Java includes JDK, and a run stack includes JRE. Stacks are chosen automatically, so you don’t have to worry about the process. But on the other hand, you can change the stack to fit the requirements of your project.

You can use the default JDK or take advantage of buildpacks with a lightweight JDK. You can even create a custom runtime for your application with the jlink tool. Java’s path to production has never been faster! 

Optimizing Java Applications for the Cloud

To tackle the issue of slow Java startup/warmup, you can use GraalVM Native Image. It turns Java applications into fully compiled platform-specific native executables that start up almost instantly and at peak performance. It is possible to create fully static native images that can run on distroless, another cloud trend. 

The reason why Native Image is not on the list above is because you don’t need to install the tool separately. You can enable native image generation with buildpacks, too! If you need to build native images for various platforms, there is a GitHub Action for that.

As a result, Java applications are turned into native images and containerized with one command and are sent further down the pipeline. In production, they can be scaled up and down effortlessly, optimizing cloud expenses.

The Java language has also become more expressive. Java programs have become more concise thanks to new features such as Pattern matching for switch. They also communicate with other languages more reliably thanks to Foreign Function & Memory API and other tools.

As AI plays a major role in modern cloud-native setups, solutions in the Java ecosystem such as Spring AI make it possible to integrate language models and AI chat bots into Java programs.  

The solutions for running Java applications efficiently in the cloud don’t stop there. For instance, virtual threads finalized in JDK 21 can help to scale Java applications maintaining optimal hardware utilization. In addition, the family of Java garbage collectors was replenished with two low-latency collectors Z GC and Shenandoah GC, both made production-ready in JDK 15. They are suitable for very large heaps of terabytes and/or latency-sensitive applications. 

All in all, Java platform developers always stay abreast of the latest trends and modern requirements in software development, and as cloud is an indispensable part of most software projects, they work on introducing and enhancing features that will make Java services faster, smaller, and more efficient in the cloud environment.

Why Java is an Optimal Choice for Cloud Native Workloads

Taking into account everything discussed above, there is no doubt whether Java is cloud native. Or it is now, to be precise.  Years of evolution made Java a great choice for building and running cloud native applications efficiently, because Java is efficient on many levels:

  • Proactive community of individual developers and organizations that continuously contribute to the platform improvement;
  • Efficient progression in development thanks to a strict release schedule and biennial releases with long-term support;
  • Small, secure, and fast container images;
  • Excellent peak performance and support for modern hardware;
  • A variety of solutions that help to reach peak performance faster.

Conclusion

In this article, we followed Java’s journey to the cloud. We discussed features making it the right choice for cloud-native enterprise applications and looked into approaches to increasing the performance of cloud-native Java apps. 

Java is the rightful dweller in the cloud. There are numerous features, tools, and solutions that make developing and running cloud-native applications a smooth experience. Thanks to the active OpenJDK community, individual developers and companies contributing to the platform improvement, we can be sure that Java will continue evolving and keep its place among the most beloved programming languages for many years to come.

Curious What Else Java Can Do?
In this article, Dmitry Chuyko explores how Java became truly cloud native.
At JCON, he goes further with “Legal JVM Doping” – a session on boosting Java performance using tools like CRaC, Leyden & GraalVM.
The video will be available after the event – worth a watch!

Total
0
Shares
Previous Post

Next Generation Caching & In-Memory Searching

Next Post

Code Reviews with AI: a Developer Guide

Related Posts
Java turns 30 in 2025

30 Years of Java, 25 Years of Enterprise Java

Over the last three decades, technology has been evolving at a breakneck pace, with innovations constantly redefining every aspect of application development. In such a fast-moving and dynamic landscape, few technologies stand the test of time, especially in computer science. Yet, Java has done just that. As we celebrate 30 years of Java and 25 years of enterprise Java, it’s clear that these solutions have not only endured but thrived—adapting, advancing and proving their lasting value to software engineers worldwide.
Steve Millidge
Read More