From 89515be596a0ca05fd9ab4ddf76c8013dd093545 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Fri, 11 Oct 2024 21:18:37 +0100 Subject: [PATCH] gh-119786: Move garbage collection doc from devguide to InternalDocs (#125282) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Co-Authored-By: Carol Willing carolcode@willingconsulting.com Co-Authored-By: Ezio Melotti ezio.melotti@gmail.com Co-Authored-By: Hugo van Kemenade hugovk@users.noreply.github.com Co-Authored-By: Itamar Ostricher itamarost@gmail.com Co-Authored-By: Jesús Cea jcea@jcea.es Co-Authored-By: Joannah Nanjekye 33177550+nanjekyejoannah@users.noreply.github.com Co-Authored-By: Ned Batchelder ned@nedbatchelder.com Co-Authored-By: Pablo Galindo Salgado Pablogsal@gmail.com Co-Authored-By: Pamela Fox pamela.fox@gmail.com Co-Authored-By: Sam Gross colesbury@gmail.com Co-Authored-By: Stefan Pochmann 609905+pochmann@users.noreply.github.com Co-Authored-By: T. Wouters thomas@python.org Co-Authored-By: q-ata 24601033+q-ata@users.noreply.github.com Co-Authored-By: slateny 46876382+slateny@users.noreply.github.com Co-Authored-By: Борис Верховский boris.verk@gmail.com Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Jacob Coffee --- InternalDocs/README.md | 2 + InternalDocs/garbage_collector.md | 596 ++++++++++++++++++ .../images/python-cyclic-gc-1-new-page.png | Bin 0 -> 4415 bytes .../images/python-cyclic-gc-2-new-page.png | Bin 0 -> 4337 bytes .../images/python-cyclic-gc-3-new-page.png | Bin 0 -> 4876 bytes .../images/python-cyclic-gc-4-new-page.png | Bin 0 -> 4863 bytes .../images/python-cyclic-gc-5-new-page.png | Bin 0 -> 5712 bytes 7 files changed, 598 insertions(+) create mode 100644 InternalDocs/garbage_collector.md create mode 100644 InternalDocs/images/python-cyclic-gc-1-new-page.png create mode 100644 InternalDocs/images/python-cyclic-gc-2-new-page.png create mode 100644 InternalDocs/images/python-cyclic-gc-3-new-page.png create mode 100644 InternalDocs/images/python-cyclic-gc-4-new-page.png create mode 100644 InternalDocs/images/python-cyclic-gc-5-new-page.png diff --git a/InternalDocs/README.md b/InternalDocs/README.md index 8956ecafed20..805e2f97937e 100644 --- a/InternalDocs/README.md +++ b/InternalDocs/README.md @@ -22,4 +22,6 @@ it is not, please report that through the [The Source Code Locations Table](locations.md) +[Garbage collector design](garbage_collector.md) + [Exception Handling](exception_handling.md) diff --git a/InternalDocs/garbage_collector.md b/InternalDocs/garbage_collector.md new file mode 100644 index 000000000000..fd0246fa1a60 --- /dev/null +++ b/InternalDocs/garbage_collector.md @@ -0,0 +1,596 @@ + +Garbage collector design +======================== + +Abstract +======== + +The main garbage collection algorithm used by CPython is reference counting. The basic idea is +that CPython counts how many different places there are that have a reference to an +object. Such a place could be another object, or a global (or static) C variable, or +a local variable in some C function. When an object’s reference count becomes zero, +the object is deallocated. If it contains references to other objects, their +reference counts are decremented. Those other objects may be deallocated in turn, if +this decrement makes their reference count become zero, and so on. The reference +count field can be examined using the ``sys.getrefcount()`` function (notice that the +value returned by this function is always 1 more as the function also has a reference +to the object when called): + +```pycon + >>> x = object() + >>> sys.getrefcount(x) + 2 + >>> y = x + >>> sys.getrefcount(x) + 3 + >>> del y + >>> sys.getrefcount(x) + 2 +``` + +The main problem with the reference counting scheme is that it does not handle reference +cycles. For instance, consider this code: + +```pycon + >>> container = [] + >>> container.append(container) + >>> sys.getrefcount(container) + 3 + >>> del container +``` + +In this example, ``container`` holds a reference to itself, so even when we remove +our reference to it (the variable "container") the reference count never falls to 0 +because it still has its own internal reference. Therefore it would never be +cleaned just by simple reference counting. For this reason some additional machinery +is needed to clean these reference cycles between objects once they become +unreachable. This is the cyclic garbage collector, usually called just Garbage +Collector (GC), even though reference counting is also a form of garbage collection. + +Starting in version 3.13, CPython contains two GC implementations: + +- The default build implementation relies on the + [global interpreter lock](https://docs.python.org/3/glossary.html#term-global-interpreter-lock) + for thread safety. +- The free-threaded build implementation pauses other executing threads when + performing a collection for thread safety. + +Both implementations use the same basic algorithms, but operate on different +data structures. The the section on +[Differences between GC implementations](#Differences-between-GC-implementations) +for the details. + + +Memory layout and object structure +================================== + +The garbage collector requires additional fields in Python objects to support +garbage collection. These extra fields are different in the default and the +free-threaded builds. + + +GC for the default build +------------------------ + +Normally the C structure supporting a regular Python object looks as follows: + +``` + object -----> +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ \ + | ob_refcnt | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | PyObject_HEAD + | *ob_type | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ / + | ... | +``` + +In order to support the garbage collector, the memory layout of objects is altered +to accommodate extra information **before** the normal layout: + +``` + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ \ + | *_gc_next | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | PyGC_Head + | *_gc_prev | | + object -----> +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ / + | ob_refcnt | \ + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | PyObject_HEAD + | *ob_type | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ / + | ... | +``` + + +In this way the object can be treated as a normal python object and when the extra +information associated to the GC is needed the previous fields can be accessed by a +simple type cast from the original object: `((PyGC_Head *)(the_object)-1)`. + +As is explained later in the +[Optimization: reusing fields to save memory](#optimization-reusing-fields-to-save-memory) +section, these two extra fields are normally used to keep doubly linked lists of all the +objects tracked by the garbage collector (these lists are the GC generations, more on +that in the [Optimization: generations](#Optimization-generations) section), but +they are also reused to fulfill other purposes when the full doubly linked list +structure is not needed as a memory optimization. + +Doubly linked lists are used because they efficiently support the most frequently required operations. In +general, the collection of all objects tracked by GC is partitioned into disjoint sets, each in its own +doubly linked list. Between collections, objects are partitioned into "generations", reflecting how +often they've survived collection attempts. During collections, the generation(s) being collected +are further partitioned into, for example, sets of reachable and unreachable objects. Doubly linked lists +support moving an object from one partition to another, adding a new object, removing an object +entirely (objects tracked by GC are most often reclaimed by the refcounting system when GC +isn't running at all!), and merging partitions, all with a small constant number of pointer updates. +With care, they also support iterating over a partition while objects are being added to - and +removed from - it, which is frequently required while GC is running. + +GC for the free-threaded build +------------------------------ + +In the free-threaded build, Python objects contain a 1-byte field +``ob_gc_bits`` that is used to track garbage collection related state. The +field exists in all objects, including ones that do not support cyclic +garbage collection. The field is used to identify objects that are tracked +by the collector, ensure that finalizers are called only once per object, +and, during garbage collection, differentiate reachable vs. unreachable objects. + +``` + object -----> +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ \ + | ob_tid | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | + | pad | ob_mutex | ob_gc_bits | ob_ref_local | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | PyObject_HEAD + | ob_ref_shared | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | + | *ob_type | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ / + | ... | +``` + +Note that not all fields are to scale. ``pad`` is two bytes, ``ob_mutex`` and +``ob_gc_bits`` are each one byte, and ``ob_ref_local`` is four bytes. The +other fields, ``ob_tid``, ``ob_ref_shared``, and ``ob_type``, are all +pointer-sized (that is, eight bytes on a 64-bit platform). + + +The garbage collector also temporarily repurposes the ``ob_tid`` (thread ID) +and ``ob_ref_local`` (local reference count) fields for other purposes during +collections. + + +C APIs +------ + +Specific APIs are offered to allocate, deallocate, initialize, track, and untrack +objects with GC support. These APIs can be found in the +[Garbage Collector C API documentation](https://docs.python.org/3/c-api/gcsupport.html). + +Apart from this object structure, the type object for objects supporting garbage +collection must include the ``Py_TPFLAGS_HAVE_GC`` in its ``tp_flags`` slot and +provide an implementation of the ``tp_traverse`` handler. Unless it can be proven +that the objects cannot form reference cycles with only objects of its type or unless +the type is immutable, a ``tp_clear`` implementation must also be provided. + + +Identifying reference cycles +============================ + +The algorithm that CPython uses to detect those reference cycles is +implemented in the ``gc`` module. The garbage collector **only focuses** +on cleaning container objects (that is, objects that can contain a reference +to one or more objects). These can be arrays, dictionaries, lists, custom +class instances, classes in extension modules, etc. One could think that +cycles are uncommon but the truth is that many internal references needed by +the interpreter create cycles everywhere. Some notable examples: + +- Exceptions contain traceback objects that contain a list of frames that + contain the exception itself. +- Module-level functions reference the module's dict (which is needed to resolve globals), + which in turn contains entries for the module-level functions. +- Instances have references to their class which itself references its module, and the module + contains references to everything that is inside (and maybe other modules) + and this can lead back to the original instance. +- When representing data structures like graphs, it is very typical for them to + have internal links to themselves. + +To correctly dispose of these objects once they become unreachable, they need +to be identified first. To understand how the algorithm works, let’s take +the case of a circular linked list which has one link referenced by a +variable ``A``, and one self-referencing object which is completely +unreachable: + +```pycon + >>> import gc + + >>> class Link: + ... def __init__(self, next_link=None): + ... self.next_link = next_link + + >>> link_3 = Link() + >>> link_2 = Link(link_3) + >>> link_1 = Link(link_2) + >>> link_3.next_link = link_1 + >>> A = link_1 + >>> del link_1, link_2, link_3 + + >>> link_4 = Link() + >>> link_4.next_link = link_4 + >>> del link_4 + + # Collect the unreachable Link object (and its .__dict__ dict). + >>> gc.collect() + 2 +``` + +The GC starts with a set of candidate objects it wants to scan. In the +default build, these "objects to scan" might be all container objects or a +smaller subset (or "generation"). In the free-threaded build, the collector +always scans all container objects. + +The objective is to identify all the unreachable objects. The collector does +this by identifying reachable objects; the remaining objects must be +unreachable. The first step is to identify all of the "to scan" objects that +are **directly** reachable from outside the set of candidate objects. These +objects have a refcount larger than the number of incoming references from +within the candidate set. + +Every object that supports garbage collection will have an extra reference +count field initialized to the reference count (``gc_ref`` in the figures) +of that object when the algorithm starts. This is because the algorithm needs +to modify the reference count to do the computations and in this way the +interpreter will not modify the real reference count field. + +![gc-image1](images/python-cyclic-gc-1-new-page.png) + +The GC then iterates over all containers in the first list and decrements by one the +`gc_ref` field of any other object that container is referencing. Doing +this makes use of the ``tp_traverse`` slot in the container class (implemented +using the C API or inherited by a superclass) to know what objects are referenced by +each container. After all the objects have been scanned, only the objects that have +references from outside the “objects to scan” list will have ``gc_ref > 0``. + +![gc-image2](images/python-cyclic-gc-2-new-page.png) + +Notice that having ``gc_ref == 0`` does not imply that the object is unreachable. +This is because another object that is reachable from the outside (``gc_ref > 0``) +can still have references to it. For instance, the ``link_2`` object in our example +ended having ``gc_ref == 0`` but is referenced still by the ``link_1`` object that +is reachable from the outside. To obtain the set of objects that are really +unreachable, the garbage collector re-scans the container objects using the +``tp_traverse`` slot; this time with a different traverse function that marks objects with +``gc_ref == 0`` as "tentatively unreachable" and then moves them to the +tentatively unreachable list. The following image depicts the state of the lists in a +moment when the GC processed the ``link_3`` and ``link_4`` objects but has not +processed ``link_1`` and ``link_2`` yet. + +![gc-image3](images/python-cyclic-gc-3-new-page.png) + +Then the GC scans the next ``link_1`` object. Because it has ``gc_ref == 1``, +the gc does not do anything special because it knows it has to be reachable (and is +already in what will become the reachable list): + +![gc-image4](images/python-cyclic-gc-4-new-page.png) + +When the GC encounters an object which is reachable (``gc_ref > 0``), it traverses +its references using the ``tp_traverse`` slot to find all the objects that are +reachable from it, moving them to the end of the list of reachable objects (where +they started originally) and setting its ``gc_ref`` field to 1. This is what happens +to ``link_2`` and ``link_3`` below as they are reachable from ``link_1``. From the +state in the previous image and after examining the objects referred to by ``link_1`` +the GC knows that ``link_3`` is reachable after all, so it is moved back to the +original list and its ``gc_ref`` field is set to 1 so that if the GC visits it again, +it will know that it's reachable. To avoid visiting an object twice, the GC marks all +objects that have already been visited once (by unsetting the ``PREV_MASK_COLLECTING`` +flag) so that if an object that has already been processed is referenced by some other +object, the GC does not process it twice. + +![gc-image5](images/python-cyclic-gc-5-new-page.png) + +Notice that an object that was marked as "tentatively unreachable" and was later +moved back to the reachable list will be visited again by the garbage collector +as now all the references that that object has need to be processed as well. This +process is really a breadth first search over the object graph. Once all the objects +are scanned, the GC knows that all container objects in the tentatively unreachable +list are really unreachable and can thus be garbage collected. + +Pragmatically, it's important to note that no recursion is required by any of this, +and neither does it in any other way require additional memory proportional to the +number of objects, number of pointers, or the lengths of pointer chains. Apart from +``O(1)`` storage for internal C needs, the objects themselves contain all the storage +the GC algorithms require. + +Why moving unreachable objects is better +---------------------------------------- + +It sounds logical to move the unreachable objects under the premise that most objects +are usually reachable, until you think about it: the reason it pays isn't actually +obvious. + +Suppose we create objects A, B, C in that order. They appear in the young generation +in the same order. If B points to A, and C to B, and C is reachable from outside, +then the adjusted refcounts after the first step of the algorithm runs will be 0, 0, +and 1 respectively because the only reachable object from the outside is C. + +When the next step of the algorithm finds A, A is moved to the unreachable list. The +same for B when it's first encountered. Then C is traversed, B is moved *back* to +the reachable list. B is eventually traversed, and then A is moved back to the reachable +list. + +So instead of not moving at all, the reachable objects B and A are each moved twice. +Why is this a win? A straightforward algorithm to move the reachable objects instead +would move A, B, and C once each. The key is that this dance leaves the objects in +order C, B, A - it's reversed from the original order. On all *subsequent* scans, +none of them will move. Since most objects aren't in cycles, this can save an +unbounded number of moves across an unbounded number of later collections. The only +time the cost can be higher is the first time the chain is scanned. + +Destroying unreachable objects +============================== + +Once the GC knows the list of unreachable objects, a very delicate process starts +with the objective of completely destroying these objects. Roughly, the process +follows these steps in order: + +1. Handle and clear weak references (if any). Weak references to unreachable objects + are set to ``None``. If the weak reference has an associated callback, the callback + is enqueued to be called once the clearing of weak references is finished. We only + invoke callbacks for weak references that are themselves reachable. If both the weak + reference and the pointed-to object are unreachable we do not execute the callback. + This is partly for historical reasons: the callback could resurrect an unreachable + object and support for weak references predates support for object resurrection. + Ignoring the weak reference's callback is fine because both the object and the weakref + are going away, so it's legitimate to say the weak reference is going away first. +2. If an object has legacy finalizers (``tp_del`` slot) move it to the + ``gc.garbage`` list. +3. Call the finalizers (``tp_finalize`` slot) and mark the objects as already + finalized to avoid calling finalizers twice if the objects are resurrected or + if other finalizers have removed the object first. +4. Deal with resurrected objects. If some objects have been resurrected, the GC + finds the new subset of objects that are still unreachable by running the cycle + detection algorithm again and continues with them. +5. Call the ``tp_clear`` slot of every object so all internal links are broken and + the reference counts fall to 0, triggering the destruction of all unreachable + objects. + +Optimization: generations +========================= + +In order to limit the time each garbage collection takes, the GC +implementation for the default build uses a popular optimization: +generations. The main idea behind this concept is the assumption that most +objects have a very short lifespan and can thus be collected soon after their +creation. This has proven to be very close to the reality of many Python +programs as many temporary objects are created and destroyed very quickly. + +To take advantage of this fact, all container objects are segregated into +three spaces/generations. Every new +object starts in the first generation (generation 0). The previous algorithm is +executed only over the objects of a particular generation and if an object +survives a collection of its generation it will be moved to the next one +(generation 1), where it will be surveyed for collection less often. If +the same object survives another GC round in this new generation (generation 1) +it will be moved to the last generation (generation 2) where it will be +surveyed the least often. + +The GC implementation for the free-threaded build does not use multiple +generations. Every collection operates on the entire heap. + +In order to decide when to run, the collector keeps track of the number of object +allocations and deallocations since the last collection. When the number of +allocations minus the number of deallocations exceeds ``threshold_0``, +collection starts. Initially only generation 0 is examined. If generation 0 has +been examined more than ``threshold_1`` times since generation 1 has been +examined, then generation 1 is examined as well. With generation 2, +things are a bit more complicated; see +[Collecting the oldest generation](#Collecting-the-oldest-generation) for +more information. These thresholds can be examined using the +[`gc.get_threshold()`](https://docs.python.org/3/library/gc.html#gc.get_threshold) +function: + +```pycon + >>> import gc + >>> gc.get_threshold() + (700, 10, 10) +``` + +The content of these generations can be examined using the +``gc.get_objects(generation=NUM)`` function and collections can be triggered +specifically in a generation by calling ``gc.collect(generation=NUM)``. + +```pycon + >>> import gc + >>> class MyObj: + ... pass + ... + + # Move everything to the last generation so it's easier to inspect + # the younger generations. + + >>> gc.collect() + 0 + + # Create a reference cycle. + + >>> x = MyObj() + >>> x.self = x + + # Initially the object is in the youngest generation. + + >>> gc.get_objects(generation=0) + [..., <__main__.MyObj object at 0x7fbcc12a3400>, ...] + + # After a collection of the youngest generation the object + # moves to the next generation. + + >>> gc.collect(generation=0) + 0 + >>> gc.get_objects(generation=0) + [] + >>> gc.get_objects(generation=1) + [..., <__main__.MyObj object at 0x7fbcc12a3400>, ...] +``` + +Collecting the oldest generation +-------------------------------- + +In addition to the various configurable thresholds, the GC only triggers a full +collection of the oldest generation if the ratio ``long_lived_pending / long_lived_total`` +is above a given value (hardwired to 25%). The reason is that, while "non-full" +collections (that is, collections of the young and middle generations) will always +examine roughly the same number of objects (determined by the aforementioned +thresholds) the cost of a full collection is proportional to the total +number of long-lived objects, which is virtually unbounded. Indeed, it has +been remarked that doing a full collection every of object +creations entails a dramatic performance degradation in workloads which consist +of creating and storing lots of long-lived objects (for example, building a large list +of GC-tracked objects would show quadratic performance, instead of linear as +expected). Using the above ratio, instead, yields amortized linear performance +in the total number of objects (the effect of which can be summarized thusly: +"each full garbage collection is more and more costly as the number of objects +grows, but we do fewer and fewer of them"). + +Optimization: reusing fields to save memory +=========================================== + +In order to save memory, the two linked list pointers in every object with GC +support are reused for several purposes. This is a common optimization known +as "fat pointers" or "tagged pointers": pointers that carry additional data, +"folded" into the pointer, meaning stored inline in the data representing the +address, taking advantage of certain properties of memory addressing. This is +possible as most architectures align certain types of data +to the size of the data, often a word or multiple thereof. This discrepancy +leaves a few of the least significant bits of the pointer unused, which can be +used for tags or to keep other information – most often as a bit field (each +bit a separate tag) – as long as code that uses the pointer masks out these +bits before accessing memory. For example, on a 32-bit architecture (for both +addresses and word size), a word is 32 bits = 4 bytes, so word-aligned +addresses are always a multiple of 4, hence end in ``00``, leaving the last 2 bits +available; while on a 64-bit architecture, a word is 64 bits = 8 bytes, so +word-aligned addresses end in ``000``, leaving the last 3 bits available. + +The CPython GC makes use of two fat pointers that correspond to the extra fields +of ``PyGC_Head`` discussed in the `Memory layout and object structure`_ section: + +> [!WARNING] +> Because the presence of extra information, "tagged" or "fat" pointers cannot be +> dereferenced directly and the extra information must be stripped off before +> obtaining the real memory address. Special care needs to be taken with +> functions that directly manipulate the linked lists, as these functions +> normally assume the pointers inside the lists are in a consistent state. + + +- The ``_gc_prev`` field is normally used as the "previous" pointer to maintain the + doubly linked list but its lowest two bits are used to keep the flags + ``PREV_MASK_COLLECTING`` and ``_PyGC_PREV_MASK_FINALIZED``. Between collections, + the only flag that can be present is ``_PyGC_PREV_MASK_FINALIZED`` that indicates + if an object has been already finalized. During collections ``_gc_prev`` is + temporarily used for storing a copy of the reference count (``gc_ref``), in + addition to two flags, and the GC linked list becomes a singly linked list until + ``_gc_prev`` is restored. + +- The ``_gc_next`` field is used as the "next" pointer to maintain the doubly linked + list but during collection its lowest bit is used to keep the + ``NEXT_MASK_UNREACHABLE`` flag that indicates if an object is tentatively + unreachable during the cycle detection algorithm. This is a drawback to using only + doubly linked lists to implement partitions: while most needed operations are + constant-time, there is no efficient way to determine which partition an object is + currently in. Instead, when that's needed, ad hoc tricks (like the + ``NEXT_MASK_UNREACHABLE`` flag) are employed. + +Optimization: delay tracking containers +======================================= + +Certain types of containers cannot participate in a reference cycle, and so do +not need to be tracked by the garbage collector. Untracking these objects +reduces the cost of garbage collection. However, determining which objects may +be untracked is not free, and the costs must be weighed against the benefits +for garbage collection. There are two possible strategies for when to untrack +a container: + +1. When the container is created. +2. When the container is examined by the garbage collector. + +As a general rule, instances of atomic types aren't tracked and instances of +non-atomic types (containers, user-defined objects...) are. However, some +type-specific optimizations can be present in order to suppress the garbage +collector footprint of simple instances. Some examples of native types that +benefit from delayed tracking: + +- Tuples containing only immutable objects (integers, strings etc, + and recursively, tuples of immutable objects) do not need to be tracked. The + interpreter creates a large number of tuples, many of which will not survive + until garbage collection. It is therefore not worthwhile to untrack eligible + tuples at creation time. Instead, all tuples except the empty tuple are tracked + when created. During garbage collection it is determined whether any surviving + tuples can be untracked. A tuple can be untracked if all of its contents are + already not tracked. Tuples are examined for untracking in all garbage collection + cycles. It may take more than one cycle to untrack a tuple. + +- Dictionaries containing only immutable objects also do not need to be tracked. + Dictionaries are untracked when created. If a tracked item is inserted into a + dictionary (either as a key or value), the dictionary becomes tracked. During a + full garbage collection (all generations), the collector will untrack any dictionaries + whose contents are not tracked. + +The garbage collector module provides the Python function ``is_tracked(obj)``, which returns +the current tracking status of the object. Subsequent garbage collections may change the +tracking status of the object. + +```pycon + >>> gc.is_tracked(0) + False + >>> gc.is_tracked("a") + False + >>> gc.is_tracked([]) + True + >>> gc.is_tracked({}) + False + >>> gc.is_tracked({"a": 1}) + False + >>> gc.is_tracked({"a": []}) + True +``` + +Differences between GC implementations +====================================== + +This section summarizes the differences between the GC implementation in the +default build and the implementation in the free-threaded build. + +The default build implementation makes extensive use of the ``PyGC_Head`` data +structure, while the free-threaded build implementation does not use that +data structure. + +- The default build implementation stores all tracked objects in a doubly + linked list using ``PyGC_Head``. The free-threaded build implementation + instead relies on the embedded mimalloc memory allocator to scan the heap + for tracked objects. +- The default build implementation uses ``PyGC_Head`` for the unreachable + object list. The free-threaded build implementation repurposes the + ``ob_tid`` field to store a unreachable objects linked list. +- The default build implementation stores flags in the ``_gc_prev`` field of + ``PyGC_Head``. The free-threaded build implementation stores these flags + in ``ob_gc_bits``. + + +The default build implementation relies on the +[global interpreter lock](https://docs.python.org/3/glossary.html#term-global-interpreter-lock) +for thread safety. The free-threaded build implementation has two "stop the +world" pauses, in which all other executing threads are temporarily paused so +that the GC can safely access reference counts and object attributes. + +The default build implementation is a generational collector. The +free-threaded build is non-generational; each collection scans the entire +heap. + +- Keeping track of object generations is simple and inexpensive in the default + build. The free-threaded build relies on mimalloc for finding tracked + objects; identifying "young" objects without scanning the entire heap would + be more difficult. + + +> [!NOTE] +> **Document history** +> +> Pablo Galindo Salgado - Original author +> +> Irit Katriel - Convert to Markdown diff --git a/InternalDocs/images/python-cyclic-gc-1-new-page.png b/InternalDocs/images/python-cyclic-gc-1-new-page.png new file mode 100644 index 0000000000000000000000000000000000000000..2ddac50f4b5575888d8d19cd9b6f2e3863132776 GIT binary patch literal 4415 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 literal 0 Hc-jL100001 diff --git a/InternalDocs/images/python-cyclic-gc-2-new-page.png b/InternalDocs/images/python-cyclic-gc-2-new-page.png new file mode 100644 index 0000000000000000000000000000000000000000..159aeeb05024a3247381e1c3aec2b986035b5e44 GIT binary patch literal 4337 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 GU;iIs&jFnP literal 0 Hc-jL100001 diff --git a/InternalDocs/images/python-cyclic-gc-3-new-page.png b/InternalDocs/images/python-cyclic-gc-3-new-page.png new file mode 100644 index 0000000000000000000000000000000000000000..29fab0498e5b106f813e08aa2799602269cd74d1 GIT binary patch literal 4876 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 B=FtEE literal 0 Hc-jL100001 diff --git a/InternalDocs/images/python-cyclic-gc-4-new-page.png b/InternalDocs/images/python-cyclic-gc-4-new-page.png new file mode 100644 index 0000000000000000000000000000000000000000..51a2b1065ea64eaf009a96ce6fecd3132424ea0f GIT binary patch literal 4863 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 literal 0 Hc-jL100001 diff --git a/InternalDocs/images/python-cyclic-gc-5-new-page.png b/InternalDocs/images/python-cyclic-gc-5-new-page.png new file mode 100644 index 0000000000000000000000000000000000000000..fe67a6896fe4b07c03291ca8b21781fde8785757 GIT binary patch literal 5712 zc-nPXc|25Y-=76z9Z~juiEL#n48x$TS+Xw;im@aVgQ8(3;V!Zjg@z+C_N6R$gj6Ey zv1i;!wy{S;vgGyjJoo!N@B4nxGx(10%OQqc>SxT^g&({&YY*Ls6HzbYUwD2wJo>K?Et+*+#rWe$Oehs62Vc?pJZ>(7< zV3Ou`N&C0Vhh34DuvJF$Q!(o894wVTa#o5PNkX&wGmDbK%EppA6zd%dn8D`@aMY=+ zq#Ah!2A6TSvydCh)z(B_v>3>UmXuY*5q%Kz|WuLoSv^U`)X0F z=~$?e?579K3cwW$9Mu)Gq7Zyq<%l700xe2VGD>eN? z?7yb$-xdCQR{vMMkAU7r?CsSxP++sTa5^Z|FWCKgwgTzgF?1%qaC5Ar<-EoyOytT} z!>Ez(`m+@=Gtt3Qk|m}`w2i#Um&b&{A{Or7Qs`)5k{`eU6jW}$FXiV!lyV0%gHxQ~X)|^uOtdj(v_7;JMrVU!mF#!{A7J&03f<2>@oj`S z=z!RoUx}$?$Fm#bv_7`?kmVZ~Y2j!ml`5xOg_4-Q66{}7!Z?NoKBr`OgLujcPjaVi zlNVwEU=Fh9F=LL&G`N0GfZI6N{lzfE}R-mnyDZHjHNSrwEy_Bu<7 z7^q?_TPDh)ZhivjBd+-(!s5?+FcF10BbCARhCKzVGI3{~FGXaO?5}gx{9?3Pw^#!_>tU!Ephldv*LoW>exg$i5}=(n zj9>2QFQ4$;{NWQFSn96wMJ#{0xUB$rt!|3Zm%?pBRh16S^=wB#S50gxt;TtZn%ANg zf_{}+krs2jLn7JJDl62V)kNmYksB2=F4Joic2c;FVyWxbB>H_jLBZ!3gC>q+q~7IJ$7|jgTgb@OLnVn^wMIY z1h~7k#&AMV>|R5tHSeSv#eMlj5@wnN1i6q5d-5QJzU&|a=Pb2W>ZXMj*CI?iHXH=&8iaBaU^N&^u-|(=83hL$7V4U zea|eI=wmnGX^_;lv&77qR{@00WYvo%!`=3i z{USoa(oX)$pvyWmyVTx>fVwGVCBiGMqPm)Y(q}R2Ic;y0jnNZ3VjT1@?e%=agpST^ z;yK(KL*wJOm!0vsit&zE=8qXMX#Vor`6ZSQ?l9jZq-ASsEFA+JH@3R9lTbE7!nh(= zSK(h`SdHNXC(+%9h*5Skcc`xsUG{x^Jd8XJmIY}v$%6P*QF7Tpo~}%QvFei;^o~90 z7qK8?#uBhKJ8)*4;H#Ju|MZxV#>W(44e6~HKj_4aT0(vNe4)Fw#B=AK*P|I}+v*ht zf;ulD2PGIOuCfMJaP@~sho3|SE*}0XuX!g}~o;HEk(6obqooyArg0%96U$+O>PQzMQB0Zc^PiE7Eoq7q$}! zuywBv$0ZbIo>>xKhf|Bsc5U+=uAS+8nH$j7DafgMdXk$b2T8k}`Q3PMGxp6XeX`r- zU%x)hou)vsI__2kPVj}+-R!L1DlvwJ6uvEtv_E0|n%iX;$`%)9!IE&N@EexXjar>H zdsXOzTjm0sDwAeE;fHj>32>}n66$15`WKN_#@`(Jy_569*Fa=6KIRB_TZCxlPSwVq z6}%{dFS`=FKrQ^95(q=rH`X{9#9PP2b?K1fXGs;R+Al8qnd=;5UeAkX5WhGz=@`iX zI_&W%ICQ^B+|ED<@LcrX62yMhVWBGUFu%1d=hV^>0{i7(ZJ0a=2@#)VIL@*8=YG*C zZGLuv(};phd2I&v8WMX5Awjz<=NJ7|c=mfvDc9)z_Cwd^H6{Z@e3Xl9P2M6KaJb)f zNT5uJ3BjWe=AxApDSN6Du15K{hY|c?C>c*5pkFrNDW=fYD}otZFZ&+R9;3Mx-?DdL z&lyZiX03IU2!Be7GZ&Fhb+T;9p}-r?uNp81BChye&?iO6%;i!*NgF zi54q-mDT>Er$33j*XpyW*K6c!uLZnZ3RGQ=E1XIMqi?rhg%l2h&lw;FZoM?lA)XV- zJl_8Zy~ILGV)sBT4@zL-tGH?R7pE@&5O0z+FR7DKi#raMWUgKff40yS{5V<1M*L#M zx=~0Xwx;gD^U};_T*E$8Dp(BKqu&4VE{X;3rFF7tRrbZ|>3X52C4HpHFmMaYe~C>}jTf`$Vl^&&=LyD< zjlsLmU71Q$?StS6YPfnRjrVoBvYl$ux$*R~)N>IE$)ttA8AEQq}cC!=GeK=!hH0m=y4Qdo*=<=VDlZpF~LJgg>D^xUI z-dMv#p0~kfFLThekgTzMoU!Y-Q(mloNTpUZ8hRxrR)@9}=-{-d)zrDyL`!)bW7Bv< zJSUCf{$}*AV0=rq2N1WCH3- zbD%V!#1_4O9HP`_8V(JhmaO06pvi80ks{U+NK-c#q@lb0n8-Q9ss}K9Bh#RSqtaad z88-E%IMfu4t^g-uOaf}p*bB(+xRr(Z)3ZU7<7(@U*)+*xT3JFh~a zR&^sTQ=@fxNwp)7yfj7-vO(Cgf*;ZXN~YUk;->zM$5c(ng{$^=P(XR@NUW+`jEL-C z?FN&7jaQtp%^vekEAHWn@Hgbw#18*#4}!0j*J7WZe(RELiY&KPtbQe7#ZlL6KmJgJ zE-|v0)~nlWt7Ke?k_(4T2v4I!Tz&mLL(EUuQ=0KlPB2RB*|Xfst+HlLP90 zVi5MGE+WtFYFmsd={7U@mKuIz&AF$`<>z6ds=!~#MPV_y7p8RI3-e7dBFE14&K=6x z*kJi(D#u^NmD{h0fo;>YP6t3p{vddBFsx8& z?EPq1*bs~ch2qqwh=EZ0NQ!5{+;xR+;EiPF6*lK|XBd|2Sa#RbS{Dep?w;^ST6aQ8 z|8{hWsXW=l#XbWgM49;9dM(ulc;735V7fc>a|YwD4V{R3fm3nR%dCEn8IaPG1#LU@ zC*k;KsHwHaKe%F7Tpn6abJKe5=Csn0v&k@WI+nW_t1rD57Juq8Y$AWP&|fLj{!jua zZA~+w6pLhzujY-+F_TpohPZqOH#C8VB{wu=97dpIun#lX!!z)#M>B`V`DYm>NcT+? zLxWW@V)KNW*MNcpL$I6AP@t1wd16QLMx6G@$H0uh1Gvz}8jHpzjGfbi%9eU1!F+m1Nyv4~p*M@;RinHH35}xMOWT4}>&B_x} z@_Du_UU(rl;1cb`C;_#a01J!8GpUM4irghE{By6HuHvk?xngWMeAU#(RVD?U79e4q zkZ&o`{wZ^aDQN#JA2~?L~sG=sS;Q1X(5%cWQYKCrI z6iPNo(#%#-!0Y#z7A{1dVrME5TI&q9E4NBOe>}}c_X_LW(TNwvU9Yrd@CZ0Xx%Q*V z$*>yg8+OWXpXUCt$}-%G#ij8|>KuQrb)**?|MF(BB3j9zwTv+fEomhZcqPl$>!DF7 z+6TaD>xk!b66+?EE9ITa?v5dRdG6!4L?a1>H*T2ib>FOY(u9(~P;khwb5a6I*AG89 zBPSo#?LEG4o4FemeYK6-w9|M&J$_KmRH=O#Ht{}R%|;g`Zzy3}JHZCWHl=R%w|SQp zieFmFbLXSkO|H!SNGFYmq|#8(U7RN^TU(YO2f$V1=-lHtHgJ8))80hxg(T*Ybi%bk zER0?XBSbC~RHn~)uoC8Zfi9Ji4zT_@SF+#hC4Yk1jdpdRzUG}@{0)9x%!Cv~mlCHa z5^vg_kKS|v_VSq&BNIm1BZnXa9_JTJ`XarN5VT~IDDAxtf3M}?K05%P=a`#s@~(hm zq2X`O=!}o+1;HaR;3bg?PG`|wqO0M{1=1v<(^w-;7;(Ew6Npj;6S$~799TqF6Vr;| z!O*Y$E5q4Pa!^CR$7PLL%88*+H}m+L6GI#a=6m1Q{U`lhwpK-*k=oaV@{+%M^jR@w zN&B-BBn2FjKKif()DuoyxwXNCYRS4X1psm;8cQ(C4^gusPYTzqGbaOr; zxi9!)Y=yrkEkJ+xQs2$KlD+r)N9W6lFd*FGvhZk6Bry#2$nSlgzAN2TI9=ixV0AdD zPaa8Q0X@g^LX06~=$+#LClj?d(uW!J8jxO&Tg7X1%v{|lgb*O$I=0P`5#{0!^#s0h zVWJx^%Cdls@Cn}i^Uz%edXLb^gdjB&38SZCa&6AS=+u7*La_EkU7cBe#S+NG#p$81 zLw})36;p!XC8Dlw-075fgS3LXOWhZM#^d0Ok%+s(%^!Z8u_$Ix8o@4I>WF}#MUsA4 z5OqaqHCKdU)&*!A*VA9-y^TJ25r6ELKs2knTo2cJi`WDUzVyT5*4BlW;iHn}#mod5 zfYQ+wwgK|c=>}ep_ehIJj(ki>MfhUyg@DtZSnIpLif;*OX&}+O1d6^?GHh| zb000Yo-hB_x;+f<3}y$H6?j_UO-J!&4X4in>)Y=d9)LcuhPQOwH|plmp3qS~igOp- zjFBQWkFp^YuWO(M64nCrbs(qcF|QSpLjaLE7Wdj5kZ`wv!# z8*qpFp26s4?~g*LVo>@+Nk{0eT0#9#ZTd2fV6~#(Wc73M0z!ldy8BxFx~rf^B@*Aa z+h!W_vQbzL| z3OJ)vv2pZ#7L@E~GyhMLGxax3_iw7~pA6-1aD=YAOoK$|z-Jh|sIO|KSi2iX~bsy Q531Pog5~)N1CP7^1>(V7QUCw| literal 0 Hc-jL100001 -- 2.47.3