I help teams fix systemic engineering issues: processes, architecture, and clarity.
→ See how I work with teams.
Note (2025): The GC flags in this article apply to older Java 7/8 HBase deployments where CMS and ParallelGC were common choices. In newer JVMs, the Concurrent Mark-Sweep (CMS) collector is deprecated and removed in favour of collectors like G1GC. Treat the configuration below as legacy tuning for old HBase stacks, not as recommendations for new clusters.
Why HBase GC tuning mattered so much
HBase region servers keep a lot of data in memory (memstores and block cache). Without careful JVM tuning, garbage collection can easily cause long pauses, region server timeouts and client-visible outages. Adjusting HBASE_OPTS was therefore a necessary step to get acceptable performance and stability in larger installations.
Example JVM options for HBase
A common CMS-based tuning for HBase region servers looked like this:
HBASE_OPTS="-XX:+UseParNewGC \
-XX:+UseConcMarkSweepGC \
-XX:CMSInitiatingOccupancyFraction=70 \
-XX:+CMSParallelRemarkEnabled"
An alternative setup used the throughput-focused ParallelGC:
HBASE_OPTS="-server \
-XX:+UseParallelGC \
-XX:+UseParallelOldGC \
-XX:ParallelGCThreads=8"
Both approaches were used successfully in production, but the trade-offs are quite different.
CMS vs ParallelGC for HBase
Concurrent Mark-Sweep (CMS)
CMS runs major parts of its collection concurrently with the application threads. That means:
- It uses more CPU cycles, because GC work competes with the region server threads.
- Pauses are typically shorter, which is important for request latency and region server heartbeats.
- On failure or under memory pressure, CMS can fall back to a “stop-the-world” full GC, which will pause the entire JVM.
In HBase, the risk of these longer pauses can be reduced when the heap is sized carefully and memstore allocations avoid excessive old-generation fragmentation. This is one of the reasons why MSLAB (MemStore-Local Allocation Buffers) became important.
ParallelGC
ParallelGC focuses on throughput: it runs stop-the-world collections with multiple threads:
- Better raw throughput than CMS in many cases.
- All collections are stop-the-world; the JVM is paused during each collection cycle.
- Pause times typically scale at roughly ~1s per GB of heap for full collections, which can be unacceptable under high load.
For HBase, that meant ParallelGC could easily create multi-second pauses on heavily loaded clusters, causing timeouts, region reassignment or client errors. That’s why many operators preferred CMS despite its complexity and CPU cost.
MSLAB (MemStore-Local Allocation Buffers)
Many of the worst GC pauses in HBase were caused by fragmentation in the old generation. CMS cannot compact the heap without a full stop-the-world pause, so a highly fragmented old gen leads to long “Juliet” pauses when the JVM finally has to compact.
MSLAB (MemStore-Local Allocation Buffers) improves this situation by allocating memstore data in fixed-size chunks that are placed into the old generation in a more controlled way. This reduces fragmentation and makes CMS behaviour more predictable.
Starting with HBase 0.92, MSLAB is enabled by default. Typical configuration looked like this:
hbase.hregion.memstore.mslab.enabled=true
hbase.hregion.memstore.mslab.chunksize=2MB # 2MB by default
hbase.hregion.memstore.mslab.max.allocation=256KB # 256KB by default
The idea is:
- Memstore allocations go into controlled chunks rather than scattered small objects.
- These chunks live in the old generation, but fragment it much less.
- CMS can run shorter and more predictable collections, reducing long GC pauses that used to bring region servers down.
For more background, see classic presentations on HBase GC and MSLAB (for example Todd Lipcon’s early talks), which walk through how heap layout and memstore behaviour interact with the JVM collectors.
When this tuning still makes sense
You might still care about this pattern if:
- You maintain a legacy HBase cluster on Java 7 or 8 where CMS and ParallelGC are still supported.
- You are debugging long GC pauses and want to understand why MSLAB became the default.
- You are planning a migration from an old CDH/HBase environment and need to stabilise it first.
For modern deployments, you will usually:
- Run on newer Java versions where CMS is no longer available and G1GC (or other collectors) are preferred.
- Follow up-to-date HBase tuning guides for heap sizing, off-heap usage and GC selection.
- Focus on schema design, compaction strategy and data model changes rather than relying only on GC flags.
In other words: this configuration is part of the historical toolbox that kept early HBase clusters alive. It is still useful to know how and why it worked, but you should not blindly copy these flags into a modern JVM without checking current HBase and Java recommendations.
If you need help with distributed systems, backend engineering, or data platforms, check my Services.