update GC pseudocode
parent
613f848ce1
commit
61a7bf871c
|
@ -89,9 +89,6 @@ def gc_collect(): # implemented by ports
|
||||||
# add root pointers (platform-dependent)
|
# add root pointers (platform-dependent)
|
||||||
gc_collect_root(roots)
|
gc_collect_root(roots)
|
||||||
|
|
||||||
# mark all reachable blocks
|
|
||||||
gc_mark()
|
|
||||||
|
|
||||||
# if there was a stack overflow, scan all memory for reachable blocks
|
# if there was a stack overflow, scan all memory for reachable blocks
|
||||||
gc_deal_with_stack_overflow()
|
gc_deal_with_stack_overflow()
|
||||||
|
|
||||||
|
@ -100,41 +97,48 @@ def gc_collect(): # implemented by ports
|
||||||
|
|
||||||
def gc_collect_root(roots):
|
def gc_collect_root(roots):
|
||||||
for root in roots:
|
for root in roots:
|
||||||
# Add this root to the (empty) stack. len(gc.stack) will be 1.
|
# Test whether this could be a pointer.
|
||||||
may_add_to_stack(root)
|
if looks_like_pointer(root):
|
||||||
|
block = block_from_ptr(root)
|
||||||
|
if block.state == HEAD:
|
||||||
|
# Set the mark bit on this block.
|
||||||
|
block.state = MARK
|
||||||
|
|
||||||
# Shrink the stack to zero, hopefully without GC stack overflow.
|
# Mark all descendants of this root.
|
||||||
gc_drain_stack()
|
gc_mark_subtree(block)
|
||||||
|
|
||||||
def may_add_to_stack(pointer): # VERIFY_MARK_AND_PUSH
|
def gc_mark_subtree(block):
|
||||||
# If this is a pointer and points to an unmarked block, mark it and try to
|
# The passed in block must already been marked.
|
||||||
# push it on the stack.
|
|
||||||
if is_heap_pointer(pointer):
|
|
||||||
# Get the block number this pointer points to.
|
|
||||||
block = BLOCK_FROM_PTR(pointer)
|
|
||||||
if block.state == HEAD:
|
|
||||||
block.state = MARK
|
|
||||||
# Is there space left on the GC stack?
|
|
||||||
if len(gc.stack) < MICROPY_ALLOC_GC_STACK_SIZE:
|
|
||||||
# Yes, add this block to the stack.
|
|
||||||
gc.stack.append(block)
|
|
||||||
else:
|
|
||||||
# Sadly, no. The mark phase can continue, but the whole memory will need
|
|
||||||
# to be scanned again (some marked blocks may have unmarked children).
|
|
||||||
gc.stack_overflow = True
|
|
||||||
|
|
||||||
def gc_drain_stack():
|
|
||||||
# Try to reduce the stack, and don't return until it's empty. But with every
|
# Try to reduce the stack, and don't return until it's empty. But with every
|
||||||
# reduction, multiple blocks may be added to the stack so this function may
|
# reduction, multiple blocks may be added to the stack so this function may
|
||||||
# actually hit a stack overflow.
|
# actually hit a stack overflow.
|
||||||
while len(gc.stack) > 0:
|
while True:
|
||||||
block = gc.stack.pop()
|
# Check this block's children.
|
||||||
# Each block contains 4 memory words (on a 32-bit system with 16-byte
|
# Each block contains 4 memory words (on a 32-bit system with 16-byte
|
||||||
# blocks). These words may be pointers to blocks on the heap, but may also
|
# blocks). These words may be pointers to blocks on the heap, but may also
|
||||||
# be other things like integers, parts of raw data (strings, bytecode, etc.)
|
# be other things like integers, parts of raw data (strings, bytecode, etc.)
|
||||||
# or pointers to memory outside of the heap.
|
# or pointers to memory outside of the heap.
|
||||||
for pointer in block:
|
for pointer in reversed(block.all_pointers_in_chain()[1:]):
|
||||||
may_add_to_stack(pointer)
|
if looks_like_pointer(pointer):
|
||||||
|
childblock = block_from_ptr(pointer)
|
||||||
|
if childblock.state == HEAD:
|
||||||
|
childblock.state = MARK
|
||||||
|
# Is there space left on the GC stack?
|
||||||
|
if len(gc.stack) < MICROPY_ALLOC_GC_STACK_SIZE:
|
||||||
|
# Yes, add this block to the stack.
|
||||||
|
gc.stack.append(block)
|
||||||
|
else:
|
||||||
|
# Sadly, no. The mark phase can continue, but the whole memory will need
|
||||||
|
# to be scanned again (some marked blocks may have unmarked children).
|
||||||
|
gc.stack_overflow = True
|
||||||
|
|
||||||
|
# Are there any blocks on the stack?
|
||||||
|
if len(gc.stack) == 0:
|
||||||
|
break # No, stack is empty, we're done.
|
||||||
|
|
||||||
|
# pop the next block off the stack
|
||||||
|
block = gc.stack.pop()
|
||||||
|
|
||||||
def gc_deal_with_stack_overflow():
|
def gc_deal_with_stack_overflow():
|
||||||
# Keep scanning the whole memory until all marked blocks don't have unmarked
|
# Keep scanning the whole memory until all marked blocks don't have unmarked
|
||||||
|
@ -143,16 +147,13 @@ def gc_deal_with_stack_overflow():
|
||||||
# The stack is there to make the mark phase more efficient, i.e. it avoids
|
# The stack is there to make the mark phase more efficient, i.e. it avoids
|
||||||
# having to scan the whole memory too often. But sometimes, the stack is too
|
# having to scan the whole memory too often. But sometimes, the stack is too
|
||||||
# small and requires a full scan (like at the beginning of the mark phase).
|
# small and requires a full scan (like at the beginning of the mark phase).
|
||||||
gc.stack_overflow = True # indicates the whole memory needs to be scanned
|
|
||||||
while gc.stack_overflow:
|
while gc.stack_overflow:
|
||||||
gc.stack_overflow = False
|
gc.stack_overflow = False
|
||||||
gc.stack = []
|
|
||||||
for block in gc.memory:
|
for block in gc.memory:
|
||||||
# A block can have the states FREE, HEAD, TAIL or MARK, as set in the
|
# A block can have the states FREE, HEAD, TAIL or MARK, as set in the
|
||||||
# allocation table.
|
# allocation table.
|
||||||
if block.state == MARK:
|
if block.state == MARK:
|
||||||
gc.stack.append(block)
|
gc_mark_subtree(block)
|
||||||
gc_drain_stack()
|
|
||||||
|
|
||||||
def gc_sweep():
|
def gc_sweep():
|
||||||
# Free unmarked blocks and unmark marked blocks.
|
# Free unmarked blocks and unmark marked blocks.
|
||||||
|
|
Loading…
Reference in New Issue