Rung 6 β€” JVM Performance Case StudyΒΆ

Target month: Shipped by end of M9 = end of March 2027 Calendar deadline: March 31, 2027 Phase alignment: Phase 06 β€” JVM Internals & Performance Signal level: Deep-JVM β†’ proves you can find performance bugs, not just write them


What It IsΒΆ

A public repo + a blog post that document a real, before/after JVM performance investigation on your own Rung 5 code (or a comparable Java workload). The story is: β€œI had a program. It was slow. Here is exactly how I made it fast.” With numbers. With flame graphs. With commit-by-commit optimization.

This rung is deliberately structured as a case study, not a library. The artifact is the narrative β€” the repo is the appendix.

The workload you profileΒΆ

Use the virtual-threads HTTP-fanout benchmark from Rung 5 as your target. It is:

  • Real enough to have interesting bottlenecks

  • Small enough to fully understand

  • Already in your portfolio, so this rung reinforces Rung 5

If Rung 5 didn’t produce a rich-enough workload, use Artifact A (rate limiter) under contention β€” the sliding-window-log implementation is often quietly slow, and profiling it is instructive.

The investigation loop (5-8 iterations)ΒΆ

Each iteration is a tagged git commit + a section in the blog post:

  1. Establish a baseline. Run the benchmark. Capture: throughput, p50/p95/p99 latency, GC pause time, heap usage, allocation rate.

  2. Profile. Use async-profiler to capture a CPU flame graph and an allocation flame graph. Attach the .html outputs to the repo.

  3. Read the graph. Identify the hot method, hot allocation site, or GC pressure source.

  4. Form a hypothesis. Write it in the commit message before making the change.

  5. Change one thing. One commit = one intervention. Rerun the benchmark. Capture new numbers.

  6. Verify or reject. If the numbers improved, keep the change. If they didn’t, revert and note the negative result.

Aim for 5-8 iterations. Not every iteration needs to succeed β€” documented failures are as valuable as documented successes.

Suggested interventions to exploreΒΆ

(Pick 5-8; not all will apply to your workload)

  • Replace String concatenation in hot path with StringBuilder

  • Replace boxed collections with primitive-specialized ones (Eclipse Collections or fastutil)

  • Tune GC: G1 vs ZGC vs Parallel for this workload

  • Increase or decrease heap size, observe GC pause impact

  • Escape-analysis-friendly refactor: eliminate short-lived objects

  • Replace HashMap with ConcurrentHashMap (or vice versa) based on contention profile

  • Remove synchronized in favor of LongAdder where appropriate

  • Cache Thread.currentThread() reads across a hot loop

  • Replace reflection with MethodHandle or LambdaMetafactory

  • Tune -XX:CompileThreshold or use -XX:+PrintCompilation diagnosis

Where To PublishΒΆ

  • Repo: github.com/RaghulR2003/jvm-perf-case-study β€” public, pinned. Consider making this a fork of your concurrency-playground repo so the narrative is preserved.

  • Blog: Hashnode primary. Title: β€œMaking a Java 21 fanout service 4x faster β€” flame graphs, commits, and one very surprising synchronized.” (Use your actual multiplier.)

  • Cross-post: dev.to canonical to Hashnode. Reddit r/java once, r/programming once. Foojay for Java-audience amplification.

  • LinkedIn: Long-form post with the flame graph as the hero image. Highlight the most surprising finding β€” those are the posts that circulate.

Acceptance CriteriaΒΆ

  • Repo exists, public, pinned

  • 5-8 numbered iteration commits, each with: before/after numbers in commit message, flame graphs attached, hypothesis stated

  • benchmarks/ directory with all JMH runs, results-v0.csv through results-v8.csv

  • flame-graphs/ directory with async-profiler HTML output for each iteration

  • Repo README has a summary table: iteration, intervention, throughput before, throughput after, verdict (kept/reverted)

  • Blog post published with the 3 most instructive iterations documented in detail, including at least one documented negative result

  • All numbers include hardware + JDK build + GC + heap size (repeated from Rung 5’s discipline)

  • Async-profiler installed, working, documented in a PROFILING.md

  • Repo demonstrates at least one GC-related intervention (GC change or allocation reduction)

  • Repo demonstrates at least one JIT-related insight (e.g., -XX:+PrintCompilation output analyzed)

  • Blog post attracts 50+ views in 30 days OR 25+ reactions on LinkedIn

