In the core of concurrent and incremental garbage collectors
references are the objects that are referenced by the current object
Weak:
All white objects pointed to by a black object are reachable from some grey object through a chain of white objects.
Strong:
There are no pointers from black objects to white objects.
In the incremental or concurrent GC, while the marking phase is running, the mutator can change the references of a black object and break the above invariants.
Barriers are code that gets executed by the mutator every time it mutates the heap. They will enforce that the invariants are being kept.
Ensure the invariants by not allowing getting a white object out of a gray one
def read(object, field):
if is_grey(object):
shade(object)
return object[field]
Not used, since read operations are more than write operations in a program and having a slower read is worse than having a slower write
Ensure the invariants by not allowing to set a white object as a reference inside a black object
def write(object, field, value):
object[field] = value
if is_black(object):
shade(value)
This is how GC can be implemented. Everything is subject to change.
Having a custom allocator is a must have for most VM
For simplicity, we are going to not use a special allocator.
Where are these stored?
data_stack
m_SP
void Spasm::Mark() {
for (auto cell = &data_stack[0]; cell != m_SP; ++cell) {
Mark(cell); // ?
}
Mark(m_Global);
}
struct MarkVisitor
{
typedef void ResultType;
void Visit(double d) const {}
void VisitUndefined() const {}
void VisitNull() const {}
void Visit(StringValue& value) const;
void Visit(ArrayValue& value) const;
void Visit(ObjectValue& value) const;
void Visit(FunctionValue& value) const;
};
void Visit(StringValue& value) const {
if (Dead(value)) {
SetAlive(value);
}
}
void Visit(ArrayValue& value) const {
if (Dead(value)) {
SetAlive(value);
for (auto i = 0u; i < value.length(); ++i) {
value.item(i).Visit(*this);
}
}
}
void Visit(ObjectValue& value) const {
if (Dead(value)) {
SetAlive(value);
for (auto* p : value.m_Properties) {
p->first.Visit(*this);
p->second.Visit(*this);
}
value.m_Prototype->Visit(*this);
}
}
Where to store whether an object is dead or alive
bool
per object, string, array, functionbitmap
that holds one bit for a range of objectsbit
per pointer