Optimizing Performance
1. Understanding JIT Compilation
| Stage | Detail |
|---|---|
| Interpreter | Runs bytecode directly |
| C1 (client) | Fast compile, light optimization |
| C2 (server) | Aggressive optimization |
| Graal | Optional Java-written JIT |
2. Understanding Tiered Compilation
| Tier | Use |
|---|---|
| 0 | Interpreter |
| 1–3 | C1 with varying profiling |
| 4 | C2 (full optimization) |
| Default | On since JDK 8 |
3. Using Inlining Optimization
| Flag | Effect |
|---|---|
-XX:MaxInlineSize=N | Cold method size cap (bytes) |
-XX:FreqInlineSize=N | Hot method size cap |
-XX:InlineSmallCode=N | Native size cap |
| Diagnostic | -XX:+PrintInlining |
4. Understanding Escape Analysis Optimization
| Optimization | Result |
|---|---|
| Scalar replacement | Object split into local vars |
| Stack allocation | Object on stack (rarely realized) |
| Lock elision | Drop synchronized on non-escaped locks |
5. Using StringBuilder for Concatenation
| Code | Compiled As |
|---|---|
| a + b + c (single expr) | InvokeDynamic StringConcatFactory (Java 9+) |
| Loop concat with += | StringBuilder per iteration — slow |
| Manual StringBuilder | Best for loops |
6. Minimizing Object Creation
| Pattern | Use |
|---|---|
| Reuse buffers | ByteBuffer pools |
| Primitive specializations | IntStream, LongStream |
| Avoid autoboxing | Use Map<Integer> only when needed |
| Static immutables | Hoist constants out of methods |
7. Using Primitive Types vs Wrappers
| Type | Memory |
|---|---|
int | 4 bytes |
Integer | 16 bytes (header + value + padding) |
| int[1M] | ~4 MB |
| Integer[1M] | ~20 MB + GC pressure |
8. Optimizing Collections
| Tip | Detail |
|---|---|
| Pre-size | new ArrayList<>(n) avoids resizes |
| Initial capacity | HashMap: load=0.75; size = expected/0.75 |
| EnumMap / EnumSet | Bit-packed; use for enum keys |
| Avoid LinkedList | ArrayDeque is faster for queues |
9. Using Lazy Initialization
| Pattern | Use |
|---|---|
| Initialization-on-demand holder | Thread-safe via class init lock |
| Double-checked locking | Use volatile field |
| StableValue (preview) | Java 24+ replacement |
10. Caching Expensive Operations
| Tool | Use |
|---|---|
| ConcurrentHashMap.computeIfAbsent | Atomic memoize |
| Caffeine | High-perf in-process cache |
| SoftReference cache | GC-sensitive (avoid for hot data) |
11. Understanding False Sharing
| Concept | Detail |
|---|---|
| Cache line | Typically 64 bytes |
| Issue | Two threads write to different fields in same line → invalidation |
| Symptom | Scaling failure under high core count |
12. Using @Contended Annotation
Example: Padding hot fields
@jdk.internal.vm.annotation.Contended
class Counter { volatile long value; }
| Aspect | Detail |
|---|---|
| Flag | -XX:-RestrictContended for app code |
| Effect | Pads field to its own cache line |
| Use | Rare; verify with JMH |
13. Profiling Hotspots
Hotspot Profiling Workflow
- Run with realistic load
- Capture JFR or async-profiler sample
- Generate flame graph
- Identify top stacks and inefficient algorithms
- Verify allocation pressure separately (alloc profile)
- Microbench specific hot paths with JMH
| Anti-pattern | Effect |
|---|---|
| Premature optimization | Wasted effort, complex code |
| Optimizing without profile | Often wrong target |