How JVM Card Tables Boost GC Efficiency and Solve Cross-Generation References
The article explains why JVMs use generational garbage collection, how cross‑generation references cause missed objects during Minor GC, and how the card table together with write barriers and conditional marking dramatically reduces pause times and improves overall GC performance.
1. Cross‑Generation References
In a generational GC, the young generation is collected by Minor GC while the old generation is collected less frequently. If an old‑generation object A holds a reference to a young‑generation object B, the GC must treat B as reachable even during a Minor GC. Otherwise B could be reclaimed mistakenly, leading to application crashes.
To avoid missing such objects, the collector must consider two cases:
Objects reachable directly from GC roots.
Objects referenced by old‑generation objects.
Scanning the entire old generation each Minor GC is impractical because the old generation can be many gigabytes, which would defeat the purpose of a fast Minor GC.
2. Card Table Principle
The JVM solves this with a "card table" – a concrete implementation of a remembered set. The old generation is divided into fixed‑size regions called cards (typically 512 bytes). A byte array records whether each card contains a reference to the young generation.
When a reference from the old generation to the young generation is created, the corresponding card entry is marked "dirty" (value 1). During GC the JVM only scans cards marked dirty, turning a potential dozens‑of‑gigabytes scan into a scan of a few megabytes.
3. Write Barrier – Marking Dirty Cards
Dirty cards are set by a write barrier that intercepts every reference assignment where an old‑generation field is assigned a young‑generation object.
void oop_field_store(oop* field, oop new_value) {
*field = new_value; // normal assignment
// Write barrier: check if we need to mark the card table
if (field is in old generation && new_value is in young generation) {
mark_card_table(field); // set the corresponding card to dirty
}
}There are two kinds of write barriers:
Pre‑write barrier : executed before the assignment (used by G1 collector).
Post‑write barrier : executed after the assignment (used by CMS and similar collectors).
4. False Sharing Issue
The card table is a byte array, so 64 consecutive cards share a CPU cache line (typically 64 bytes). If thread A updates card1 and thread B updates card2, they contend for the same cache line, causing false sharing and performance degradation.
The JVM mitigates this by using a byte per card instead of a bit‑packed representation, sacrificing some space efficiency to avoid false sharing.
5. Reducing Card‑Table Update Overhead
Even with write barriers, every reference write may try to mark a card that is already dirty. The JVM option -XX:+UseCondCardMark adds a conditional check: if the card is already dirty, the write barrier skips the marking step, reducing unnecessary writes.
Conclusion
The card table solves the "cross‑generation reference" problem by turning a full old‑generation scan into a tiny, targeted scan of dirty cards. Write barriers are essential for keeping the card table up‑to‑date, though they introduce some overhead, which can be mitigated with conditional marking and careful layout to avoid false sharing.
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.
Lobster Programming
Sharing insights on technical analysis and exchange, making life better through technology.
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.
