Posts Tagged ‘OpenJDK’
[MunchenJUG] Navigating the JVM Ecosystem: A Safari Through Distributions (16/Sep/2024)
Lecturer
Gerrit Grunwald is a highly regarded software engineer and advocate with four decades of experience in the technology sector. He is a prominent figure in the Java community, recognized as a Java Champion and a JavaOne Rockstar. Gerrit is deeply committed to open-source software, having contributed to and led numerous projects such as JFXtras, TilesFX, Medusa, and JDKMon. He founded and leads the Java User Group Münster and is a frequent speaker at international conferences. Currently, Gerrit serves as a Developer Advocate at Azul.
Abstract
This article provides an analytical overview of the modern Java Virtual Machine (JVM) landscape, distinguishing between the OpenJDK project and its various commercial and community distributions. It evaluates the shift in Java’s release cadence and the implications for long-term support (LTS) in corporate environments. A significant portion of the analysis is dedicated to the optimization of Java runtimes through modularity and the jlink tool, demonstrating how developers can significantly reduce deployment sizes and enhance security. Finally, the article categorizes the plethora of available JDK distributions—from major cloud providers like Amazon and Alibaba to specialized runtimes like GraalVM—offering a guide for selecting the appropriate distribution based on specific use cases.
The Distinction Between OpenJDK and Distributions
A fundamental misunderstanding in the Java community is the conflation of “OpenJDK” with the software installed on a user’s machine. OpenJDK is not a downloadable product but rather the open-source project hosted on GitHub that contains the source code for the Java Platform, Standard Edition (Java SE). What developers actually utilize are “builds” or “distributions” of this source code.
The OpenJDK ecosystem is characterized by its collaborative nature, with significant contributions from tech giants such as Oracle, Amazon, ARM, Google, Intel, and IBM. This multi-corporate backing ensures the longevity and stability of the platform, preventing it from becoming a “one-man show”. Since moving to GitHub with JDK 16, the transparency and accessibility of the source code have further improved, allowing for faster build times and broader community involvement.
Release Cadence and Support Models
The evolution of Java’s release model marks a critical transition from multi-year development cycles to a predictable six-month cadence. Historically, long gaps between releases (such as the five years between JDK 6 and JDK 7) led to massive, overwhelming updates that were difficult for organizations to adopt.
The current model classifies releases into two categories:
- Feature Releases: Released every six months, these versions typically receive support for only half a year.
- Long-Term Support (LTS) Releases: These versions are designated for extended support, often spanning a decade or more, providing the stability required by enterprise applications.
This dual-track approach allows the language to innovate rapidly through feature releases while providing a safe harbor for production environments on LTS versions.
Efficiency through Modularity: The jlink Revolution
One of the most underutilized innovations introduced in JDK 9 is the modularization of the Java runtime. By breaking the monolithic JDK into 69 distinct modules, Oracle enabled developers to create custom, stripped-down runtimes tailored to specific applications.
The tool jlink allows for the creation of a custom Java Runtime Environment (JRE) containing only the modules necessary for a particular application. The impact on deployment size is profound:
- A full JDK 21 installation requires approximately 340 MB.
- A standard JRE for the same version takes about 150 MB.
- A
jlink-optimized runtime for a simple application (like a push notification server) can be as small as 48 MB.
echo Example of using jdeps to find required modules
jdeps --ignore-missing-deps --print-module-deps MyProject.jar
echo Example of using jlink to create a custom runtime
jlink --add-modules java.base,java.logging --output custom-runtime
Beyond storage savings, modular runtimes enhance security by reducing the attack surface. If a vulnerability exists in a module that has been excluded from the custom runtime (such as the desktop module in a server-side application), the application remains unaffected.
Mapping the Distribution Jungle
The JVM landscape is populated by numerous distributions, each offering different levels of support, licensing, and platform optimizations.
Community and Vendor Builds
- Eclipse Temurin (formerly AdoptOpenJDK): A widely used community build that is TCK (Technology Compatibility Kit) compliant.
- Amazon Corretto: A no-cost, multiplatform distribution used internally by Amazon for its AWS services.
- Azul Zulu: A TCK-compliant distribution offering broad platform support.
- Oracle OpenJDK: The free, GPL-licensed build provided by Oracle.
Region-Specific and Specialized Distributions
In the Asian market, distributions like Alibaba’s Dragonwell, Huawei’s Bi Sheng, and Tencent’s Kona are dominant. These often include specific optimizations for the cloud infrastructures of their respective parent companies.
Advanced Runtimes: GraalVM and Beyond
GraalVM represents a specialized branch of the JVM ecosystem, offering high-performance polyglot capabilities and “Native Image” compilation. Native images allow Java applications to start in milliseconds by compiling them into platform-specific executables, though this comes at the cost of peak performance and longer build times compared to the standard JIT (Just-In-Time) compilation used by the HotSpot JVM.
Conclusion: Strategy for Selection
Choosing the right JVM distribution is a strategic decision based on support requirements, cost, and technical constraints. For most production environments, sticking to an LTS version from a reputable vendor (like Azul, Amazon, or the Eclipse Foundation) ensures stability. Meanwhile, developers should leverage modern tools like jlink to ensure their deployments remain lean and secure, regardless of the distribution chosen.
Links:
[Oracle Dev Days 2025] Optimizing Java Performance: Choosing the Right Garbage Collector
Jean-Philippe BEMPEL , a seasoned developer at Datadog and a Java Champion, delivered an insightful presentation on selecting and tuning Garbage Collectors (GCs) in OpenJDK to enhance Java application performance. His talk, rooted in practical expertise, unraveled the complexities of GCs, offering a roadmap for developers to align their choices with specific application needs. By dissecting the characteristics of various GCs and their suitability for different workloads, Jean-Philippe provided actionable strategies to optimize memory management, reduce production issues, and boost efficiency.
Understanding Garbage Collectors in OpenJDK
Garbage Collectors are pivotal in Java’s memory management, silently handling memory allocation and reclamation. However, as Jean-Philippe emphasized, a misconfigured GC can lead to significant performance bottlenecks in production environments. OpenJDK offers a suite of GCs—Serial GC, Parallel GC, G1, Shenandoah, and ZGC—each designed with distinct characteristics to cater to diverse application requirements. The challenge lies in selecting the one that best matches the workload, whether it prioritizes throughput or low latency.
Jean-Philippe began by outlining the foundational concepts of GCs, particularly the generational model. Most GCs in OpenJDK are generational, dividing memory into the Young Generation (for short-lived objects) and the Old Generation (for longer-lived objects). The Young Generation is further segmented into the Eden space, where new objects are allocated, and Survivor spaces, which hold objects that survive initial collections before promotion to the Old Generation. Additionally, the Metaspace stores class metadata, a critical but often overlooked component of memory management.
Serial GC: Simplicity for Constrained Environments
The Serial GC, one of the oldest collectors, operates with a single thread and employs a stop-the-world approach, pausing all application threads during collection. Jean-Philippe highlighted its suitability for small-scale applications, particularly those running in containers with less than 2 GB of RAM, where it serves as the default GC. Its simplicity makes it ideal for environments with limited resources, but its stop-the-world nature can introduce noticeable pauses, making it less suitable for latency-sensitive applications.
To illustrate, Jean-Philippe explained the mechanics of the Young Generation’s Survivor spaces. These spaces, S0 and S1, alternate roles as source and destination during minor GC cycles, copying live objects to manage memory efficiently. Objects surviving multiple cycles are promoted to the Old Generation, reducing the overhead of frequent collections. This generational approach leverages the hypothesis that most objects die young, minimizing the cost of memory reclamation.
Parallel GC: Maximizing Throughput
For applications prioritizing throughput, such as batch processing jobs, the Parallel GC offers significant advantages. Unlike the Serial GC, it leverages multiple threads to reclaim memory, making it efficient for systems with ample CPU cores. Jean-Philippe noted that it was the default GC until JDK 8 and remains a strong choice for throughput-oriented workloads like Spark jobs, Kafka consumers, or ETL processes.
The Parallel GC, also stop-the-world, excels in scenarios where total execution time matters more than individual pause durations. Jean-Philippe shared a benchmark using a JFR (Java Flight Recorder) file parsing application, where Parallel GC outperformed others, achieving a throughput of 97% (time spent in application versus GC). By tuning the Young Generation size to reduce frequent minor GCs, developers can further minimize object copying, enhancing overall performance.
G1 GC: Balancing Throughput and Latency
The G1 (Garbage-First) GC, default since JDK 9 for heaps larger than 2 GB, strikes a balance between throughput and latency. Jean-Philippe described its region-based memory management, dividing the heap into smaller regions (Eden, Survivor, Old, and Humongous for large objects). This structure allows G1 to focus on regions with the most garbage, optimizing memory reclamation with minimal copying.
In his benchmark, G1 showed a throughput of 85%, with average pause times of 76 milliseconds, aligning with its target of 200 milliseconds. However, Jean-Philippe pointed out challenges with Humongous objects, which can increase GC frequency if not managed properly. By adjusting region sizes (up to 32 MB), developers can mitigate these issues, improving throughput for applications like batch jobs while maintaining reasonable pause times.
Shenandoah and ZGC: Prioritizing Low Latency
For latency-sensitive applications, such as HTTP servers or microservices, Shenandoah and ZGC are the go-to choices. These concurrent GCs minimize pause times, often below a millisecond, by performing most operations alongside the running application. Jean-Philippe highlighted Shenandoah’s non-generational approach (though a generational version is in development) and ZGC’s generational support since JDK 21, making the latter particularly efficient for large heaps.
In a latency-focused benchmark using a Spring PetClinic application, Jean-Philippe demonstrated that Shenandoah and ZGC maintained request latencies below 200 milliseconds, significantly outperforming Parallel GC’s 450 milliseconds at the 99th percentile. ZGC’s use of colored pointers and load/store barriers ensures rapid memory reclamation, allowing regions to be freed early in the GC cycle, a key advantage over Shenandoah.
Tuning Strategies for Optimal Performance
Tuning GCs is as critical as selecting the right one. For Parallel GC, Jean-Philippe recommended sizing the Young Generation to reduce the frequency of minor GCs, ideally exceeding 50% of the heap to minimize object copying. For G1, adjusting region sizes can address Humongous object issues, while setting a maximum pause time target (e.g., 50 milliseconds) can shift its behavior toward latency sensitivity, though it may not compete with Shenandoah or ZGC in extreme cases.
For concurrent GCs like Shenandoah and ZGC, ensuring sufficient heap size and CPU cores prevents allocation stalls, where threads wait for memory to be freed. Jean-Philippe emphasized that Shenandoah requires careful heap sizing to avoid full GCs, while ZGC’s rapid region reclamation reduces such risks, making it more forgiving for high-allocation-rate applications.
Selecting the Right GC for Your Workload
Jean-Philippe concluded by categorizing workloads into two types: throughput-oriented (SPOT) and latency-sensitive. For SPOT workloads, such as batch jobs or ETL processes, Parallel GC or G1 are optimal, with Parallel GC offering easier tuning for predictable performance. For latency-sensitive applications, like microservices or databases (e.g., Cassandra), ZGC’s generational efficiency and Shenandoah’s low-pause capabilities shine, with ZGC being particularly effective for large heaps.
By analyzing workload characteristics and leveraging tools like GC Easy for log analysis, developers can make informed GC choices. Jean-Philippe’s benchmarks underscored the importance of tailoring GC configurations to specific use cases, ensuring both performance and stability in production environments.
Links:
Hashtags: #Java #GarbageCollector #OpenJDK #Performance #Tuning #Datadog #JeanPhilippeBempel #OracleDevDays2025
[DevoxxBE2023] Moving Java Forward Together: Community Power
Sharat Chander, Oracle’s Senior Director of Java Developer Engagement, delivered a compelling session at DevoxxBE2023, emphasizing the Java community’s pivotal role in driving the language’s evolution. With over 25 years in the IT industry, Sharat’s passion for Java and community engagement shone through as he outlined how developers can contribute to Java’s future, ensuring its relevance for decades to come.
The Legacy and Longevity of Java
Sharat began by reflecting on Java’s 28-year journey, a testament to its enduring impact on software development. He engaged the audience with a poll, revealing the diverse experience levels among attendees, from those using Java for five years to veterans with over 25 years of expertise. This diversity underscores Java’s broad adoption across industries, from small startups to large enterprises.
Java’s success, Sharat argued, stems from its thoughtful innovation strategy. Unlike the “move fast and break things” mantra, the Java team prioritizes stability and backward compatibility, ensuring that applications built on older versions remain functional. Projects like Amber, Panama, and the recent introduction of virtual threads in Java 21 exemplify this incremental yet impactful approach to innovation.
Balancing Stability and Progress
Sharat addressed the tension between rapid innovation and maintaining stability, a challenge given Java’s extensive history. He highlighted the six-month release cadence introduced to reduce latency to innovation, allowing developers to adopt new features without waiting for major releases. This approach, likened to a train arriving every three minutes, minimizes disruption and enhances accessibility.
The Java team’s commitment to trust, innovation, and predictability guides its development process. Sharat emphasized that Java’s design principles—established 28 years ago—continue to shape its evolution, ensuring it meets the needs of diverse applications, from AI and big data to emerging fields like quantum computing.
Community as the Heart of Java
The core of Sharat’s message was the community’s role in Java’s vitality. He debunked the “build it and they will come” myth, stressing that Java’s success relies on active community participation. Programs like the OpenJDK project invite developers to engage with mailing lists, review code check-ins, and contribute to technical decisions, fostering transparency and collaboration.
Sharat also highlighted foundational programs like the Java Community Process (JCP) and Java Champions, who advocate for Java independently, providing critical feedback to the Java team. He encouraged attendees to join Java User Groups (JUGs), noting the nearly 400 groups worldwide as vital hubs for knowledge sharing and networking.
Digital Engagement and Future Initiatives
Recognizing the digital era’s impact, Sharat discussed Oracle’s efforts to reach Java’s 10 million developers through platforms like dev.java. This portal aggregates learning resources, community content, and programs like JEEP Cafe and Sip of Java, which offer digestible insights into Java’s features. The recently launched Java Playground provides a browser-based environment for experimenting with code snippets, accelerating feature adoption.
Sharat also announced the community contributions initiative on dev.java, featuring content from Java Champions like Venkat Subramaniam and Hannes Kutz. This platform aims to showcase community expertise, encouraging developers to submit their best practices via GitHub pull requests.
Nurturing Diversity and Inclusion
A poignant moment in Sharat’s talk was his call for greater gender diversity in the Java community. He acknowledged the industry’s shortcomings in achieving balanced representation and urged collective action to expand the community’s mindshare. Programs like JDuchess aim to create inclusive spaces, ensuring Java’s evolution benefits from diverse perspectives.