gc — 垃圾回收
优质
小牛编辑
135浏览
2023-12-01
Tracing References
# gc_get_referents.py
import gc
import pprint
class Graph:
def __init__(self, name):
self.name = name
self.next = None
def set_next(self, next):
print('Linking nodes {}.next = {}'.format(self, next))
self.next = next
def __repr__(self):
return '{}({})'.format(
self.__class__.__name__, self.name)
# Construct a graph cycle
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)
print()
print('three refers to:')
for r in gc.get_referents(three):
pprint.pprint(r)
# gc_get_referents_cycles.py
import gc
import pprint
import queue
class Graph:
def __init__(self, name):
self.name = name
self.next = None
def set_next(self, next):
print('Linking nodes {}.next = {}'.format(self, next))
self.next = next
def __repr__(self):
return '{}({})'.format(
self.__class__.__name__, self.name)
# Construct a graph cycle
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)
print()
seen = set()
to_process = queue.Queue()
# Start with an empty object chain and Graph three.
to_process.put(([], three))
# Look for cycles, building the object chain for each object
# found in the queue so the full cycle can be printed at the
# end.
while not to_process.empty():
chain, next = to_process.get()
chain = chain[:]
chain.append(next)
print('Examining:', repr(next))
seen.add(id(next))
for r in gc.get_referents(next):
if isinstance(r, str) or isinstance(r, type):
# Ignore strings and classes
pass
elif id(r) in seen:
print()
print('Found a cycle to {}:'.format(r))
for i, link in enumerate(chain):
print(' {}: '.format(i), end=' ')
pprint.pprint(link)
else:
to_process.put((chain, r))
Forcing Garbage Collection
# gc_collect.py
import gc
import pprint
class Graph:
def __init__(self, name):
self.name = name
self.next = None
def set_next(self, next):
print('Linking nodes {}.next = {}'.format(self, next))
self.next = next
def __repr__(self):
return '{}({})'.format(
self.__class__.__name__, self.name)
# Construct a graph cycle
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)
# Remove references to the graph nodes in this module's namespace
one = two = three = None
# Show the effect of garbage collection
for i in range(2):
print('\nCollecting {} ...'.format(i))
n = gc.collect()
print('Unreachable objects:', n)
print('Remaining Garbage:', end=' ')
pprint.pprint(gc.garbage)
Finding References to Objects that Cannot be Collected
# gc_get_referrers.py
import gc
import pprint
class Graph:
def __init__(self, name):
self.name = name
self.next = None
def set_next(self, next):
print('Linking nodes {}.next = {}'.format(self, next))
self.next = next
def __repr__(self):
return '{}({})'.format(
self.__class__.__name__, self.name)
def __del__(self):
print('{}.__del__()'.format(self))
# Construct a graph cycle
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)
# Collecting now keeps the objects as uncollectable,
# but not garbage.
print()
print('Collecting...')
n = gc.collect()
print('Unreachable objects:', n)
print('Remaining Garbage:', end=' ')
pprint.pprint(gc.garbage)
# Ignore references from local variables in this module, global
# variables, and from the garbage collector's bookkeeping.
REFERRERS_TO_IGNORE = [locals(), globals(), gc.garbage]
def find_referring_graphs(obj):
print('Looking for references to {!r}'.format(obj))
referrers = (r for r in gc.get_referrers(obj)
if r not in REFERRERS_TO_IGNORE)
for ref in referrers:
if isinstance(ref, Graph):
# A graph node
yield ref
elif isinstance(ref, dict):
# An instance or other namespace dictionary
for parent in find_referring_graphs(ref):
yield parent
# Look for objects that refer to the objects in the graph.
print()
print('Clearing referrers:')
for obj in [one, two, three]:
for ref in find_referring_graphs(obj):
print('Found referrer:', ref)
ref.set_next(None)
del ref # remove reference so the node can be deleted
del obj # remove reference so the node can be deleted
# Clear references held by gc.garbage
print()
print('Clearing gc.garbage:')
del gc.garbage[:]
# Everything should have been freed this time
print()
print('Collecting...')
n = gc.collect()
print('Unreachable objects:', n)
print('Remaining Garbage:', end=' ')
pprint.pprint(gc.garbage)
Collection Thresholds and Generations
# gc_get_threshold.py
import gc
print(gc.get_threshold())
# gc_threshold.py
import gc
import pprint
import sys
try:
threshold = int(sys.argv[1])
except (IndexError, ValueError, TypeError):
print('Missing or invalid threshold, using default')
threshold = 5
class MyObj:
def __init__(self, name):
self.name = name
print('Created', self.name)
gc.set_debug(gc.DEBUG_STATS)
gc.set_threshold(threshold, 1, 1)
print('Thresholds:', gc.get_threshold())
print('Clear the collector by forcing a run')
gc.collect()
print()
print('Creating objects')
objs = []
for i in range(10):
objs.append(MyObj(i))
print('Exiting')
# Turn off debugging
gc.set_debug(0)
Debugging
# gc_debug_stats.py
import gc
gc.set_debug(gc.DEBUG_STATS)
gc.collect()
print('Exiting')
# gc_debug_saveall.py
import gc
flags = (gc.DEBUG_COLLECTABLE |
gc.DEBUG_UNCOLLECTABLE |
gc.DEBUG_SAVEALL
)
gc.set_debug(flags)
class Graph:
def __init__(self, name):
self.name = name
self.next = None
def set_next(self, next):
self.next = next
def __repr__(self):
return '{}({})'.format(
self.__class__.__name__, self.name)
class CleanupGraph(Graph):
def __del__(self):
print('{}.__del__()'.format(self))
# Construct a graph cycle
one = Graph('one')
two = Graph('two')
one.set_next(two)
two.set_next(one)
# Construct another node that stands on its own
three = CleanupGraph('three')
# Construct a graph cycle with a finalizer
four = CleanupGraph('four')
five = CleanupGraph('five')
four.set_next(five)
five.set_next(four)
# Remove references to the graph nodes in this module's namespace
one = two = three = four = five = None
# Force a sweep
print('Collecting')
gc.collect()
print('Done')
# Report on what was left
for o in gc.garbage:
if isinstance(o, Graph):
print('Retained: {} 0x{:x}'.format(o, id(o)))
# Reset the debug flags before exiting to avoid dumping a lot
# of extra information and making the example output more
# confusing.
gc.set_debug(0)
# gc_debug_leak.py
import gc
flags = gc.DEBUG_LEAK
gc.set_debug(flags)
class Graph:
def __init__(self, name):
self.name = name
self.next = None
def set_next(self, next):
self.next = next
def __repr__(self):
return '{}({})'.format(
self.__class__.__name__, self.name)
class CleanupGraph(Graph):
def __del__(self):
print('{}.__del__()'.format(self))
# Construct a graph cycle
one = Graph('one')
two = Graph('two')
one.set_next(two)
two.set_next(one)
# Construct another node that stands on its own
three = CleanupGraph('three')
# Construct a graph cycle with a finalizer
four = CleanupGraph('four')
five = CleanupGraph('five')
four.set_next(five)
five.set_next(four)
# Remove references to the graph nodes in this module's namespace
one = two = three = four = five = None
# Force a sweep
print('Collecting')
gc.collect()
print('Done')
# Report on what was left
for o in gc.garbage:
if isinstance(o, Graph):
print('Retained: {} 0x{:x}'.format(o, id(o)))
# Reset the debug flags before exiting to avoid dumping a lot
# of extra information and making the example output more
# confusing.
gc.set_debug(0)