Java is fast, code might not be
Java’s raw JVM performance is often very high, but many developers still write code and use libraries in ways that quietly sabotage speed — from excessive object allocation, autoboxing and string concatenation to using exceptions for control flow and ORMs that generate inefficient database access patterns. Commenters contrast Java with languages like Rust, Go, and C#, argue over build tooling such as Maven and Gradle, and highlight how modern JVM features (GraalVM, ZGC, Leyden, CRaC) and profiling can mitigate startup and latency issues. A recurring theme is that performance problems usually stem less from the language itself and more from ecosystem choices, architectural patterns, and a lack of attention to algorithmic complexity and memory behavior.
Language choice: Java vs. Rust/others
- Some argue everyone “should just use Rust” for explicit control and safety; others say Rust’s compiler friction is real and Zig gives more control but fewer correctness guardrails.
- Many note massive existing Java codebases cannot be rewritten; profiling and fixing JVM‑specific antipatterns is more realistic than language migration.
- Critiques claim Java is “only fast” when written in C‑style, and that modern work could be done with Rust, Go, C/C++ extensions, Python, Node, etc. Others counter that JVM apps are extremely fast once warm, often DB‑bound, and far faster than typical Python/JS services.
Build tooling and LLMs
- Gradle is widely disliked as overly flexible and footgun‑prone; Maven is described as boring but dependable, though some recall painful fights with both.
- Experienced users now offload build maintenance to LLMs and keep configs simple. Some claim this largely removes the pain of Maven/Gradle/sbt.
Java performance footguns
- Thread highlights classic issues: autoboxing in hot loops, string concatenation vs
StringBuilder, exceptions used for control flow, repeated object/formatter creation, streams inside loops causing accidental O(n²). - Several emphasize that many examples are language‑agnostic algorithmic mistakes, but appear more often in Java culture where performance hasn’t always been prioritized.
Startup time, JIT, AOT
- Complaints about cold‑start latency and JIT warm‑up (“first request slow”) contrast with mentions of CRaC, AOT caches, GraalVM native images, and Project Leyden as partial solutions.
- Some praise alternative JVMs (OpenJ9, historical products) and note JIT features like
-XX:+OmitStackTraceInFastThrow. Others find GraalVM native image too heavy and incompatible with reflection‑heavy libraries.
GC, allocations, and low‑latency
- Strong consensus that avoiding allocations in hot paths yields large speedups, in both GC and non‑GC languages.
- There’s skepticism about object pools as a general fix; profiling, algorithm changes, and primitives are preferred.
- Debate continues over Java’s suitability for HFT/low‑latency work, with critics pointing to object overhead and GC pauses, and proponents citing modern low‑pause collectors (e.g., ZGC, commercial GCs).
Databases, ORMs, and I/O
- Many say real‑world bottlenecks are DB and external service calls; batching queries and careful SQL beat micro‑optimizing app code.
- ORMs are frequently criticized as performance traps that eventually require understanding and replacing their SQL. Libraries like jOOQ and typed query builders get praise.
- Some hope LLMs will enable direct, safer SQL and reduce ORM reliance.
Concurrency and synchronized
synchronizedis called a design mistake by some, as it tempts developers into over‑locking entire classes.- Preference is expressed for actor‑style or concurrency‑first designs and lock avoidance, with locks seen as a last‑resort band‑aid.