SATB与CMS增量标记的性能差异
SATB(Snapshot-At-The-Beginning)和CMS(Concurrent Mark-Sweep)是两种不同的垃圾回收算法,它们在增量标记阶段的性能差异主要由以下因素决定:
标记范围与停顿时间
SATB在并发标记阶段开始时对所有存活对象建立快照,后续标记仅处理该快照中的对象。这种设计减少了需要跟踪的对象数量,降低了标记阶段的复杂度。
CMS需要在标记阶段实时跟踪所有存活对象,包括并发阶段新分配的对象。这导致标记范围动态变化,增加了标记阶段的负担和停顿时间。
写屏障开销
SATB的写屏障仅需记录对象引用变更前的旧值(通过pre-write barrier),保证快照一致性。这种屏障逻辑简单,对程序执行影响较小。
CMS的写屏障需要维护卡表(Card Table)或类似结构以记录跨代引用变更,确保标记准确性。这种屏障通常涉及更多内存访问和更新操作,增加了运行时开销。
并发处理能力
SATB通过快照隔离了标记过程中的对象状态变化,允许垃圾回收线程与应用线程更高程度的并行执行。标记阶段无需频繁同步,减少了线程竞争。并发过程新生成的对象都会标记成黑色(可存活)根据top指针位置判断新生成,并会将top 指针向前移动到最新对象的位置,不会为新增的对象创建新的位图。先设置为存活的,等下一轮判断如果没有引用再去清除。
CMS在标记阶段需处理动态变化的引用关系,可能导致标记线程与应用线程竞争共享数据(如卡表)。这种竞争会降低并发效率,延长标记时间。新增对象会从GC root 开始扫描,速度比较慢。
以下为两种算法的伪代码对比,体现其核心差异:
SATB标记逻辑
void satbMark() {
snapshot = captureHeapSnapshot(); // 初始快照
for (Object obj : snapshot) {
if (isMarked(obj)) continue;
mark(obj);
addToWorkQueue(obj); // 递归标记引用对象
}
}
CMS标记逻辑
void cmsMark() {
for (Object root : roots) {
mark(root);
addToWorkQueue(root);
}
while (!workQueue.isEmpty()) {
Object obj = workQueue.poll();
for (Object ref : getReferences(obj)) {
if (isDirty(ref)) scanCardTable(ref); // 卡表扫描开销
if (!isMarked(ref)) {
mark(ref);
addToWorkQueue(ref);
}
}
}
}
内存模型影响
SATB适合大堆内存场景,快照机制使标记时间相对稳定,不受堆大小线性增长影响。CMS的标记时间与存活对象数量正相关,堆内存增大时性能下降更明显。
实际应用表现
在G1等现代垃圾收集器中,SATB被证明比传统CMS减少50%以上的标记停顿时间。OpenJDK测试数据显示,对于4TB堆内存的应用,SATB的标记阶段延迟能控制在10ms以内,而CMS通常超过50ms。
169万+

被折叠的 条评论
为什么被折叠?



