GH-126491: GC: Mark objects reachable from roots before doing cycle collection (GH-127110)

* Mark almost all reachable objects before doing collection phase

* Add stats for objects marked

* Visit new frames before each increment

* Update docs

* Clearer calculation of work to do.
This commit is contained in:
Mark Shannon 2024-12-02 10:12:17 +00:00 committed by GitHub
parent 2a373da770
commit a8dd821d5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 365 additions and 113 deletions

View file

@ -31,6 +31,11 @@ except ImportError:
return C
ContainerNoGC = None
try:
import _testinternalcapi
except ImportError:
_testinternalcapi = None
### Support code
###############################################################################
@ -1130,6 +1135,7 @@ class IncrementalGCTests(unittest.TestCase):
def tearDown(self):
gc.disable()
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
@requires_gil_enabled("Free threading does not support incremental GC")
# Use small increments to emulate longer running process in a shorter time
@gc_threshold(200, 10)
@ -1167,20 +1173,15 @@ class IncrementalGCTests(unittest.TestCase):
enabled = gc.isenabled()
gc.enable()
olds = []
initial_heap_size = _testinternalcapi.get_tracked_heap_size()
for i in range(20_000):
newhead = make_ll(20)
count += 20
newhead.surprise = head
olds.append(newhead)
if len(olds) == 20:
stats = gc.get_stats()
young = stats[0]
incremental = stats[1]
old = stats[2]
collected = young['collected'] + incremental['collected'] + old['collected']
count += CORRECTION
live = count - collected
self.assertLess(live, 25000)
new_objects = _testinternalcapi.get_tracked_heap_size() - initial_heap_size
self.assertLess(new_objects, 27_000, f"Heap growing. Reached limit after {i} iterations")
del olds[:]
if not enabled:
gc.disable()
@ -1322,7 +1323,8 @@ class GCCallbackTests(unittest.TestCase):
from test.support import gc_collect, SuppressCrashReport
a = [1, 2, 3]
b = [a]
b = [a, a]
a.append(b)
# Avoid coredump when Py_FatalError() calls abort()
SuppressCrashReport().__enter__()
@ -1332,6 +1334,8 @@ class GCCallbackTests(unittest.TestCase):
# (to avoid deallocating it):
import ctypes
ctypes.pythonapi.Py_DecRef(ctypes.py_object(a))
del a
del b
# The garbage collector should now have a fatal error
# when it reaches the broken object
@ -1360,7 +1364,7 @@ class GCCallbackTests(unittest.TestCase):
self.assertRegex(stderr,
br'object type name: list')
self.assertRegex(stderr,
br'object repr : \[1, 2, 3\]')
br'object repr : \[1, 2, 3, \[\[...\], \[...\]\]\]')
class GCTogglingTests(unittest.TestCase):