Skip to content

Commit 90cd440

Browse files
[vm, gc] Update GC doc to account for mark-through-new-space.
Change-Id: Icbf77cf6989daee8671641c8b5f558ba230d5b7a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/396522 Reviewed-by: Siva Annamalai <[email protected]>
1 parent 039f8c9 commit 90cd440

File tree

1 file changed

+10
-6
lines changed

1 file changed

+10
-6
lines changed

runtime/docs/gc.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ To reduce the time the mutator is paused for old-space GCs, we allow the mutator
7171

7272
### Barrier
7373

74-
With the mutator and marker running concurrently, the mutator could write a pointer to an object that has not been marked (TARGET) into an object that has already been marked and visited (SOURCE), leading to incorrect collection of TARGET. To prevent this, the write barrier checks if a store creates a pointer from an old-space object to an old-space object that is not marked, and marks the target object for such stores. We ignore pointers from new-space objects because we treat new-space objects as roots and will revisit them to finalize marking. We ignore the marking state of the source object to avoid expensive memory barriers required to ensure reordering of accesses to the header and slots can't lead skipped marking, and on the assumption that objects accessed during marking are likely to remain live when marking finishes.
74+
With the mutator and marker running concurrently, the mutator could write a pointer to an object that has not been marked (TARGET) into an object that has already been marked and visited (SOURCE), leading to incorrect collection of TARGET. To prevent this, the write barrier checks if a store creates a pointer to an object that is not marked, and marks the target object. We ignore the marking state of the source object to avoid expensive memory barriers required to ensure reordering of accesses to the header and slots can't lead skipped marking, and on the assumption that objects accessed during marking are likely to remain live when marking finishes.
7575

7676
The barrier is equivalent to
7777

@@ -82,7 +82,7 @@ StorePointer(ObjectPtr source, ObjectPtr* slot, ObjectPtr target) {
8282
if (source->IsOldObject() && !source->IsRemembered() && target->IsNewObject()) {
8383
source->SetRemembered();
8484
AddToRememberedSet(source);
85-
} else if (source->IsOldObject() && target->IsOldObject() && !target->IsMarked() && Thread::Current()->IsMarking()) {
85+
} else if (!target->IsMarked() && Thread::Current()->IsMarking()) {
8686
if (target->TryAcquireMarkBit()) {
8787
AddToMarkList(target);
8888
}
@@ -150,7 +150,9 @@ For old-space objects created before marking started, in each slot the marker ca
150150

151151
For old-space objects created after marking started, the marker may see uninitialized values because operations on slots are not synchronized. To prevent this, during marking we allocate old-space objects [black (marked)](https://en.wikipedia.org/wiki/Tracing_garbage_collection#TRI-COLOR) so the marker will not visit them.
152152

153-
New-space objects and roots are only visited during a safepoint, and safepoints establish synchronization.
153+
New-space objects inside an active TLAB and roots are only visited during a safepoint, and safepoints establish synchronization.
154+
155+
New-space objects outside an active TLAB are synchronized by the store-release used to switch to the next TLAB.
154156

155157
When the mutator's mark block becomes full, it transferred to the marker by an acquire-release operation, so the marker will see the stores into the block.
156158

@@ -167,8 +169,7 @@ When this occurs, we must insert `container` into the remembered set.
167169

168170
The incremental marking write barrier, needed by the marker, checks if
169171

170-
* `container` is old, and
171-
* `value` is old and not marked, and
172+
* `value` is not marked, and
172173
* marking is in progress
173174

174175
When this occurs, we must insert `value` into the marking worklist.
@@ -178,7 +179,10 @@ We can eliminate these checks when the compiler can prove these cases cannot hap
178179
* `value` is a constant. Constants are always old, and they will be marked via the constant pools even if we fail to mark them via `container`.
179180
* `value` has the static type bool. All possible values of the bool type (null, false, true) are constants.
180181
* `value` is known to be a Smi. Smis are not heap objects.
181-
* `container` is known to be a new object or known to be an old object that is in the remembered set and is marked if marking is in progress.
182+
* `value` is `container`. Self-references cannot cross generations or marking states.
183+
* `container` is known to be
184+
* a new object or or an old object that is in the remembered set, AND
185+
* a new object in the active TLAB or queued for re-scanning
182186

183187
We can know that `container` meets the last property if `container` is the result of an allocation (instead of a heap load), and there is no instruction that can trigger a GC between the allocation and the store. This is because the allocation stubs ensure the result of AllocateObject is either a new-space object (common case, bump pointer allocation succeeds), or has been preemptively added to the remembered set and marking worklist (uncommon case, entered runtime to allocate object, possibly triggering GC).
184188

0 commit comments

Comments
 (0)