Java Performance Tuning
Java(TM) - see bottom of page
Our valued sponsors who help make this site possible
JProfiler: Get rid of your performance problems and memory leaks!
Training online: Concurrency, Threading, GC, Advanced Java and more ...
Tips July 2026
|
JProfiler
|
|
Get rid of your performance problems and memory leaks!
|
|
JProfiler
|
|
Get rid of your performance problems and memory leaks!
|
|
|
Back to newsletter 308 contents
https://www.youtube.com/watch?v=w3qO-uzwTUc
Scotty I need Warp Speed - Ways to improve JVM startup and warmup (Page last updated June 2026, Added 2026-07-27, Author Gerrit Grunwald, Publisher Bulgarian Java User Group). Tips:
- Separate JVM startup from application warm-up; they are different problems with different fixes.
- Measure ?startup? as time to first response, but remember first response may still be slow until hot code is compiled.
- Reduce class-loading cost: large frameworks load thousands of classes, and file I/O plus archive decompression add measurable startup time.
- Use exploded JARs instead of fat JARs when startup matters.
- Use AppCDS on JDK 12+ to pre-load application classes into a JVM-friendly archive. From JDK24+ project Leyden gives even better startup gains through cached loading/linking and profile reuse.
- Some startup optimizations, such as in the article benchmark, can benefit noticeably from more CPU.
- Use jlink to build a smaller custom runtime instead of deploying a full JRE/JDK.
- Benchmark startup on a clean VM or restarted OS; Linux filesystem caching can make repeated runs misleadingly fast.
- CRaC can give you the fastest JVM startup by restoring from a warmed checkpoint. Create CRaC checkpoints after enough warm-up if you need both fast startup and hot code.
https://www.youtube.com/watch?v=l30BJZ7joCI
When Benchmarks go Bad: What I learned from measuring performance wrong (Page last updated July 2026, Added 2026-07-27, Author Holly Cummins, Publisher NLJUG). Tips:
- Verify you are measuring what you think you are measuring.
- Prioritize reproducibility when comparing two implementations; otherwise you may just be comparing noise.
- Do not trust laptop benchmark results; thermal throttling and power management make them highly variable. Control CPU behavior for lab benchmarks: disable Turbo Boost/Speed Shift and set the governor to "performance".
- Clear filesystem and container caches between runs when cached state would distort the result.
- Avoid running the load generator on the same resources as the app unless you carefully isolate CPU and memory affinity.
- Use core pinning/task isolation when sharing hardware between load generator and system under test.
- Balance reproducibility with realism; a perfectly controlled benchmark may be too unlike production to guide real decisions.
- Warm up the application before measuring steady-state performance.
- Use production-like data volumes; empty or stale database images can completely change results.
- Define what "faster" means before benchmarking: throughput, latency, memory footprint, startup, elasticity, cost, or sustainability.
- For response times, measure distributions such as p99, not just averages.
- Avoid load tools with coordinated omission problems.
- Measure RSS, not just JVM heap, when comparing memory footprint.
- Add observability to benchmarks: track utilization, saturation, and errors for CPU, disk, network, database, and app resources.
https://www.youtube.com/watch?v=pLrD6dxvNQQ
Java JVM Performance Engineer: 131 Java JVM Real-World Production Incidents (Page last updated May 2026, Added 2026-07-27, Author Ace Interviews, Publisher Ace Interviews). Tips:
- Keep heap predictable, sawtooth between upper and lower flat lines.
- Prefer streaming, pagination, bounded buffers, and single-pass aggregation rather than materializing large datasets.
- OOMs are often caused by downstream latency, unbounded queues, missing cache eviction, or retained request backlogs.
- Always put eviction policies on caches; avoid raw unbounded map caches in production.
- Use streaming parsers for large JSON/XML payloads; avoid full ObjectMapper/DOM materialization for huge documents.
- Close JDBC ResultSet, Statement, and Connection with try-with-resources.
- Clear ThreadLocal values in a finally block after every request; prefer ScopedValue on modern Java where suitable.
- For Hibernate batch jobs, call flush() and clear() regularly, or use StatelessSession for large read-heavy processing.
- Size Kafka batches to fit the JVM's young generation; reduce max.poll.records and fetch.max.bytes when polls cause old-gen pressure.
- Keep high-cardinality values like customer_id out of Micrometer metrics; use traces or structured logs instead.
- Use JFR, heap histograms, and diagnostic volumes before taking huge heap dumps in production.
- Monitor ?heap after GC? trends; a rising post-GC baseline is a stronger leak signal than raw heap usage.
- Tune GC from small changes and measurement; change one or two flags at a time and compare with a production-like baseline.
- Watch GC overhead; if time spent in GC exceeds about 10-20%, the JVM may be close to a thrashing failure.
- Stay below the compressed-oops cliff, usually around 31G, or jump much higher; avoid the 32G-40G heaps.
- For very large heaps or strict latency targets, consider ZGC or Shenandoah, but leave enough heap headroom for allocation bursts.
- Disable Linux Transparent Huge Pages for latency-sensitive JVMs; use explicit/static huge pages if needed.
- Use flame graphs or async-profiler to find real CPU hotspots before optimizing.
- Replace reflection-heavy mappers like BeanUtils in hot paths with generated mapping such as MapStruct.
- Use async logging appenders with bounded buffers and discarding rules for non-critical logs.
- Keep thread pools bounded and monitored; every production queue should have a maximum size and a backpressure policy.
- Never block Netty/Reactor event-loop threads; move blocking calls to boundedElastic or a dedicated pool.
- Do not hold database connections while making external network calls.
- Use bulkheads and circuit breakers so one slow dependency cannot consume all request threads.
- Always release ReentrantLock in finally; prefer tryLock with timeout for failure isolation.
- Cap direct memory with -XX:MaxDirectMemorySize; Kubernetes sees RSS, not just Java heap.
- Enable Native Memory Tracking in production, at least summary, so native leaks are diagnosable.
- For faster startup, use AppCDS, narrow Spring component scanning, Spring context indexing, and build CDS archives inside the final image.
- Warm new JVM nodes before sending full traffic; use load-balancer slow start to avoid cold JIT saturation.
- For virtual threads, avoid synchronized I/O, cap concurrency with semaphores, replace heavy ThreadLocal usage, and isolate native blocking calls.
- Prevent cache stampedes with coalesced loading, such as computeIfAbsent, so only one thread reloads a missing value.
- Enable G1 string deduplication when duplicate strings dominate old-gen memory: -XX:+UseStringDeduplication.
- Avoid huge on-heap arrays; split large buffers into smaller chunks or move them off-heap.
- Watch G1 humongous regions; if they grow beyond about 10% of heap, region sizing or allocation design is likely wrong.
- Keep short-lived bursts in young gen; premature promotion usually means survivor or young-gen sizing is too small.
- Use async-profiler allocation profiling to find the exact lines creating object churn.
- If app pauses exceed GC pauses, investigate safepoint synchronization, long counted loops, or JNI calls.
- Test regexes against hostile ?evil strings?; avoid nested quantifiers on unbounded input.
- Use TLS session resumption and edge termination to reduce JVM CPU spent on handshakes.
- Explore alternative compression algorithms (LZ4, Snappy, GZIP) for high-throughput when CPU is the bottleneck.
- Use Thread.onSpinWait() plus backoff for spin loops; tight busy-waits waste cores.
- Give distributed locks a TTL or lease; a lock without expiry can halt business indefinitely.
- Use -XX:+ExitOnOutOfMemoryError when a damaged JVM should die quickly and let Kubernetes restart it.
Jack Shirazi
Back to newsletter 308 contents
Last Updated: 2026-07-27
Copyright © 2000-2026 Fasterj.com. All Rights Reserved.
All trademarks and registered trademarks appearing on JavaPerformanceTuning.com are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries. JavaPerformanceTuning.com is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
URL: http://www.JavaPerformanceTuning.com/news/newtips308.shtml
RSS Feed: http://www.JavaPerformanceTuning.com/newsletters.rss
Trouble with this page? Please contact us