18 Advanced Java Coding Practices for High-Performance, Low-CPU Systems (Part 2)
This article details 18 advanced Java coding techniques (19-36) for high-performance, low-CPU systems, covering void/null avoidance, nested code flattening, final/static method optimization, primitive type usage, collection pre-sizing, memory management, immutable collections, loop optimization, wrapper caching, exception reuse, stack trace reduction, try-catch narrowing, bitwise operations, regex precompilation, memory alignment, constant-first comparisons, and batch processing.
19. Avoid void/null Return Parameters
Core Principle: void forces callers to rely on exceptions for status; exception creation fills stack traces — a heavy CPU operation. Returning null forces null checks, causing branch prediction failures and NPE risks. null does not save memory (reference still occupies 4 bytes). Returning empty collections ( Collections.emptyList()) or empty objects is a zero-overhead singleton.
Implementation: Use meaningful return values (boolean, empty DTO, empty collection, unified response) so callers need no null checks or exception handling.
Attention: Collections.emptyList() is a global static singleton — zero GC even after millions of calls. Never new ArrayList<>() for empty returns; each call creates a new object, causing frequent YGC under high concurrency.
Examples:
20. Avoid Double Negatives
Core Principle: Double negatives ( !isNotFinish, !list.isEmpty()) increase cognitive load, cause logic bugs, and make debugging harder. Positive naming ( isFinish, isValid) and positive methods ( list.isNotEmpty()) yield clearer, flatter logic.
Implementation: Rename boolean fields/methods to positive form; replace !collection.isEmpty() with collection.isNotEmpty() (or Guava's Iterables.isNotEmpty).
Examples:
21. Avoid Deep Nesting
Core Principle: Deep nesting ( if/else, loops, try-catch, lambdas) increases branch mispredictions, hinders JIT optimizations (loop unrolling, escape analysis), raises bug risk, hurts readability, and nested loops turn O(n) into O(n²) or O(n³).
Implementation: Use guard clauses (early return), extract methods, invert conditions, avoid three-plus loop levels (use Map lookups or pre-processing), replace multi-level if-else with strategy pattern or Map dispatch.
Examples:
if (status == 1) { /* logic1 */ }
else {
if (status == 2) { /* logic2 */ }
else {
if (status == 3) { /* logic3 */ }
}
}Optimized with else if or Map dispatch:
22. Why final/static Methods Are Fastest & When to Use
Core Principle: Ordinary methods are virtual — JVM must resolve via v-table at runtime. final methods cannot be overridden; static methods have no this — both are early-bound (compile/class-load time), enabling 100% inlining, eliminating polymorphic checks, and reducing CPU instructions. Comparison (converted from table):
static : binding at compile-time, no polymorphism, no v-table lookup, 100% inlining, highest performance.
final : binding at compile-time, no polymorphism, no v-table lookup, 100% inlining, highest performance.
Ordinary : binding at runtime, polymorphism yes, v-table lookup yes, limited inlining, average performance.
Suitable for static: Stateless utilities (StringUtils, DateUtils), constant calculations, conversions, validations, low-level common logic. Suitable for final: Core business methods not to be overridden, high-frequency hot-path methods, template methods, base algorithms. Not suitable: Methods requiring polymorphism, interface methods, Spring AOP proxied methods (final/static break proxying). Attention: Static methods cannot access non-static fields; final methods cannot be overridden; Spring AOP/transactions fail on final/static; don't force everything static — preserve design. Examples: <code>// Static utility: no state, early binding, 100% inline public static boolean isEmpty(String str) { return str == null || str.isEmpty(); } // High-frequency core method + final public final BigDecimal calculatePrice(Order order) { /* ... */ }</code>
23. Prefer char/byte Over String for Low-Level Operations
Core Principle: String is an object (header + array reference + array content) requiring GC. char / byte are primitives stored on stack/arrays with zero overhead. All string operations ultimately decompose to char/byte arrays; using primitives directly removes a layer of wrapping, method calls, and unboxing. CPU executes primitive ops in 1-2 instructions vs dozens for String . Network/IO natively transmits bytes; using String forces double encoding/decoding. Implementation: Use char for text parsing/character checks; use byte for network, file IO, binary protocols; compare chars with == (10x faster than equals ); process batches with char[] / byte[] ; convert to String only at the output boundary. Attention: char is 16-bit unsigned; byte is 8-bit signed; specify charset (UTF-8/GBK) when converting bytes to String; watch array bounds — primitives have no safety checks. Examples: <code>// Good: native char[] array, zero GC char[] result = new char[len]; int index = 0; for (char c : buffer) { result[index++] = c; } return new String(result);</code> 24. Initialize Collections with Estimated Capacity Core Principle: Default capacities are tiny (ArrayList=10, HashMap=16). When exceeded, resizing allocates a larger array, copies all elements (CPU), and discards the old array (GC). Pre-sizing eliminates resizing entirely. For HashMap, use formula: initialCapacity = (int)(expectedSize / 0.75f) + 1 to account for load factor 0.75. Implementation: new ArrayList<>(1000) , new HashMap<>(initialCapacity) . For addAll , pre-size target with sum of source sizes. Attention: Don't overestimate (wastes memory). HashMap must use formula, not raw size. Rough estimates still help (e.g., expect 100, actual 120 → only one resize). addAll is fast (copies references) but triggers massive overhead if it causes repeated resizing. Examples: <code>// Wrong: HashMap(100) resizes at 75 elements Map<Long, User> map = new HashMap<>(100); // Correct formula int initialCapacity = (int)(count / 0.75f) + 1; Map<Long, User> map = new HashMap<>(initialCapacity);</code> <code>List<User> target = new ArrayList<>(listA.size() + listB.size()); target.addAll(listA); target.addAll(listB);</code> 25. Clear Large Collections & Release Strong References Promptly Core Principle: GC only collects unreachable objects. Large collections (backed by large arrays) hold strong references to all elements, preventing collection. They fill young generation, promote to old generation, trigger frequent YGC and Full GC, causing latency spikes. clear() cuts element references immediately, making them eligible for rapid GC. Implementation: After batch processing, call collection.clear() . For temporary large collections/arrays, set reference to null . Avoid global Maps/Lists accumulating temporary data. For nested structures (Map<List<DTO>>), clear inner collections first, then outer. Attention: Only clear business-temporary collections; long-lived caches must not be cleared. clear() keeps the container; if container itself is unneeded, also set list = null . Nested collections require individual clear() . Don't over-optimize small collections — they die young in young gen, manual clearing adds CPU instructions and hurts JIT. Examples: Q&A Highlights: Manual clear() isn't to replace GC but to release memory now , preventing short-lived objects from surviving multiple YGCs and promoting to old gen. For nested collections, outer clear() only drops references to inner collections; inner objects remain strongly referenced. In microservices, manual clearing is needed for batch endpoints (10k+ items), nested large collections, long call chains, scheduled tasks, and global/static caches. 26. Use Immutable Collections for Read-Only Data Core Principle: Immutable collections ( List.of() , Set.of() , Map.of() since JDK 9) are absolutely thread-safe without locks, have compact memory (fixed size, no extra capacity), enable aggressive JIT optimizations (constant folding, cache hits), prevent accidental modifications, and never resize (zero GC from array copies). Implementation: Use JDK 9+ factory methods for static configuration, constants, enum dictionaries, read-only API returns. Attention: Any mutation attempt throws UnsupportedOperationException . Collections.unmodifiableList is a shallow wrapper — changes to backing list are visible. Immutable collections reject null elements. Not suitable for runtime-mutable data. Examples: 27. Avoid Complex Calculations in Loop Conditions Core Principle: The loop condition is evaluated every iteration. Placing method calls, DB/IO, regex, or complex computations there multiplies their cost by the iteration count. Cache the result in a local variable before the loop. Implementation: Extract condition to a final/local variable; loop condition becomes a simple comparison. Examples: 28. Manual Caching for Wrapper Classes Outside Default Range Core Principle: Integer , Long , Short , Byte cache only -128 to 127 (Character 0-127). Values outside this range create new objects on each boxing, causing GC pressure. For high-frequency values beyond the cache, build a manual cache pool (e.g., ConcurrentHashMap ) and access via a valueOf -style method. Default Cache Ranges: Integer: -128 ~ 127 Long: -128 ~ 127 Short: -128 ~ 127 Byte: all (-128~127) Character: 0 ~ 127 Implementation: Static ConcurrentHashMap<Integer, Integer> cache; getOrPut pattern; use only for hot values; never new Integer() . Examples: 29. Reuse Exception Objects, Avoid Frequent new Exception Core Principle: new Exception() invokes fillInStackTrace() , which walks the entire thread stack, capturing method names, line numbers, class names — a heavy CPU operation. Business exceptions with fixed messages/codes are immutable and can be global singletons, eliminating object allocation and stack capture. Implementation: Declare static final exception instances for fixed error codes/messages; use a global exception constant pool; for predictable exceptions, override fillInStackTrace() to return this (disabling stack capture). Never new Exception() inside loops/high-frequency paths. Attention: System crashes (NPE, ArrayIndexOutOfBounds) must create fresh exceptions with full stacks for debugging. Reuse only for expected business exceptions. Examples: 30. Disable Unnecessary Stack Trace Printing Core Principle: e.printStackTrace() or logging full exceptions traverses all stack frames, builds a huge string, and writes to console/disk — 10-100x slower than normal logic. fillInStackTrace() (called during exception construction) is similarly expensive. Implementation: Log only error code + message. Override fillInStackTrace() in business exceptions to return this . Configure logging frameworks to exclude stack traces for known business exceptions. Examples: 31. Narrow try-catch Scope Core Principle: Large try blocks restrict JIT optimizations (register allocation, instruction reordering, inlining) because the JVM must preserve stack context for potential exceptions. They also swallow unrelated exceptions (NPE, index errors), hiding bugs. Implementation: Wrap only the specific line(s) that can throw the checked exception. Examples: 32. Bitwise Operations for Powers of Two Core Principle: CPU executes bitwise ops (shift, AND) in 1 clock cycle; arithmetic (*, /, %) takes 10-30 cycles. For divisors that are powers of two, replace: x * 2^n → x << n , x / 2^n → x >> n , x % 2^n → x & (2^n - 1) . Example: 33. Precompile Regex Pattern as Static Final Core Principle: Pattern.compile() is a heavy CPU operation. Doing it inside loops or high-concurrency methods spikes CPU. Compile once as a static final constant; reuse pattern.matcher(str).matches() . Examples: 34. Reduce Memory Alignment Waste Core Principle: With compressed oops (default in JDK 8+), object layout is: header (12B) + instance fields + padding to 8-byte multiple. Fields are aligned to their size (long/double=8, int/float=4, reference=4, short/char=2, byte/boolean=1). Poor ordering (small, large, small) creates padding gaps. Ordering fields from largest to smallest, grouping same-width fields, minimizes padding. Implementation: Declare fields in order: long/double → int/float → references → short/char → byte/boolean. Use JOL (Java Object Layout) to verify object size. Examples: 35. Put Constants First in equals Comparisons Core Principle: Constants (literals, enums, static finals) are never null. Writing "literal".equals(variable) safely returns false if variable is null, avoiding NPE. This is defensive programming. Rules: "value".equals(str) , Enum.CONSTANT.equals(var) , CONSTANT.equals(var) . Never variable.equals(constant) . 36. Batch Processing Outperforms Single-Item Loops Core Principle: Batch APIs ( addAll , putAll , containsAll ) reduce IO round-trips, lock contention, method call overhead, and memory copying. For external resources (DB, cache, remote), one batch request replaces N individual requests. For in-memory collections, bulk operations avoid repeated resizing and array copies. Boundaries: Tiny datasets (1-10) — batching overhead may exceed savings. Huge datasets (10k+) — paginate batches to avoid memory/DB limits. Key Points: ArrayList.addAll : single capacity calculation, one resize, one array copy vs multiple in loop. HashSet.containsAll : O(M) vs O(M*N) for ArrayList ; convert source list to HashSet first. HashMap.putAll : computes total size, resizes once, then inserts — avoids multiple rehashes. Examples: <code>// Batch contains via HashSet Set<String> set = new HashSet<>(srcList); boolean allExist = set.containsAll(checkList);</code> Conclusion: Every line of code impacts production performance and CPU consumption. Respect each line.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JD Tech
Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