Signal It SendsΒΆ

  • You can debug production. The M13 pitch says β€œfrom OutOfMemoryError to deployment.” This rung is the OutOfMemoryError half. study partners will read the repo and know you’ve held a flame graph in your hands.

  • You know what a flame graph means. Most Java engineers cannot read one. Publishing one, with commentary, immediately places you in the top 10%.

  • You know when to use ZGC. By M9, ZGC is the default recommendation for low-latency Java services. Having tuned GC for your own workload proves you understand why it matters.

  • You publish negative results. This is a strong senior-signal. Junior engineers only publish wins; senior engineers publish reverts because the reverts prove the process was rigorous.

  • You write about hard things. JVM internals blog posts have low supply and high demand. This post will accumulate SEO value for years.

Common Failure ModesΒΆ

  • Optimizing without profiling. β€œI bet if I switch to LongAdder it’ll be faster.” Maybe. Prove it with a flame graph first, then measure the change. Every intervention needs a profile as its justification.

  • Micro-benchmarking traps. Running a tight loop 1M times without warmup, dead-code elimination, or blackhole. Every measurement uses JMH properly or it doesn’t count. This is the Rung-5 discipline continued.

  • Cherry-picking wins. Only publishing the interventions that worked. Read a senior-level Java performance post β€” the failed attempts are the interesting part. Include yours.

  • Skipping GC analysis. GC is often the largest lever. If your case study never touches GC, you’ve dodged the hardest part of JVM performance.

  • Flame graph, no story. Attaching flame graphs without narrative. The graph is evidence β€” the reading of the graph is the artifact. Write the reading.

  • Running on your laptop under load. Your laptop is thermally throttled, has background processes, and shares a CPU with your browser. Use a dedicated machine or a cloud VM. Document which.

Time EstimateΒΆ

  • Async-profiler setup + baseline profiling: ~4 hours

  • 5-8 iterations, each ~4 hours (change + benchmark + profile + commit): ~28 hours

  • GC tuning experiments specifically: ~5 hours

  • JIT-related investigation: ~4 hours

  • Charts, tables, results wrangling: ~4 hours

  • Blog post (this one is difficult β€” you’re translating hard technical content): ~15 hours

  • README, PROFILING.md, LinkedIn post: ~4 hours

  • Total: ~64 hours over 4-5 weeks (~14-16 hours/week during M9)

M9 is a heavy month. This rung deserves the full month.

PrerequisitesΒΆ

  • Rung 5 shipped (this rung profiles Rung 5’s code)

  • All files in 06_jvm_internals_performance/ read and worked through

  • Async-profiler installed and working on your machine (10-30 min setup)

  • JMH already in your workflow from Rung 5

  • Read: β€œJava Performance” by Scott Oaks (2nd ed.) β€” chapters 5-8 minimum

  • Familiarity with G1 and ZGC log outputs (know what Concurrent Mark means, know what Pause Full means)

  • Access to a stable benchmarking machine (dedicated laptop, home server, or EC2 c-series instance)

Stretch Goals (Optional)ΒΆ

  1. Add a JFR + Mission Control section. Async-profiler is the primary tool; JFR is the JDK-native alternative. Showing both signals fluency across the toolchain.

  2. Publish a follow-up post: β€œHow to read a Java flame graph in 5 minutes.” This is an educational companion piece. Educational posts pull heavier long-tail traffic than case studies. Two posts, compounding SEO.

  3. Contribute a real-world perf fix to an OSS project. Once you have the profiling instinct, look at a mid-size Java OSS repo (Micronaut, jOOQ, Jackson) and find a documented performance issue. Even one merged PR is a career-marker.


Return to README.md Β· Next: 07_rung7_url_shortener_service.md