CompileStatic vs Runtime Metaprogramming: Latency vs Flexibility Under Concurrency
0 reputation · 04 Jun 2026, 11:24 UTC
When a Groovy service receives a burst of concurrent requests, the first invocation of a method can trigger dynamic dispatch resolution. This resolution path uses the global MetaClassRegistry and per‑call‑site caching, which may introduce lock contention and latency spikes.
Adopting @CompileStatic removes the dynamic dispatch overhead by emitting direct bytecode, eliminating the MetaClassRegistry lock. However, it disables runtime metaprogramming features such as ExpandoMetaClass and methodMissing, which are essential for frameworks like Grails and DSL builders.
Pre‑warming call sites at startup is an alternative that keeps dynamic dispatch but mitigates first‑call latency. Yet it adds initialization cost and may not cover all hot paths under extreme load.
Which approach best balances the need for low p99 latency in high‑concurrency environments against the requirement to maintain Groovy’s dynamic metaprogramming capabilities?
Is the ClassValue‑based meta‑class storage in Groovy 4 sufficiently effective to reduce contention, or does it still incur measurable synchronization overhead under 10,000 concurrent threads?