Java Performance Optimization: Eight Anti-Patterns That Slow Down Your Code

Performance Improvements from Fixing Anti-Patterns
Jonathan Vogel built a Java order-processing app that initially had 1,198ms elapsed time, handled 85,000 orders per second, used just over 1GB heap, and had 19 GC pauses. After fixing eight anti-patterns without architectural changes or JDK updates, performance improved to 239ms elapsed time, 419,000 orders per second, 139MB heap, and 4 GC pauses. This represents 5x throughput, 87% less heap usage, and 79% fewer GC pauses.
Eight Java Performance Anti-Patterns to Fix
- String concatenation in loops - O(n²) copying from immutability
- O(n²) stream iteration inside loops - streaming the full list per element
- String.format() in hot paths - slowest string builder, parses format every call
- Autoboxing in hot paths - millions of throwaway wrapper objects
- Exceptions for control flow - fillInStackTrace() walks the entire call stack
- Too-broad synchronization - one lock becomes the bottleneck
- Recreating reusable objects - ObjectMapper, DateTimeFormatter, Gson per call
- Virtual thread pinning (JDK 21-23) - synchronized + blocking I/O pins carriers
Detailed Examples and Fixes
1. String Concatenation in Loops
Problem code:
String report = "";
for (String line : logLines) {
report = report + line + "\n";
}This creates O(n²) copying due to String immutability. BellSoft JMH benchmarks show that when n grows by 4x, loop-concatenation slows down by more than 7x.
Fix:
StringBuilder sb = new StringBuilder();
for (String line : logLines) {
sb.append(line).append("\n");
}
String report = sb.toString();Note: Since JDK 9, the compiler optimizes single-line concatenation like "Order: " + id + " total: " + amount, but this optimization doesn't carry into loops.
2. Accidental O(n²) with Streams Inside Loops
Problem code:
for (Order order : orders) {
int hour = order.timestamp().atZone(ZoneId.systemDefault()).getHour();
long countForHour = orders.stream()
.filter(o -> o.timestamp().atZone(ZoneId.systemDefault()).getHour() == hour)
.count();
ordersByHour.put(hour, countForHour);
}This pattern accounted for nearly 71% of CPU stack samples in the JFR recording. With 10,000 orders, it performs 100 million comparisons instead of a single pass.
Fix:
for (Order order : orders) {
int hour = order.timestamp().atZone(ZoneId.systemDefault()).getHour();
ordersByHour.merge(hour, 1L, Long::sum);
}This provides O(n) performance with one pass. You could also use Collectors.groupingBy(... Collectors.counting()) in a single stream pipeline.
The article is part 1 of a 3-part Java Performance Optimization series, with parts 2 and 3 coming soon. Part 2 will walk through the profiling data behind these numbers, including flame graphs and which methods were actually hot.
📖 Read the full source: HN AI Agents
👀 See Also

OpenClaw v2.0 Update: Critical Pre-Update Checklist to Avoid Breaking Changes
OpenClaw's latest update introduces 12 breaking changes, a new plugin system, and 30+ security patches. This guide outlines five essential checks to perform before updating, including environment variable renaming, state directory migration, and browser automation reconfiguration.

Designing Constraints for Production-Grade AI Agent Reliability
A Reddit post details a constraint-based approach to using Claude for complex codebase operations, emphasizing explicit failure mode enumeration, phased execution with checkpoints, and anti-shortcut rules to achieve zero broken builds when removing 140 files.

Using AI as a Cognitive Partner Instead of a Code Factory
A Reddit post proposes a system prompt called 'Cognitive Authorship Copilot' that forces AI to act as a pair programming partner rather than an autonomous solution generator, with three intervention levels based on task complexity.

Stop Asking Which AI Model to Use: Route Tasks to Haiku, Sonnet, and Opus Tiers
Use at least three models by task type: Haiku-tier for reading/summarizing, Sonnet-tier for writing code, and Opus-tier only for multi-file refactors and debugging. One user's setup routes 40% to cheap models, 35% to mid, 25% to frontier, costing ~$30-40/month.