Learn how the JIT compiler and HotSpot optimize frequently-run code paths for performance.
Let the JVM allocate objects on the stack (when safe) by writing escape-friendly code patterns.
Use IntStream/LongStream/DoubleStream to avoid boxing overhead in numeric pipelines.
Understand visibility, happens-before, volatile, and atomic operations for correct concurrency.
Use `var` for local inference where readability is preserved; avoid overuse in public APIs.
Use `record` for immutable data carriers to reduce boilerplate and express intent clearly.
Restrict subclassing with `sealed`/`permits` to make exhaustive handling safer and clearer.
Modularize large apps to improve encapsulation, startup, and maintainability.
Use `ConcurrentHashMap`, `CopyOnWriteArrayList`, and `ConcurrentLinkedQueue` for thread safety.
Compose async tasks non-blockingly with `CompletableFuture` and its completion stages.
Understand backpressure, publishers/subscribers and use Project Reactor or RxJava for reactive streams.
Study virtual threads and structured concurrency to simplify massively concurrent code.
For very large datasets or low GC pause targets, consider off-heap storage (ByteBuffer, native libs).
Always measure with profilers (VisualVM, async-profiler) to find real bottlenecks.
Pick the right GC (G1, ZGC, Shenandoah) and tune heap settings for latency or throughput goals.
Reuse objects, use primitives, and prefer pooling only when it improves real performance.
Understand isolation, memory leaks, and lifecycle when loading classes dynamically.
Validate types, use whitelists, or avoid Java serialization; prefer safer formats (JSON, protobuf).
Choose compact, schema-based formats (Avro/Protobuf) for inter-service communication and storage.
Immutable data reduces bugs in concurrent code and makes reasoning easier.
Learn `MethodHandle` and `VarHandle` for low-level, high-performance dynamic access.
Generate code at compile-time (via `javax.annotation.processing`) to reduce runtime reflection.
Reflection is flexible but slow — cache lookups or generate bytecode where performance matters.
Use ASM/ByteBuddy for dynamic proxies or high-performance frameworks instead of heavy reflection.
Log levels, structured logs, and sampling help diagnose production issues without noise.
Protect services with token buckets, circuit breakers, and request throttling.
GraalVM native-image improves startup and memory but requires reflection/config tuning.
Instrument apps with metrics, traces, and structured logs for real operational insight.
Avoid synchronized blocks on public objects, prefer executors and immutable shared state.
Minimize transitive dependencies, track CVEs, and update libraries to stay secure and efficient.