{"id":232717,"date":"2025-12-16T11:17:27","date_gmt":"2025-12-16T19:17:27","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/java\/?p=232717"},"modified":"2025-12-16T15:09:50","modified_gmt":"2025-12-16T23:09:50","slug":"beyond-ergonomics-how-the-azure-command-launcher-for-java-improves-gc-stability-and-throughput-on-azure-vms","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/java\/beyond-ergonomics-how-the-azure-command-launcher-for-java-improves-gc-stability-and-throughput-on-azure-vms\/","title":{"rendered":"Beyond Ergonomics: How the Azure Command Launcher for Java Improves GC Stability and Throughput on Azure VMs"},"content":{"rendered":"<p>In our <a href=\"https:\/\/devblogs.microsoft.com\/java\/from-complexity-to-simplicity-intelligent-jvm-optimizations-on-azure\/\">previous blog<\/a> we introduced Azure Command Launcher for Java (<code>jaz<\/code>) \u2014a safe, resource-aware way to launch the JVM without hand-tuning dozens of flags. This follow-up shares performance results, focusing on how <code>jaz<\/code> affects G1 behavior, heap dynamics, and pause characteristics under a long-running, allocation-intensive workload: SPECjbb 2015 (JBB).<\/p>\n<p><strong>Test bed<\/strong>: 4-vCPU, 16-GB Azure Linux\/Arm64 VM running the Microsoft Build of OpenJDK.<\/p>\n<p><strong>JDKs exercised<\/strong>: Validated on JDK 17 (17.0.17), 21 (21.0.9), and 25 (25.0.1); all figures in this post are from the JDK 17 runs. Trends on 21\/25 matched the 17 results.<\/p>\n<p><strong>How we ran it:<\/strong><\/p>\n<pre class=\"prettyprint language-default\"><code class=\"language-default\"># baseline\r\njava -jar specjbb.jar\r\n\r\n# with jaz\r\njaz -jar specjbb.jar<\/code><\/pre>\n<p><strong>Controls<\/strong>: Same JBB workload config, OS settings, and JVM flags for both runs\u2014the launcher was the only change.<\/p>\n<p><div class=\"alert alert-success\">SPECjbb 2015 (JBB) is a SPEC benchmark; we report relative trends only and do not publish raw scores.<\/div><\/p>\n<h2>Why JBB Is the Right Stress Test<\/h2>\n<p>JBB exercises high allocation rate, object churn, humongous-allocation behavior, generational sizing, region pressure, concurrent-mark sustainability, GC scheduling, and pause-time predictability. Because it is both bandwidth-intensive and latency-sensitive, JBB is ideal for validating heap ergonomics and GC policies in the cloud.<\/p>\n<p>As a capacity-planning tool it helps explore sustained throughput limits, GC headroom before SLA violations, warm-up behavior under load, and how a given VM size (4 cores, 16 GB) holds up under continuous allocation pressure.<\/p>\n<p><div class=\"alert alert-primary\">\nFor detailed methodology and the JBB phase guide, see <a href=\"#appendix-a\">Appendix A<\/a> and <a href=\"#appendix-b\">Appendix B<\/a>.\nA GC refresher and figure legend are in <a href=\"#appendix-c\">Appendix C<\/a> and <a href=\"#appendix-d\">Appendix D<\/a>.<\/div><\/p>\n<h2>Performance Summary: Baseline vs <code>jaz<\/code><\/h2>\n<table style=\"width: 100%;\">\n<tbody>\n<tr>\n<td width=\"156\"><strong>Metric<\/strong><\/td>\n<td width=\"156\"><strong>Baseline<\/strong><\/td>\n<td width=\"156\"><strong>With <code>jaz<\/code><\/strong><\/td>\n<td width=\"156\"><strong>Improvement<\/strong><\/td>\n<\/tr>\n<tr>\n<td width=\"156\"><strong>Peak Throughput<\/strong><\/td>\n<td width=\"156\">Baseline<\/td>\n<td width=\"156\">22%<\/td>\n<td width=\"156\">Higher max-jOPS<\/td>\n<\/tr>\n<tr>\n<td width=\"156\"><strong>SLA Performance<\/strong><\/td>\n<td width=\"156\">Baseline<\/td>\n<td width=\"156\">15%<\/td>\n<td width=\"156\">Higher critical-jOPS<\/td>\n<\/tr>\n<tr>\n<td width=\"156\"><strong>Total GC Events<\/strong><\/td>\n<td width=\"156\">3777<\/td>\n<td width=\"156\">2526<\/td>\n<td width=\"156\">-33% (1251 fewer)<\/td>\n<\/tr>\n<tr>\n<td width=\"156\"><strong>Young GC Count<\/strong><\/td>\n<td width=\"156\">1100<\/td>\n<td width=\"156\">1596<\/td>\n<td width=\"156\">+45% (handles higher load)<\/td>\n<\/tr>\n<tr>\n<td width=\"156\"><strong>Mixed GC Count<\/strong><\/td>\n<td width=\"156\">778<\/td>\n<td width=\"156\">265<\/td>\n<td width=\"156\">-66% (513 fewer)<\/td>\n<\/tr>\n<tr>\n<td width=\"156\"><strong>Young GC Overhead<\/strong><\/td>\n<td width=\"156\">1.41%<\/td>\n<td width=\"156\">2.60%<\/td>\n<td width=\"156\">Higher but efficient<\/td>\n<\/tr>\n<tr>\n<td width=\"156\"><strong>Mixed GC Overhead<\/strong><\/td>\n<td width=\"156\">0.96<\/td>\n<td width=\"156\">0.39<\/td>\n<td width=\"156\">-59% reduction<\/td>\n<\/tr>\n<tr>\n<td width=\"156\"><strong>Old Gen Pattern<\/strong><\/td>\n<td width=\"156\">Flat plateau (600-900)<\/td>\n<td width=\"156\">Deep sawtooth (200-1000)<\/td>\n<td width=\"156\">Dynamic sizing active<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>Key Insight:<\/strong> <code>jaz<\/code> achieves 22% higher throughput by keeping Young GC efficient\u2014objects die in Eden\/Survivor instead of promoting prematurely to Old gen, dramatically reducing expensive Mixed GC work.<\/p>\n<h2>Baseline Behavior: Where the Wild Things Are<\/h2>\n<p><em>Microsoft Build of OpenJDK with default G1 GC ergonomics. Long JBB run on a 4-core VM<\/em>.<\/p>\n<h3>Region Dynamics: Tight Band with Sustained Old Gen Plateau<\/h3>\n<ul>\n<li>Collection cadence is crowded: Young promotes excessively to Old gen; Mixed GC runs continuously but can\u2019t get Old occupancy down.<\/li>\n<li>Eden (before GC) sits in high, tightly bound band; each Young GC arrives with Eden already large.<\/li>\n<li>Old (after GC) settles into a crowded, high plateau (~600\u2013900 regions) with a slight upward drift, indicating continued promotion pressure.<\/li>\n<li>Humongous-triggered Concurrent Start (markers labeled <em>Humongous)<\/em> denote very large allocations that force a new concurrent marking cycle. They appear clustered, intermittent and align with heavier Old gen activity.<\/li>\n<\/ul>\n<h3>GC Pause Envelope: Heavy Mixed GC Response to JBB Phase Shifts<\/h3>\n<ul>\n<li>Young pauses (teal): early noisy cluster (spikes ~300 ms), then a long, flatter band around ~80\u2013130 ms; moderate variance as Eden fill \u2192 evacuate \u2192 refill repeats.<\/li>\n<li>Overlay (Concurrent-Start (magenta) + Prepare-Mixed (gray) + Mixed (blue)): the combined envelope includes the magenta points (Concurrent Start) followed by effectively continuous periods of Prepare\/Mixed (~50\u2013150 ms with occasional higher outliers) and dense bands. Old gen&#8217;s elevated plateau drives frequent concurrent cycles and Mixed activity.<\/li>\n<\/ul>\n<h2>With <code>jaz<\/code>: Beyond the Wild Rumpus<\/h2>\n<p><em>Same VM\/JDK\/workload; only the launcher changed (<code>jaz<\/code><\/em><em>\u00a0resource-aware defaults). <\/em><\/p>\n<h3>Region Dynamics: Dynamic Sizing with Dramatic Old Gen Sawtooth<\/h3>\n<ul>\n<li>Handling higher throughput: <code>jaz<\/code> achieves 22% higher max-jOPS, driving higher allocation rate. Both Eden and Old show dramatic oscillation (200-1000 regions) &#8211; wider variation reflects increased load and dynamic heap sizing, not the tight bands of baseline.<\/li>\n<li>Eden (before-GC): Wider oscillation reflects the higher allocation rate from increased throughput;\u00a0dynamic sizing adapts to load.<\/li>\n<li>Old (after-GC): Dramatic sawtooth pattern is the key insight:\n<ul>\n<li>Deep troughs (~200 regions):\u00a0Mixed GC efficiently reclaims Old gen, bringing occupancy down to minimal levels<\/li>\n<li>Gradual rises (200 \u2192 1000):\u00a0Steady, controlled promotion over many cycles<\/li>\n<li>Sharp drops:\u00a0Mixed cycles reclaim aggressively, restoring headroom for next load phase<\/li>\n<\/ul>\n<\/li>\n<li>Humongous-triggered Concurrent Starts: rare and isolated, most avoiding Old gen spikes tied to large allocations.<\/li>\n<\/ul>\n<h3>GC Pause Envelope: Narrow and Predictable Early<\/h3>\n<ul>\n<li>Young pauses (teal): Wavy pattern with periodic oscillations ~15\u2013250 ms; working to handle the increased throughput load.\n<ul>\n<li>Key insight: Higher Young GC frequency (1,596 vs 1,100) keeps pace with the higher allocations and ages objects in the Young gen where collection is cheap, preventing premature promotion to Old gen<\/li>\n<\/ul>\n<\/li>\n<li>Overlay (Concurrent-Start (magenta) + Prepare-Mixed (gray) + Mixed (blue)): Shows the episodic nature clearly:\n<ul>\n<li>Concurrent Start begins marking cycle (114 events vs baseline&#8217;s 439)<\/li>\n<li>Cleanup (which precedes Prepare-Mixed) finalizes old-region candidates<\/li>\n<li>Prepare-Mixed transitions to Mixed GC phase<\/li>\n<li>Mixed pauses reclaim old regions aggressively when needed<\/li>\n<\/ul>\n<\/li>\n<li>Overall pattern: Young GC works harder (2.60% overhead vs 1.41%) but keeps promotions low, resulting in 59% less Mixed GC overhead (0.39% vs 0.96%).<\/li>\n<\/ul>\n<h2>Side-by-Side: Baseline vs <code>jaz<\/code><\/h2>\n<h3>Region Dynamics \u2014 Before GC (Eden)<\/h3>\n<p>Fig 1 vs Fig 2. Comparison: Baseline shows tight, stable Eden band (700-900 regions). <code>jaz<\/code> shows wider oscillation because it&#8217;s handling 22% higher throughput\u2014increased allocation rate from higher max-jOPS.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211859.133.webp\"><img decoding=\"async\" class=\"alignnone wp-image-232730 size-full\" src=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211859.133.webp\" alt=\"Baseline Before-GC region timeline showing high, tightly bounded Eden and Survivor bands with early spikes; Old remains elevated across the run.\" width=\"1556\" height=\"600\" srcset=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211859.133.webp 1556w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211859.133-300x116.webp 300w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211859.133-1024x395.webp 1024w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211859.133-768x296.webp 768w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211859.133-1536x592.webp 1536w\" sizes=\"(max-width: 1556px) 100vw, 1556px\" \/><\/a><\/p>\n<p>Figure 1: G1 Region States Over Time \u2014 Before GC (baseline)<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211809.010.webp\"><img decoding=\"async\" class=\"alignnone wp-image-232731 size-full\" src=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211809.010.webp\" alt=\"jaz Before-GC region timeline with small, regular Eden rises and low amplitude; bands are smooth and evenly spaced.\" width=\"1556\" height=\"600\" srcset=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211809.010.webp 1556w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211809.010-300x116.webp 300w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211809.010-1024x395.webp 1024w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211809.010-768x296.webp 768w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211809.010-1536x592.webp 1536w\" sizes=\"(max-width: 1556px) 100vw, 1556px\" \/><\/a><\/p>\n<p>Figure 2: G1 Region States Over Time \u2014 Before GC (with<code>jaz<\/code>)<\/p>\n<h3>Region Dynamics \u2014 After GC (Old)<\/h3>\n<p>Fig 3 vs Fig 4. Comparison: Baseline keeps Old gen at elevated plateau (600-900 regions) continuously. <code>jaz<\/code>&#8216;s dramatic sawtooth (200-1000 regions) proves efficient Mixed GC reclamation\u2014deep troughs demonstrate productive old gen cleanup, restoring headroom. Result: 265 Mixed GCs vs 778 (\u221266%) despite 22% higher throughput.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211929.174.webp\"><img decoding=\"async\" class=\"alignnone wp-image-232733 size-full\" src=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211929.174.webp\" alt=\"Baseline After-GC region timeline where Old remains at a high plateau with frequent peaks; Survivors persist at non-trivial levels.\" width=\"1556\" height=\"600\" srcset=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211929.174.webp 1556w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211929.174-300x116.webp 300w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211929.174-1024x395.webp 1024w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211929.174-768x296.webp 768w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211929.174-1536x592.webp 1536w\" sizes=\"(max-width: 1556px) 100vw, 1556px\" \/><\/a><\/p>\n<p>Figure 3: G1 Region States Over Time \u2014 After GC (baseline)<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211721.896.webp\"><img decoding=\"async\" class=\"alignnone wp-image-232732 size-full\" src=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211721.896.webp\" alt=\"jaz After-GC region timeline featuring deep Old troughs around ~200\u2013250 regions and gradual rises to peaks before being reclaimed again.\" width=\"1556\" height=\"600\" srcset=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211721.896.webp 1556w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211721.896-300x116.webp 300w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211721.896-1024x395.webp 1024w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211721.896-768x296.webp 768w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T211721.896-1536x592.webp 1536w\" sizes=\"(max-width: 1556px) 100vw, 1556px\" \/><\/a><\/p>\n<p>Figure 4: G1 Region States Over Time \u2014 After GC (with <code>jaz<\/code>)<\/p>\n<h3>GC Pause Envelope \u2014 Young<\/h3>\n<p>Fig 5 vs Fig 6. Comparison: Baseline shows 1,100 Young GCs with early spikes then ~80-130ms band. <code>jaz<\/code> shows 1,596 Young GCs (+45%) with wavy pattern ~15-250ms. More Young GC activity is positive\u2014it&#8217;s handling 22% higher throughput while keeping objects from promoting prematurely.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T212106.206-1.webp\"><img decoding=\"async\" class=\"alignnone wp-image-232741 size-full\" src=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T212106.206-1.webp\" alt=\"Baseline Young-GC scatter with early high-variance cluster, mid-run wavy band near ~80\u2013130 ms, and a small burst near the end.\" width=\"1556\" height=\"600\" srcset=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T212106.206-1.webp 1556w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T212106.206-1-300x116.webp 300w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T212106.206-1-1024x395.webp 1024w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T212106.206-1-768x296.webp 768w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-2025-12-09T212106.206-1-1536x592.webp 1536w\" sizes=\"(max-width: 1556px) 100vw, 1556px\" \/><\/a><\/p>\n<p>Figure 5: Young-only pauses (baseline)<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-86.webp\"><img decoding=\"async\" class=\"alignnone wp-image-232735 \" src=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-86.webp\" alt=\"jaz Young-GC scatter showing an early, tight band around ~70\u2013110 ms with waviness and small end-of-run taper.\" width=\"1384\" height=\"517\" srcset=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-86.webp 1205w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-86-300x112.webp 300w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-86-1024x382.webp 1024w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-86-768x287.webp 768w\" sizes=\"(max-width: 1384px) 100vw, 1384px\" \/><\/a><\/p>\n<p>Figure 6: Young-only pauses (with <code>jaz<\/code>)<\/p>\n<h3>GC Pause Envelope \u2014 Overlay (Concurrent Start + Prepare-Mixed + Mixed)<\/h3>\n<p>Fig 7 vs Fig 8. Comparison: Baseline shows dense, continuous Mixed activity (778 events) driven by Old gen&#8217;s elevated plateau. <code>jaz<\/code> shows episodic pattern with quiet stretches (265 events = \u221266%)\u2014efficient GC prevents Old gen buildup proactively, resulting in 59% lower Mixed overhead.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-94.webp\"><img decoding=\"async\" class=\"alignnone wp-image-232738 size-full\" src=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-94.webp\" alt=\"Baseline scatter of Mixed (blue), Prepare-Mixed (gray), and Concurrent-Start evacuation (magenta) showing dense activity and periodic higher outliers.\" width=\"1205\" height=\"600\" srcset=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-94.webp 1205w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-94-300x149.webp 300w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-94-1024x510.webp 1024w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-94-768x382.webp 768w\" sizes=\"(max-width: 1205px) 100vw, 1205px\" \/><\/a><\/p>\n<p>Figure 7: Prepare-Mixed + Mixed pauses with Concurrent Start overlay (baseline)<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-87.webp\"><img decoding=\"async\" class=\"alignnone wp-image-232736 size-full\" src=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-87.webp\" alt=\"jaz scatter of Mixed and Prepare-Mixed with lower heights and clear gaps between clusters; only occasional concurrent-start evacuation markers.\" width=\"1205\" height=\"450\" srcset=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-87.webp 1205w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-87-300x112.webp 300w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-87-1024x382.webp 1024w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-87-768x287.webp 768w\" sizes=\"(max-width: 1205px) 100vw, 1205px\" \/><\/a><\/p>\n<p>Figure 8: Prepare-Mixed + Mixed pauses with Concurrent Start overlay (with <code>jaz<\/code>)<\/p>\n<h3>GC Pause Envelope \u2014Humongous Starts Concurrent Marking<\/h3>\n<p>Fig 9 vs Fig 10. Comparison: Baseline shows clustered Humongous Starts Concurrent Marking events at warm-up and tail. <code>jaz<\/code> shows only ~10 isolated events\u2014large allocations are absorbed avoiding humongous-triggered marking, post-marking and Old gen pressure.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-95.webp\"><img decoding=\"async\" class=\"alignnone wp-image-232739 size-full\" src=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-95.webp\" alt=\"Baseline diamonds marking humongous-triggered Concurrent Start events clustered early and at end of run.\" width=\"1205\" height=\"600\" srcset=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-95.webp 1205w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-95-300x149.webp 300w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-95-1024x510.webp 1024w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-95-768x382.webp 768w\" sizes=\"(max-width: 1205px) 100vw, 1205px\" \/><\/a><\/p>\n<p>Figure 9: Humongous-trigger events (baseline). Early and tail clusters align with mixed-GC activity<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-89.webp\"><img decoding=\"async\" class=\"alignnone wp-image-232737 size-full\" src=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-89.webp\" alt=\"jaz diamonds for humongous-triggered Concurrent Start events appearing only as a few isolated points early and late.\" width=\"1205\" height=\"450\" srcset=\"https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-89.webp 1205w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-89-300x112.webp 300w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-89-1024x382.webp 1024w, https:\/\/devblogs.microsoft.com\/java\/wp-content\/uploads\/sites\/51\/2025\/12\/newplot-89-768x287.webp 768w\" sizes=\"(max-width: 1205px) 100vw, 1205px\" \/><\/a><\/p>\n<p>Figure 10: Humongous-trigger events (with <code>jaz<\/code>)\u2014rare, non-disruptive<\/p>\n<h2>Comparison Matrix<\/h2>\n<table style=\"width: 100%;\">\n<tbody>\n<tr>\n<td style=\"width: 19.9081%;\" width=\"126\"><strong>Aspect<\/strong><\/td>\n<td style=\"width: 24.5023%;\" width=\"156\"><strong>Baseline<\/strong><\/td>\n<td style=\"width: 27.2588%;\" width=\"174\"><strong>With <code>jaz<\/code><\/strong><\/td>\n<td style=\"width: 26.34%;\" width=\"168\"><strong>What Changed<\/strong><\/td>\n<\/tr>\n<tr>\n<td style=\"width: 19.9081%;\" width=\"126\">Throughput<\/td>\n<td style=\"width: 24.5023%;\" width=\"156\">Baseline<\/td>\n<td style=\"width: 27.2588%;\" width=\"174\"><code>jaz<\/code><\/td>\n<td style=\"width: 26.34%;\" width=\"168\">+22% peak throughput<\/p>\n<p>+15% at SLA<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 19.9081%;\" width=\"126\">Total GC Events<\/td>\n<td style=\"width: 24.5023%;\" width=\"156\">3,777 cycles<\/td>\n<td style=\"width: 27.2588%;\" width=\"174\">2,526 cycles<\/td>\n<td style=\"width: 26.34%;\" width=\"168\">\u221233% (1251 fewer events)<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 19.9081%;\" width=\"126\">Regions: Before GC (Eden)<\/p>\n<p>Fig. 1 \u2192 Fig. 2<\/td>\n<td style=\"width: 24.5023%;\" width=\"156\">Tight band (~700-900 regions)<\/p>\n<p>Eden already large at Young GC arrival<\/td>\n<td style=\"width: 27.2588%;\" width=\"174\">Wider oscillation<\/p>\n<p>Reflects higher allocation rate<\/td>\n<td style=\"width: 26.34%;\" width=\"168\">Handling 22% more work<\/p>\n<p>Dynamic sizing active<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 19.9081%;\" width=\"126\">Regions: After GC (Old)<\/p>\n<p>Fig. 3 \u2192 Fig. 4<\/td>\n<td style=\"width: 24.5023%;\" width=\"156\">Flat plateau (~600\u2013900 regions)<\/p>\n<p>Always elevated<\/td>\n<td style=\"width: 27.2588%;\" width=\"174\">Dramatic sawtooth (~200\u20131000 regions)<\/p>\n<p>Deep troughs restore headroom<\/td>\n<td style=\"width: 26.34%;\" width=\"168\">Efficient Young GC keeps promotions low<\/p>\n<p>Old cleanup prevents buildup<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 19.9081%;\" width=\"126\">Young GC Count<\/p>\n<p>Fig. 5 \u2192 Fig. 6<\/td>\n<td style=\"width: 24.5023%;\" width=\"156\">1,100 events<\/p>\n<p>1.41% overhead<\/td>\n<td style=\"width: 27.2588%;\" width=\"174\">1,596 events (+45%)<\/p>\n<p>2.60% overhead<\/td>\n<td style=\"width: 26.34%;\" width=\"168\">More Young GC is good\u2014handles higher throughput<\/p>\n<p>Handles transients efficiently in the Young gen<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 19.9081%;\" width=\"126\">Mixed GC Count<\/p>\n<p>Fig. 7 \u2192 Fig. 8<\/td>\n<td style=\"width: 24.5023%;\" width=\"156\">778 events<\/p>\n<p>0.96% overhead<\/p>\n<p>Continuous pattern<\/td>\n<td style=\"width: 27.2588%;\" width=\"174\">265 events (\u221266%)<\/p>\n<p>0.39% overhead (\u221259%)<\/p>\n<p>Episodic pattern<\/td>\n<td style=\"width: 26.34%;\" width=\"168\">Massive reduction in Old gen work<\/p>\n<p>Cadenced GC prevents reactive storms<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 19.9081%;\" width=\"126\">Humongous Events<\/p>\n<p>Fig. 9 \u2192 Fig. 10<\/td>\n<td style=\"width: 24.5023%;\" width=\"156\">41 clustered bursts\nat warm-up and tail<\/td>\n<td style=\"width: 27.2588%;\" width=\"174\">~10 isolated events\nSparse, absorbed<\/td>\n<td style=\"width: 26.34%;\" width=\"168\">Large allocations don&#8217;t trigger\nexcessive marking cycles<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Baseline Implications<\/h2>\n<p>In baseline, Young\u00a0 GC dump large volumes of live data to Old gen. Premature promotions lead to continuous Mixed GC work. Old then stays high, thresholds trip early and often, and Mixed\/concurrent activity becomes dense. JBB hits the system throughput ceiling early.<\/p>\n<ul>\n<li>High stop-the-world (STW) frequency across phases: Young dominates count; Mixed\/Prepare are effectively continuous once load stabilizes\u2014no long quiet stretches.<\/li>\n<li>Premature promotion tax: 2677 Concurrent Starts, Remark, Cleanup, Prepare-Mixed and Mixed GC events represent continuous Old gen collection work.<\/li>\n<li>Pattern: Stable but limited\u2014GC keeps up with load but cannot scale to higher throughput. G1 is catching up, not cruising.<\/li>\n<\/ul>\n<h2>The <code>jaz<\/code> Breakthrough: Efficient Young GC Enables Higher Throughput<\/h2>\n<p><code>jaz<\/code> achieves 22% higher peak throughput through resource-aware defaults that provide the capacity to handle increased load, combined with efficient GC that keeps it sustainable.<\/p>\n<h3>How jaz Works<\/h3>\n<ol>\n<li>Resource-aware defaults provide capacity for higher throughput:\n<ul>\n<li>Larger heap sizing based on available VM memory (16 GB)<\/li>\n<li>Dynamic heap management adapts to load phases<\/li>\n<li>More Eden headroom \u2192 can handle higher allocation rate from increased operations\/sec<\/li>\n<li>Result: System can sustain 22% higher max-jOPS without choking on memory pressure<\/li>\n<\/ul>\n<\/li>\n<li>Efficient Young GC keeps it sustainable:\n<ul>\n<li>1,596 Young GCs vs baseline&#8217;s 1,100 (+45% more cycles)<\/li>\n<li>2.60% overhead vs 1.41% (+1.19 percentage points)<\/li>\n<li>But: Handling 22% higher throughput\u2014more work per unit time<\/li>\n<li>Objects die in Eden\/Survivor instead of promoting to Old<\/li>\n<\/ul>\n<\/li>\n<li>Dynamic sizing + cadenced GC maintain headroom:\n<ul>\n<li>Creates breathing room for episodic Mixed GC to reclaim aggressively<\/li>\n<li>Prevents Old gen buildup, avoiding continuous Mixed GC tax seen in baseline<\/li>\n<li>Sawtooth pattern shows efficient heap usage: expand \u2192 promote \u2192 reclaim \u2192 repeat<\/li>\n<li>Result: Old gen sawtooth drops to ~200 regions (vs baseline&#8217;s 600-900 plateau)<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h2><code>jaz<\/code> Takeaways<\/h2>\n<ul>\n<li><strong>22% higher peak throughput: <\/strong><code>jaz<\/code> scales where baseline hits ceiling<\/li>\n<li><strong>15% better SLA performance:<\/strong> steadier latency under load<\/li>\n<li><strong>66% fewer Mixed GCs: <\/strong>265 vs 778 events\u2014massive reduction in expensive Old gen work<\/li>\n<li><strong>33% fewer total GC events: <\/strong>2,526 vs 3,777 cycles despite handling more work<\/li>\n<li><strong>Efficient Young GC strategy: <\/strong>More Young cycles (1,596 vs 1,100) but keeps promotions low<\/li>\n<\/ul>\n<h2>Conclusion: <code>jaz<\/code> Unlocks Higher Throughput on Azure VMs<\/h2>\n<p>This performance study shows that <code>jaz<\/code>\u00a0is more than a convenience wrapper\u2014it&#8217;s a resource-aware optimization pipeline that delivers measurable, significant improvements in real-world workloads:<\/p>\n<ul>\n<li>Sizes heap and generations appropriately, avoiding reactive warm-up churn.<\/li>\n<li>Stabilizes early GC behavior, tightening pause bands sooner.<\/li>\n<li>Reduces humongous-triggered marking moments, easing Mixed-GC pressure.<\/li>\n<li>Maintains GC cadence as load steps up, preventing premature promotions and high plateaus.<\/li>\n<li>Lifts overall throughput\/SLA metrics\u2014with the launcher as the only change.<\/li>\n<\/ul>\n<p>On Azure Linux\/Arm VMs with the Microsoft Build of OpenJDK, <code>jaz<\/code> consistently delivered:<\/p>\n<ul>\n<li><strong>Faster warm-up<\/strong><\/li>\n<li><strong>Higher sustained throughput<\/strong><\/li>\n<li><strong>Lower p99 response-time tails<\/strong><\/li>\n<li><strong>Tamed Old gen<\/strong> that repeatedly returns to a low post-GC watermark<\/li>\n<\/ul>\n<h2>What\u2019s Next<\/h2>\n<p>We\u2019re extending <code>jaz<\/code> beyond a great default into a continuously adaptive launcher:<\/p>\n<ul>\n<li>JVM configuration profiles: pre-vetted, resource-aware profiles for common VM and container shapes.<\/li>\n<li>Continuous tuning: light-touch runtime feedback to stay stable under shifting pressure\u2014no app changes.<\/li>\n<li>Telemetry: opt-in summaries that inform on-the-fly decisions and explain \u201cwhy `<code>jaz<\/code> chose <em>X<\/em>.\u201d<\/li>\n<li>AppCDS: optional archive generation\/consumption to shorten warm-up and smooth early allocation\/JIT behavior.<\/li>\n<li>Leyden alignment: play nicely with Leyden\u2019s startup\/profile optimizations so <code>jaz<\/code>\u00a0can pick the right combo per workload.<\/li>\n<\/ul>\n<p><strong>Stay tuned<\/strong>\u2014<code>jaz<\/code> is becoming a foundation for <strong>self-optimizing<\/strong> Java runtimes on Azure.<\/p>\n<h2>Appendices<\/h2>\n<h3><a id=\"appendix-a\"><\/a>\u00a0Appendix A \u2014 Test Environment Preparation &amp; Methodology (Reproducibility)<\/h3>\n<p>To ensure clean, comparable results across baseline and <code>jaz<\/code> runs, each iteration followed this protocol.<\/p>\n<h4>Cache Reset Per Run<\/h4>\n<pre class=\"prettyprint language-default\"><code class=\"language-default\">sync                                # force pending disk writes\r\necho 3 &gt; \/proc\/sys\/vm\/drop_caches   # drop page cache, dentries, and inode caches\r\n<\/code><\/pre>\n<p>We reset Linux page cache before each trial to remove cross-run noise from warm caches. <code>drop_caches<\/code> does not discard dirty data; <code>sync<\/code> persists it first. <em>(Run as <code>root<\/code> \/ via <code>sudo<\/code>.)<\/em><\/p>\n<h4>Repeated Trials<\/h4>\n<p>Both configurations (baseline vs <code>jaz<\/code>) were executed multiple times across Microsoft Build of OpenJDK versions. Runs showed highly consistent GC behavior, region-state evolution, and throughput trends.\n<a id=\"appendix-b\"><\/a><\/p>\n<h3>Appendix B \u2014 From JBB Phases to GC Pauses<\/h3>\n<p>JBB is a complex tool designed to simulate a 3-tier system and measure the performance of the JVM on a given OS + hardware. A full run cycles through several distinct operational phases, each with unique performance and memory characteristics that are crucial for performance engineers to understand for effective tuning. Let\u2019s get a quick look at how JBB phases shape allocation\/promotion pressure.<\/p>\n<h4>How JBB Drives Load and GC (Phase Guide)<\/h4>\n<p>Why this matters: JBB pushes the JVM through distinct load phases that shift allocation and promotion pressure. Understanding these phases helps when analyzing GC logs or perf telemetry, because JVM behavior changes dramatically from startup to peak load and final shutdown.<\/p>\n<h5>Phase 1: Warm-up \/ HBIR Search<\/h5>\n<p>This initial phase is all about getting the system ready and estimating capacity.<\/p>\n<ul>\n<li>Activity: Threads come online, the JVM performs JIT compilation, and profiling begins. The benchmark searches for the High Bound Injection Rate (HBIR), a preliminary estimate of the maximum throughput.<\/li>\n<li>Memory behavior: This phase is characterized by high bursts of object allocation and moderate, but rising, promotion pressure as the app code is loaded, initialized, and begins processing initial transactions.<\/li>\n<\/ul>\n<h5>Phase 2: The RT-Curve Build<\/h5>\n<p>This is the core measurement phase where the benchmark systematically increases the load to build the Response-Throughput (RT) curve.<\/p>\n<ul>\n<li>Activity: The load (Injection Rate or IR) increases stepwise. Performance metrics are rigorously collected at each step.<\/li>\n<li>Memory behavior: The system experiences sustained and rising allocation pressure. More transient (short-lived) objects are created, and the promotion pressure increases significantly as the system approaches maximum capacity.<\/li>\n<\/ul>\n<p><div class=\"alert alert-primary\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>The Relationship Between jOPS and Memory Pressure<\/strong><\/p><\/p>\n<ul>\n<li>Higher throughput \u21d2 higher allocation rate: jOPS counts ops\/sec; more ops create more objects per unit time.<\/li>\n<li>Promotion pressure rises with load: to sustain higher IR, GC runs more often; survivors are promoted to Old sooner.<\/li>\n<li>The memory subsystem must handle this churn without excessive pauses or fragmentation.<\/li>\n<li>Key SPEC metrics:\n<ul>\n<li><strong>max-jOPS<\/strong> \u2014 highest throughput at the last successful IR level before the first RT-curve failure.<\/li>\n<li><strong>critical-jOPS<\/strong> \u2014 geometric mean of jOPS at p99 response time across five SLA points (10, 25, 50, 75, 100 ms).<\/div><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h5>Phase 3: Validation \/ Tail<\/h5>\n<p>The final phase winds down the benchmark run and validates the data collected.<\/p>\n<ul>\n<li>Activity: The run concludes with report\/validation segments following the RT phase.<\/li>\n<li>Memory behavior:\u00a0Allocation pressure tapers; promotion pressure declines which is typical of a ramp-down in workload.<\/li>\n<\/ul>\n<h3><a id=\"appendix-c\"><\/a>\u00a0Appendix C \u2014 GC Cheat Sheet (Quick Primer)<\/h3>\n<p><div class=\"alert alert-success\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Lightbulb\"><\/i><strong>Overarching GC goal<\/strong><\/p>Get out of the way \u2014 maximize mutator time, keep pauses predictable and short, avoid premature promotion\/copying, and reclaim promptly.<\/div><\/p>\n<ul class=\"gc-primer\">\n<li><strong>Eden<\/strong>: where most new objects are born. It fills quickly and is emptied during Young (and later Mixed) GCs.\n<div class=\"alert alert-success\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Lightbulb\"><\/i><strong>GC goal<\/strong><\/p>Keep Eden large enough that most short-lived objects die there, but not so large that evacuations must copy a big live set, overflow Survivor or force premature tenure.<\/div><\/li>\n<li><strong>Survivor:<\/strong> short-term holding for objects that just survived a Young GC (they \u201cage\u201d here).\n<div class=\"alert alert-success\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Lightbulb\"><\/i><strong>GC goal<\/strong><\/p>Let objects age briefly and avoid premature promotion to Old.<\/div><\/li>\n<li><strong>Old<\/strong>: promoted medium\/long-lived objects. Growth here drives Mixed GCs.\n<div class=\"alert alert-success\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Lightbulb\"><\/i><strong>GC goal<\/strong><\/p>Keep only the long-lived live data set (LDS); minimize promotion churn and lower region waste\/fragmentation.<\/div><\/li>\n<li><strong>Humongous<\/strong>: very large objects (\u226550% of a region) allocated directly into Old as one or more contiguous humongous regions, bypassing Young.\n<div class=\"alert alert-danger\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--ErrorBadge\"><\/i><strong>Gotcha<\/strong><\/p>Short-lived\/bursty humongous allocations can fragment Old or force extra GC\/cycle work to find contiguous space; reclaiming them eagerly is key.<\/div><\/li>\n<\/ul>\n<h3><a id=\"appendix-d\"><\/a>Appendix D \u2014 What the Figures Show (Baseline, <code>jaz<\/code>)<\/h3>\n<h4><strong>Region Composition Over Time<\/strong><\/h4>\n<p>These plots count regions by role and reveal how the regions react across JBB phases.<\/p>\n<ul>\n<li>Fig 1\u20133 (baseline): Before GC, After GC<\/li>\n<li>Fig 2\u20134 (<code>jaz<\/code>): Before GC, After GC<\/li>\n<\/ul>\n<p>\u201cBefore GC\u201d = right before a collection (peaks). \u201cAfter GC\u201d = immediately after (valleys).<\/p>\n<h4>Pause-time Envelope (STW Pauses Over Time)<\/h4>\n<p>These plots show frequent STW events across the run, with variance shifting as JBB moves through Warm-up \u2192 SLA \u2192 Tail.<\/p>\n<ul>\n<li>Young (teal): Fig 5 (baseline), Fig 6 (<code>jaz<\/code>)<\/li>\n<li>Overlay (Concurrent-Start + Prepare-Mixed + Mixed markers): Fig 7 (baseline), Fig 8 (<code>jaz<\/code>)<\/li>\n<li>Concurrent-Start due to humongous allocation (diamond markers): Fig 9 (baseline), Fig 10 (<code>jaz<\/code>)<\/li>\n<\/ul>\n<p>Each dot is an STW pause (y-axis = ms, x-axis = runtime (s)). <em>These pairings let you compare pause frequency\/ceilings and variance shifts across the same JBB phases.<\/em><\/p>\n<p><div class=\"alert alert-primary\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>Note on Remark\/Cleanup<\/strong><\/p>These are short closing STW phases of a concurrent marking cycle. Remark finalizes marking bookkeeping while Cleanup tidies metadata and remembered sets and completes any leftover work before the next Mixed GCs begin. Once marking stabilizes, they\u2019re typically tiny and flat in these runs, so we omit separate plots for brevity.<\/div><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In our previous blog we introduced Azure Command Launcher for Java (jaz) \u2014a safe, resource-aware way to launch the JVM without hand-tuning dozens of flags. This follow-up shares performance results, focusing on how jaz affects G1 behavior, heap dynamics, and pause characteristics under a long-running, allocation-intensive workload: SPECjbb 2015 (JBB). Test bed: 4-vCPU, 16-GB Azure [&hellip;]<\/p>\n","protected":false},"author":9384,"featured_media":227205,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-232717","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java"],"acf":[],"blog_post_summary":"<p>In our previous blog we introduced Azure Command Launcher for Java (jaz) \u2014a safe, resource-aware way to launch the JVM without hand-tuning dozens of flags. This follow-up shares performance results, focusing on how jaz affects G1 behavior, heap dynamics, and pause characteristics under a long-running, allocation-intensive workload: SPECjbb 2015 (JBB). Test bed: 4-vCPU, 16-GB Azure [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/java\/wp-json\/wp\/v2\/posts\/232717","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/java\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/java\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/java\/wp-json\/wp\/v2\/users\/9384"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/java\/wp-json\/wp\/v2\/comments?post=232717"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/java\/wp-json\/wp\/v2\/posts\/232717\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/java\/wp-json\/wp\/v2\/media\/227205"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/java\/wp-json\/wp\/v2\/media?parent=232717"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/java\/wp-json\/wp\/v2\/categories?post=232717"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/java\/wp-json\/wp\/v2\/tags?post=232717"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}