Working with Performance Optimization
1. Profiling Java Applications
| Tool | Use |
|---|---|
| JFR (Java Flight Recorder) | Built-in, low overhead |
| JMC (Mission Control) | Analyze JFR recordings |
| async-profiler | Sampling, flame graphs |
| YourKit, JProfiler | Commercial profilers |
2. Using JMH for Benchmarking
Example: JMH benchmark
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class StringConcatBench {
@Benchmark
public String plus() { return "a" + 1 + "b"; }
@Benchmark
public String builder() { return new StringBuilder("a").append(1).append("b").toString(); }
}
| Annotation | Use |
|---|---|
@Benchmark | Mark method |
@State | Per-thread/benchmark state |
@Setup / @TearDown | Lifecycle |
Blackhole | Prevent dead-code elimination |
3. Optimizing String Operations
| Practice | Detail |
|---|---|
| Concat in loops | Use StringBuilder |
Java 9+ + | Compiles to invokedynamic — usually fast |
| String.intern() | Save memory for repeated strings (caution) |
| Compact strings | Java 9+ uses byte[] for Latin-1 strings |
4. Optimizing Collection Performance
| Tip | Detail |
|---|---|
| Pre-size | new ArrayList<>(expectedSize) |
| Right type | ArrayList for index access; LinkedList for queue |
| EnumMap/EnumSet | Far more efficient than HashMap for enums |
| Avoid boxing | Use IntStream / primitive arrays for hot paths |
5. Reducing Object Creation
| Practice | Detail |
|---|---|
| Reuse immutables | Cache constants |
| Avoid auto-boxing | Use primitive specializations |
| Builder reuse | Reset and reuse where safe |
| Escape analysis | JIT may stack-allocate small short-lived objects |
6. Using Object Pooling Wisely
| When | Detail |
|---|---|
| Good for | Expensive resources (connections, threads) |
| Avoid for | Small/short-lived objects (GC is faster) |
7. Optimizing I/O Operations
| Practice | Detail |
|---|---|
| Buffered streams | BufferedReader/Writer/InputStream/OutputStream |
| NIO channels | For high throughput |
| Memory-mapped files | FileChannel.map() for large files |
| Async I/O | Java 21+: virtual threads make sync APIs scale |
8. Tuning JVM Parameters
| Param | Effect |
|---|---|
-Xmx / -Xms | Heap; set equal to avoid resizing |
-XX:+UseG1GC / +UseZGC | GC choice |
-XX:MaxGCPauseMillis | Pause target |
-XX:+UseStringDeduplication | Save memory in G1 |
9. Understanding Escape Analysis
| Optimization | Detail |
|---|---|
| Scalar replacement | Decompose unescaped objects into fields |
| Stack allocation | Conceptual; usually scalar replaced |
| Lock elision | Skip sync on thread-local objects |
10. Using Inline Caches
| Aspect | Detail |
|---|---|
| Monomorphic | 1 receiver type — inlined fast path |
| Bimorphic | 2 types — inlined with check |
| Megamorphic | Many types — vtable dispatch |
| Practice | Avoid swarms of types behind virtual calls |
11. Optimizing Lambda Performance
| Aspect | Detail |
|---|---|
| Stateless lambdas | Cached as singletons |
| Capturing lambdas | Allocate per call — avoid in hot loops |
| JIT | Inlines monomorphic call sites |
12. Reducing GC Pressure
| Strategy | Detail |
|---|---|
| Reuse buffers | ByteBuffer/StringBuilder |
| Pre-size collections | Avoid resize churn |
| Avoid boxing | Primitive arrays/streams |
| Off-heap | Direct ByteBuffer for large data |
| Profile allocations | JFR allocation profiling |
13. Understanding Branch Prediction
| Practice | Detail |
|---|---|
| Predictable branches | Sort data to make branches consistent |
| Branchless | Use bit manipulation when feasible |
| Hot path first | Common case as straight-line code |