Update pseudocode to better reflect how the GC works.
parent
c75890ed75
commit
1358308750
|
@ -79,43 +79,32 @@ The `gc_collect` function itself is implemented per port, so that it can easily
|
|||
The Python pseudocode of the garbage collector (mark and sweep) would be the following:
|
||||
|
||||
```python
|
||||
|
||||
gc.stack_overflow = False
|
||||
gc.stack = [] # array of blocks that are marked but not their children
|
||||
gc.memory = ... # list of all blocks managed by the memory manager
|
||||
|
||||
MICROPY_ALLOC_GC_STACK_SIZE = 64 # default stack size (see py/mpconfig.h)
|
||||
|
||||
def gc_mark(): # gc_deal_with_stack_overflow
|
||||
# Keep scanning the whole memory until all marked blocks don't have unmarked
|
||||
# children.
|
||||
# A 'stack overflow' happens if the GC stack cannot contain any more items.
|
||||
# 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
|
||||
# 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:
|
||||
gc.stack_overflow = False
|
||||
gc.stack = []
|
||||
for block in gc.memory:
|
||||
# A block can have the states FREE, HEAD, TAIL or MARK, as set in the
|
||||
# allocation table.
|
||||
if block.state == MARK:
|
||||
gc.stack.append(block)
|
||||
gc_drain_stack()
|
||||
def gc_collect(): # implemented by ports
|
||||
# add root pointers (platform-dependent)
|
||||
gc_collect_root(roots)
|
||||
|
||||
def gc_drain_stack():
|
||||
# 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
|
||||
# actually hit a stack overflow.
|
||||
while len(gc.stack) > 0:
|
||||
block = gc.stack.pop()
|
||||
# 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
|
||||
# be other things like integers, parts of raw data (strings, bytecode, etc.)
|
||||
# or pointers to memory outside of the heap.
|
||||
for pointer in block:
|
||||
may_add_to_stack(pointer)
|
||||
# mark all reachable blocks
|
||||
gc_mark()
|
||||
|
||||
# if there was a stack overflow, scan all memory for reachable blocks
|
||||
gc_deal_with_stack_overflow()
|
||||
|
||||
# free all unreachable blocks
|
||||
gc_sweep()
|
||||
|
||||
def gc_collect_root(roots):
|
||||
for root in roots:
|
||||
# Add this root to the (empty) stack. len(gc.stack) will be 1.
|
||||
may_add_to_stack(root)
|
||||
|
||||
# Shrink the stack to zero, hopefully without GC stack overflow.
|
||||
gc_drain_stack()
|
||||
|
||||
def may_add_to_stack(pointer): # VERIFY_MARK_AND_PUSH
|
||||
# If this is a pointer and points to an unmarked block, mark it and try to
|
||||
|
@ -134,6 +123,37 @@ def may_add_to_stack(pointer): # VERIFY_MARK_AND_PUSH
|
|||
# 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
|
||||
# reduction, multiple blocks may be added to the stack so this function may
|
||||
# actually hit a stack overflow.
|
||||
while len(gc.stack) > 0:
|
||||
block = gc.stack.pop()
|
||||
# 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
|
||||
# be other things like integers, parts of raw data (strings, bytecode, etc.)
|
||||
# or pointers to memory outside of the heap.
|
||||
for pointer in block:
|
||||
may_add_to_stack(pointer)
|
||||
|
||||
def gc_deal_with_stack_overflow():
|
||||
# Keep scanning the whole memory until all marked blocks don't have unmarked
|
||||
# children.
|
||||
# A 'stack overflow' happens if the GC stack cannot contain any more items.
|
||||
# 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
|
||||
# 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:
|
||||
gc.stack_overflow = False
|
||||
gc.stack = []
|
||||
for block in gc.memory:
|
||||
# A block can have the states FREE, HEAD, TAIL or MARK, as set in the
|
||||
# allocation table.
|
||||
if block.state == MARK:
|
||||
gc.stack.append(block)
|
||||
gc_drain_stack()
|
||||
|
||||
def gc_sweep():
|
||||
# Free unmarked blocks and unmark marked blocks.
|
||||
free_tail = False
|
||||
|
@ -147,15 +167,6 @@ def gc_sweep():
|
|||
elif block.state == MARK:
|
||||
block.state = HEAD
|
||||
free_tail = False
|
||||
|
||||
def gc_collect(): # implemented by ports
|
||||
# add root pointers (platform-dependent)
|
||||
|
||||
# mark all reachable blocks
|
||||
gc_mark()
|
||||
|
||||
# free all unreachable blocks
|
||||
gc_sweep()
|
||||
```
|
||||
|
||||
### Macros
|
||||
|
|
Loading…
Reference in New Issue