// Cyclic garbage collector implementation for free-threaded build. #include "Python.h" #include "pycore_ceval.h" // _Py_set_eval_breaker_bit() #include "pycore_context.h" #include "pycore_dict.h" // _PyDict_MaybeUntrack() #include "pycore_initconfig.h" #include "pycore_interp.h" // PyInterpreterState.gc #include "pycore_object.h" #include "pycore_object_alloc.h" // _PyObject_MallocWithType() #include "pycore_object_stack.h" #include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_tstate.h" // _PyThreadStateImpl #include "pycore_weakref.h" // _PyWeakref_ClearRef() #include "pydtrace.h" #ifdef Py_GIL_DISABLED typedef struct _gc_runtime_state GCState; #ifdef Py_DEBUG # define GC_DEBUG #endif // Automatically choose the generation that needs collecting. #define GENERATION_AUTO (-1) // A linked-list of objects using the `ob_tid` field as the next pointer. struct worklist { uintptr_t head; }; struct worklist_iter { uintptr_t *ptr; // pointer to current object uintptr_t *next; // next value of ptr }; struct visitor_args { size_t offset; // offset of PyObject from start of block }; // Per-collection state struct collection_state { struct visitor_args base; PyInterpreterState *interp; GCState *gcstate; Py_ssize_t collected; Py_ssize_t uncollectable; struct worklist unreachable; struct worklist legacy_finalizers; struct worklist wrcb_to_call; struct worklist objs_to_decref; }; // iterate over a worklist #define WORKSTACK_FOR_EACH(stack, op) \ for ((op) = (PyObject *)(stack)->head; (op) != NULL; (op) = (PyObject *)(op)->ob_tid) // iterate over a worklist with support for removing the current object #define WORKSTACK_FOR_EACH_ITER(stack, iter, op) \ for (worklist_iter_init((iter), &(stack)->head), (op) = (PyObject *)(*(iter)->ptr); \ (op) != NULL; \ worklist_iter_init((iter), (iter)->next), (op) = (PyObject *)(*(iter)->ptr)) static void worklist_push(struct worklist *worklist, PyObject *op) { assert(op->ob_tid == 0); op->ob_tid = worklist->head; worklist->head = (uintptr_t)op; } static PyObject * worklist_pop(struct worklist *worklist) { PyObject *op = (PyObject *)worklist->head; if (op != NULL) { worklist->head = op->ob_tid; op->ob_tid = 0; } return op; } static void worklist_iter_init(struct worklist_iter *iter, uintptr_t *next) { iter->ptr = next; PyObject *op = (PyObject *)*(iter->ptr); if (op) { iter->next = &op->ob_tid; } } static void worklist_remove(struct worklist_iter *iter) { PyObject *op = (PyObject *)*(iter->ptr); *(iter->ptr) = op->ob_tid; op->ob_tid = 0; iter->next = iter->ptr; } static inline int gc_is_unreachable(PyObject *op) { return (op->ob_gc_bits & _PyGC_BITS_UNREACHABLE) != 0; } static void gc_set_unreachable(PyObject *op) { op->ob_gc_bits |= _PyGC_BITS_UNREACHABLE; } static void gc_clear_unreachable(PyObject *op) { op->ob_gc_bits &= ~_PyGC_BITS_UNREACHABLE; } // Initialize the `ob_tid` field to zero if the object is not already // initialized as unreachable. static void gc_maybe_init_refs(PyObject *op) { if (!gc_is_unreachable(op)) { gc_set_unreachable(op); op->ob_tid = 0; } } static inline Py_ssize_t gc_get_refs(PyObject *op) { return (Py_ssize_t)op->ob_tid; } static inline void gc_add_refs(PyObject *op, Py_ssize_t refs) { assert(_PyObject_GC_IS_TRACKED(op)); op->ob_tid += refs; } static inline void gc_decref(PyObject *op) { op->ob_tid -= 1; } // Merge refcounts while the world is stopped. static void merge_refcount(PyObject *op, Py_ssize_t extra) { assert(_PyInterpreterState_GET()->stoptheworld.world_stopped); Py_ssize_t refcount = Py_REFCNT(op); refcount += extra; #ifdef Py_REF_DEBUG _Py_AddRefTotal(_PyInterpreterState_GET(), extra); #endif // No atomics necessary; all other threads in this interpreter are paused. op->ob_tid = 0; op->ob_ref_local = 0; op->ob_ref_shared = _Py_REF_SHARED(refcount, _Py_REF_MERGED); } static void gc_restore_tid(PyObject *op) { mi_segment_t *segment = _mi_ptr_segment(op); if (_Py_REF_IS_MERGED(op->ob_ref_shared)) { op->ob_tid = 0; } else { // NOTE: may change ob_tid if the object was re-initialized by // a different thread or its segment was abandoned and reclaimed. // The segment thread id might be zero, in which case we should // ensure the refcounts are now merged. op->ob_tid = segment->thread_id; if (op->ob_tid == 0) { merge_refcount(op, 0); } } } static void gc_restore_refs(PyObject *op) { if (gc_is_unreachable(op)) { gc_restore_tid(op); gc_clear_unreachable(op); } } // Given a mimalloc memory block return the PyObject stored in it or NULL if // the block is not allocated or the object is not tracked or is immortal. static PyObject * op_from_block(void *block, void *arg, bool include_frozen) { struct visitor_args *a = arg; if (block == NULL) { return NULL; } PyObject *op = (PyObject *)((char*)block + a->offset); assert(PyObject_IS_GC(op)); if (!_PyObject_GC_IS_TRACKED(op)) { return NULL; } if (!include_frozen && (op->ob_gc_bits & _PyGC_BITS_FROZEN) != 0) { return NULL; } return op; } static int gc_visit_heaps_lock_held(PyInterpreterState *interp, mi_block_visit_fun *visitor, struct visitor_args *arg) { // Offset of PyObject header from start of memory block. Py_ssize_t offset_base = sizeof(PyGC_Head); if (_PyMem_DebugEnabled()) { // The debug allocator adds two words at the beginning of each block. offset_base += 2 * sizeof(size_t); } // Objects with Py_TPFLAGS_PREHEADER have two extra fields Py_ssize_t offset_pre = offset_base + 2 * sizeof(PyObject*); // visit each thread's heaps for GC objects for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { struct _mimalloc_thread_state *m = &((_PyThreadStateImpl *)p)->mimalloc; arg->offset = offset_base; if (!mi_heap_visit_blocks(&m->heaps[_Py_MIMALLOC_HEAP_GC], true, visitor, arg)) { return -1; } arg->offset = offset_pre; if (!mi_heap_visit_blocks(&m->heaps[_Py_MIMALLOC_HEAP_GC_PRE], true, visitor, arg)) { return -1; } } // visit blocks in the per-interpreter abandoned pool (from dead threads) mi_abandoned_pool_t *pool = &interp->mimalloc.abandoned_pool; arg->offset = offset_base; if (!_mi_abandoned_pool_visit_blocks(pool, _Py_MIMALLOC_HEAP_GC, true, visitor, arg)) { return -1; } arg->offset = offset_pre; if (!_mi_abandoned_pool_visit_blocks(pool, _Py_MIMALLOC_HEAP_GC_PRE, true, visitor, arg)) { return -1; } return 0; } // Visits all GC objects in the interpreter's heaps. // NOTE: It is not safe to allocate or free any mimalloc managed memory while // this function is running. static int gc_visit_heaps(PyInterpreterState *interp, mi_block_visit_fun *visitor, struct visitor_args *arg) { // Other threads in the interpreter must be paused so that we can safely // traverse their heaps. assert(interp->stoptheworld.world_stopped); int err; HEAD_LOCK(&_PyRuntime); err = gc_visit_heaps_lock_held(interp, visitor, arg); HEAD_UNLOCK(&_PyRuntime); return err; } // Subtract an incoming reference from the computed "gc_refs" refcount. static int visit_decref(PyObject *op, void *arg) { if (_PyObject_GC_IS_TRACKED(op) && !_Py_IsImmortal(op)) { // If update_refs hasn't reached this object yet, mark it // as (tentatively) unreachable and initialize ob_tid to zero. gc_maybe_init_refs(op); gc_decref(op); } return 0; } // Compute the number of external references to objects in the heap // by subtracting internal references from the refcount. The difference is // computed in the ob_tid field (we restore it later). static bool update_refs(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, false); if (op == NULL) { return true; } // Exclude immortal objects from garbage collection if (_Py_IsImmortal(op)) { op->ob_tid = 0; _PyObject_GC_UNTRACK(op); gc_clear_unreachable(op); return true; } // Untrack tuples and dicts as necessary in this pass. if (PyTuple_CheckExact(op)) { _PyTuple_MaybeUntrack(op); if (!_PyObject_GC_IS_TRACKED(op)) { gc_restore_refs(op); return true; } } else if (PyDict_CheckExact(op)) { _PyDict_MaybeUntrack(op); if (!_PyObject_GC_IS_TRACKED(op)) { gc_restore_refs(op); return true; } } Py_ssize_t refcount = Py_REFCNT(op); _PyObject_ASSERT(op, refcount >= 0); // Add the actual refcount to ob_tid. gc_maybe_init_refs(op); gc_add_refs(op, refcount); // Subtract internal references from ob_tid. Objects with ob_tid > 0 // are directly reachable from outside containers, and so can't be // collected. Py_TYPE(op)->tp_traverse(op, visit_decref, NULL); return true; } static int visit_clear_unreachable(PyObject *op, _PyObjectStack *stack) { if (gc_is_unreachable(op)) { _PyObject_ASSERT(op, _PyObject_GC_IS_TRACKED(op)); gc_clear_unreachable(op); return _PyObjectStack_Push(stack, op); } return 0; } // Transitively clear the unreachable bit on all objects reachable from op. static int mark_reachable(PyObject *op) { _PyObjectStack stack = { NULL }; do { traverseproc traverse = Py_TYPE(op)->tp_traverse; if (traverse(op, (visitproc)&visit_clear_unreachable, &stack) < 0) { _PyObjectStack_Clear(&stack); return -1; } op = _PyObjectStack_Pop(&stack); } while (op != NULL); return 0; } #ifdef GC_DEBUG static bool validate_gc_objects(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, false); if (op == NULL) { return true; } _PyObject_ASSERT(op, gc_is_unreachable(op)); _PyObject_ASSERT_WITH_MSG(op, gc_get_refs(op) >= 0, "refcount is too small"); return true; } #endif static bool mark_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, false); if (op == NULL) { return true; } if (gc_is_unreachable(op) && gc_get_refs(op) != 0) { // Object is reachable but currently marked as unreachable. // Mark it as reachable and traverse its pointers to find // any other object that may be directly reachable from it. gc_clear_unreachable(op); // Transitively mark reachable objects by clearing the unreachable flag. if (mark_reachable(op) < 0) { return false; } } return true; } /* Return true if object has a pre-PEP 442 finalization method. */ static int has_legacy_finalizer(PyObject *op) { return Py_TYPE(op)->tp_del != NULL; } static bool scan_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, false); if (op == NULL) { return true; } struct collection_state *state = (struct collection_state *)args; if (gc_is_unreachable(op)) { // Merge and add one to the refcount to prevent deallocation while we // are holding on to it in a worklist. merge_refcount(op, 1); if (has_legacy_finalizer(op)) { // would be unreachable, but has legacy finalizer gc_clear_unreachable(op); worklist_push(&state->legacy_finalizers, op); } else { worklist_push(&state->unreachable, op); } } else { // object is reachable, restore `ob_tid`; we're done with these objects gc_restore_tid(op); state->gcstate->long_lived_total++; } return true; } static int move_legacy_finalizer_reachable(struct collection_state *state); static int deduce_unreachable_heap(PyInterpreterState *interp, struct collection_state *state) { // Identify objects that are directly reachable from outside the GC heap // by computing the difference between the refcount and the number of // incoming references. gc_visit_heaps(interp, &update_refs, &state->base); #ifdef GC_DEBUG // Check that all objects are marked as unreachable and that the computed // reference count difference (stored in `ob_tid`) is non-negative. gc_visit_heaps(interp, &validate_gc_objects, &state->base); #endif // Transitively mark reachable objects by clearing the // _PyGC_BITS_UNREACHABLE flag. if (gc_visit_heaps(interp, &mark_heap_visitor, &state->base) < 0) { return -1; } // Identify remaining unreachable objects and push them onto a stack. // Restores ob_tid for reachable objects. gc_visit_heaps(interp, &scan_heap_visitor, &state->base); if (state->legacy_finalizers.head) { // There may be objects reachable from legacy finalizers that are in // the unreachable set. We need to mark them as reachable. if (move_legacy_finalizer_reachable(state) < 0) { return -1; } } return 0; } static int move_legacy_finalizer_reachable(struct collection_state *state) { // Clear the reachable bit on all objects transitively reachable // from the objects with legacy finalizers. PyObject *op; WORKSTACK_FOR_EACH(&state->legacy_finalizers, op) { if (mark_reachable(op) < 0) { return -1; } } // Move the reachable objects from the unreachable worklist to the legacy // finalizer worklist. struct worklist_iter iter; WORKSTACK_FOR_EACH_ITER(&state->unreachable, &iter, op) { if (!gc_is_unreachable(op)) { worklist_remove(&iter); worklist_push(&state->legacy_finalizers, op); } } return 0; } // Clear all weakrefs to unreachable objects. Weakrefs with callbacks are // enqueued in `wrcb_to_call`, but not invoked yet. static void clear_weakrefs(struct collection_state *state) { PyObject *op; WORKSTACK_FOR_EACH(&state->unreachable, op) { if (PyWeakref_Check(op)) { // Clear weakrefs that are themselves unreachable to ensure their // callbacks will not be executed later from a `tp_clear()` // inside delete_garbage(). That would be unsafe: it could // resurrect a dead object or access a an already cleared object. // See bpo-38006 for one example. _PyWeakref_ClearRef((PyWeakReference *)op); } if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(op))) { continue; } // NOTE: This is never triggered for static types so we can avoid the // (slightly) more costly _PyObject_GET_WEAKREFS_LISTPTR(). PyWeakReference **wrlist = _PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(op); // `op` may have some weakrefs. March over the list, clear // all the weakrefs, and enqueue the weakrefs with callbacks // that must be called into wrcb_to_call. for (PyWeakReference *wr = *wrlist; wr != NULL; wr = *wrlist) { // _PyWeakref_ClearRef clears the weakref but leaves // the callback pointer intact. Obscure: it also // changes *wrlist. _PyObject_ASSERT((PyObject *)wr, wr->wr_object == op); _PyWeakref_ClearRef(wr); _PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None); // We do not invoke callbacks for weakrefs that are themselves // unreachable. This is partly for historical reasons: weakrefs // predate safe object finalization, and a weakref that is itself // unreachable may have a callback that resurrects other // unreachable objects. if (wr->wr_callback == NULL || gc_is_unreachable((PyObject *)wr)) { continue; } // Create a new reference so that wr can't go away before we can // process it again. merge_refcount((PyObject *)wr, 1); // Enqueue weakref to be called later. worklist_push(&state->wrcb_to_call, (PyObject *)wr); } } } static void call_weakref_callbacks(struct collection_state *state) { // Invoke the callbacks we decided to honor. PyObject *op; while ((op = worklist_pop(&state->wrcb_to_call)) != NULL) { _PyObject_ASSERT(op, PyWeakref_Check(op)); PyWeakReference *wr = (PyWeakReference *)op; PyObject *callback = wr->wr_callback; _PyObject_ASSERT(op, callback != NULL); /* copy-paste of weakrefobject.c's handle_callback() */ PyObject *temp = PyObject_CallOneArg(callback, (PyObject *)wr); if (temp == NULL) { PyErr_WriteUnraisable(callback); } else { Py_DECREF(temp); } gc_restore_tid(op); Py_DECREF(op); // drop worklist reference } } static GCState * get_gc_state(void) { PyInterpreterState *interp = _PyInterpreterState_GET(); return &interp->gc; } void _PyGC_InitState(GCState *gcstate) { } PyStatus _PyGC_Init(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; gcstate->garbage = PyList_New(0); if (gcstate->garbage == NULL) { return _PyStatus_NO_MEMORY(); } gcstate->callbacks = PyList_New(0); if (gcstate->callbacks == NULL) { return _PyStatus_NO_MEMORY(); } return _PyStatus_OK(); } static void debug_cycle(const char *msg, PyObject *op) { PySys_FormatStderr("gc: %s <%s %p>\n", msg, Py_TYPE(op)->tp_name, op); } /* Run first-time finalizers (if any) on all the objects in collectable. * Note that this may remove some (or even all) of the objects from the * list, due to refcounts falling to 0. */ static void finalize_garbage(struct collection_state *state) { // NOTE: the unreachable worklist holds a strong reference to the object // to prevent it from being deallocated while we are holding on to it. PyObject *op; WORKSTACK_FOR_EACH(&state->unreachable, op) { if (!_PyGC_FINALIZED(op)) { destructor finalize = Py_TYPE(op)->tp_finalize; if (finalize != NULL) { _PyGC_SET_FINALIZED(op); finalize(op); assert(!_PyErr_Occurred(_PyThreadState_GET())); } } } } // Break reference cycles by clearing the containers involved. static void delete_garbage(struct collection_state *state) { PyThreadState *tstate = _PyThreadState_GET(); GCState *gcstate = state->gcstate; assert(!_PyErr_Occurred(tstate)); PyObject *op; while ((op = worklist_pop(&state->objs_to_decref)) != NULL) { Py_DECREF(op); } while ((op = worklist_pop(&state->unreachable)) != NULL) { _PyObject_ASSERT(op, gc_is_unreachable(op)); // Clear the unreachable flag. gc_clear_unreachable(op); if (!_PyObject_GC_IS_TRACKED(op)) { // Object might have been untracked by some other tp_clear() call. Py_DECREF(op); // drop the reference from the worklist continue; } state->collected++; if (gcstate->debug & _PyGC_DEBUG_SAVEALL) { assert(gcstate->garbage != NULL); if (PyList_Append(gcstate->garbage, op) < 0) { _PyErr_Clear(tstate); } } else { inquiry clear = Py_TYPE(op)->tp_clear; if (clear != NULL) { (void) clear(op); if (_PyErr_Occurred(tstate)) { PyErr_FormatUnraisable("Exception ignored in tp_clear of %s", Py_TYPE(op)->tp_name); } } } Py_DECREF(op); // drop the reference from the worklist } } static void handle_legacy_finalizers(struct collection_state *state) { GCState *gcstate = state->gcstate; assert(gcstate->garbage != NULL); PyObject *op; while ((op = worklist_pop(&state->legacy_finalizers)) != NULL) { state->uncollectable++; if (gcstate->debug & _PyGC_DEBUG_UNCOLLECTABLE) { debug_cycle("uncollectable", op); } if ((gcstate->debug & _PyGC_DEBUG_SAVEALL) || has_legacy_finalizer(op)) { if (PyList_Append(gcstate->garbage, op) < 0) { PyErr_Clear(); } } Py_DECREF(op); // drop worklist reference } } // Show stats for objects in each generations static void show_stats_each_generations(GCState *gcstate) { // TODO } // Traversal callback for handle_resurrected_objects. static int visit_decref_unreachable(PyObject *op, void *data) { if (gc_is_unreachable(op) && _PyObject_GC_IS_TRACKED(op)) { op->ob_ref_local -= 1; } return 0; } // Handle objects that may have resurrected after a call to 'finalize_garbage'. static int handle_resurrected_objects(struct collection_state *state) { // First, find externally reachable objects by computing the reference // count difference in ob_ref_local. We can't use ob_tid here because // that's already used to store the unreachable worklist. PyObject *op; struct worklist_iter iter; WORKSTACK_FOR_EACH_ITER(&state->unreachable, &iter, op) { assert(gc_is_unreachable(op)); assert(_Py_REF_IS_MERGED(op->ob_ref_shared)); if (!_PyObject_GC_IS_TRACKED(op)) { // Object was untracked by a finalizer. Schedule it for a Py_DECREF // after we finish with the stop-the-world pause. gc_clear_unreachable(op); worklist_remove(&iter); worklist_push(&state->objs_to_decref, op); continue; } Py_ssize_t refcount = (op->ob_ref_shared >> _Py_REF_SHARED_SHIFT); if (refcount > INT32_MAX) { // The refcount is too big to fit in `ob_ref_local`. Mark the // object as immortal and bail out. gc_clear_unreachable(op); worklist_remove(&iter); _Py_SetImmortal(op); continue; } op->ob_ref_local += (uint32_t)refcount; // Subtract one to account for the reference from the worklist. op->ob_ref_local -= 1; traverseproc traverse = Py_TYPE(op)->tp_traverse; (void) traverse(op, (visitproc)visit_decref_unreachable, NULL); } // Find resurrected objects bool any_resurrected = false; WORKSTACK_FOR_EACH(&state->unreachable, op) { int32_t gc_refs = (int32_t)op->ob_ref_local; op->ob_ref_local = 0; // restore ob_ref_local _PyObject_ASSERT(op, gc_refs >= 0); if (gc_is_unreachable(op) && gc_refs > 0) { // Clear the unreachable flag on any transitively reachable objects // from this one. any_resurrected = true; gc_clear_unreachable(op); if (mark_reachable(op) < 0) { return -1; } } } if (any_resurrected) { // Remove resurrected objects from the unreachable list. WORKSTACK_FOR_EACH_ITER(&state->unreachable, &iter, op) { if (!gc_is_unreachable(op)) { _PyObject_ASSERT(op, Py_REFCNT(op) > 1); worklist_remove(&iter); merge_refcount(op, -1); // remove worklist reference } } } #ifdef GC_DEBUG WORKSTACK_FOR_EACH(&state->unreachable, op) { _PyObject_ASSERT(op, gc_is_unreachable(op)); _PyObject_ASSERT(op, _PyObject_GC_IS_TRACKED(op)); _PyObject_ASSERT(op, op->ob_ref_local == 0); _PyObject_ASSERT(op, _Py_REF_IS_MERGED(op->ob_ref_shared)); } #endif return 0; } /* Invoke progress callbacks to notify clients that garbage collection * is starting or stopping */ static void invoke_gc_callback(PyThreadState *tstate, const char *phase, int generation, Py_ssize_t collected, Py_ssize_t uncollectable) { assert(!_PyErr_Occurred(tstate)); /* we may get called very early */ GCState *gcstate = &tstate->interp->gc; if (gcstate->callbacks == NULL) { return; } /* The local variable cannot be rebound, check it for sanity */ assert(PyList_CheckExact(gcstate->callbacks)); PyObject *info = NULL; if (PyList_GET_SIZE(gcstate->callbacks) != 0) { info = Py_BuildValue("{sisnsn}", "generation", generation, "collected", collected, "uncollectable", uncollectable); if (info == NULL) { PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); return; } } PyObject *phase_obj = PyUnicode_FromString(phase); if (phase_obj == NULL) { Py_XDECREF(info); PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks"); return; } PyObject *stack[] = {phase_obj, info}; for (Py_ssize_t i=0; icallbacks); i++) { PyObject *r, *cb = PyList_GET_ITEM(gcstate->callbacks, i); Py_INCREF(cb); /* make sure cb doesn't go away */ r = PyObject_Vectorcall(cb, stack, 2, NULL); if (r == NULL) { PyErr_WriteUnraisable(cb); } else { Py_DECREF(r); } Py_DECREF(cb); } Py_DECREF(phase_obj); Py_XDECREF(info); assert(!_PyErr_Occurred(tstate)); } /* Find the oldest generation (highest numbered) where the count * exceeds the threshold. Objects in the that generation and * generations younger than it will be collected. */ static int gc_select_generation(GCState *gcstate) { for (int i = NUM_GENERATIONS-1; i >= 0; i--) { if (gcstate->generations[i].count > gcstate->generations[i].threshold) { /* Avoid quadratic performance degradation in number of tracked objects (see also issue #4074): To limit the cost of garbage collection, there are two strategies; - make each collection faster, e.g. by scanning fewer objects - do less collections This heuristic is about the latter strategy. In addition to the various configurable thresholds, we only trigger a full collection 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 (i.e., 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 in creating and storing lots of long-lived objects (e.g. building a large list of GC-tracked objects would show quadratic performance, instead of linear as expected: see issue #4074). 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"). This heuristic was suggested by Martin von Löwis on python-dev in June 2008. His original analysis and proposal can be found at: http://mail.python.org/pipermail/python-dev/2008-June/080579.html */ if (i == NUM_GENERATIONS - 1 && gcstate->long_lived_pending < gcstate->long_lived_total / 4) { continue; } return i; } } return -1; } static void cleanup_worklist(struct worklist *worklist) { PyObject *op; while ((op = worklist_pop(worklist)) != NULL) { gc_restore_tid(op); gc_clear_unreachable(op); Py_DECREF(op); } } static void gc_collect_internal(PyInterpreterState *interp, struct collection_state *state) { _PyEval_StopTheWorld(interp); // Find unreachable objects int err = deduce_unreachable_heap(interp, state); if (err < 0) { _PyEval_StartTheWorld(interp); goto error; } // Print debugging information. if (interp->gc.debug & _PyGC_DEBUG_COLLECTABLE) { PyObject *op; WORKSTACK_FOR_EACH(&state->unreachable, op) { debug_cycle("collectable", op); } } // Clear weakrefs and enqueue callbacks (but do not call them). clear_weakrefs(state); _PyEval_StartTheWorld(interp); // Call weakref callbacks and finalizers after unpausing other threads to // avoid potential deadlocks. call_weakref_callbacks(state); finalize_garbage(state); // Handle any objects that may have resurrected after the finalization. _PyEval_StopTheWorld(interp); err = handle_resurrected_objects(state); _PyEval_StartTheWorld(interp); if (err < 0) { goto error; } // Call tp_clear on objects in the unreachable set. This will cause // the reference cycles to be broken. It may also cause some objects // to be freed. delete_garbage(state); // Append objects with legacy finalizers to the "gc.garbage" list. handle_legacy_finalizers(state); return; error: cleanup_worklist(&state->unreachable); cleanup_worklist(&state->legacy_finalizers); cleanup_worklist(&state->wrcb_to_call); cleanup_worklist(&state->objs_to_decref); PyErr_NoMemory(); PyErr_FormatUnraisable("Out of memory during garbage collection"); } /* This is the main function. Read this to understand how the * collection process works. */ static Py_ssize_t gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) { int i; Py_ssize_t m = 0; /* # objects collected */ Py_ssize_t n = 0; /* # unreachable objects that couldn't be collected */ _PyTime_t t1 = 0; /* initialize to prevent a compiler warning */ GCState *gcstate = &tstate->interp->gc; // gc_collect_main() must not be called before _PyGC_Init // or after _PyGC_Fini() assert(gcstate->garbage != NULL); assert(!_PyErr_Occurred(tstate)); int expected = 0; if (!_Py_atomic_compare_exchange_int(&gcstate->collecting, &expected, 1)) { // Don't start a garbage collection if one is already in progress. return 0; } if (generation == GENERATION_AUTO) { // Select the oldest generation that needs collecting. We will collect // objects from that generation and all generations younger than it. generation = gc_select_generation(gcstate); if (generation < 0) { // No generation needs to be collected. _Py_atomic_store_int(&gcstate->collecting, 0); return 0; } } assert(generation >= 0 && generation < NUM_GENERATIONS); #ifdef Py_STATS if (_Py_stats) { _Py_stats->object_stats.object_visits = 0; } #endif GC_STAT_ADD(generation, collections, 1); if (reason != _Py_GC_REASON_SHUTDOWN) { invoke_gc_callback(tstate, "start", generation, 0, 0); } if (gcstate->debug & _PyGC_DEBUG_STATS) { PySys_WriteStderr("gc: collecting generation %d...\n", generation); show_stats_each_generations(gcstate); t1 = _PyTime_GetPerfCounter(); } if (PyDTrace_GC_START_ENABLED()) { PyDTrace_GC_START(generation); } /* update collection and allocation counters */ if (generation+1 < NUM_GENERATIONS) { gcstate->generations[generation+1].count += 1; } for (i = 0; i <= generation; i++) { gcstate->generations[i].count = 0; } PyInterpreterState *interp = tstate->interp; struct collection_state state = { .interp = interp, .gcstate = gcstate, }; gc_collect_internal(interp, &state); m = state.collected; n = state.uncollectable; if (gcstate->debug & _PyGC_DEBUG_STATS) { double d = _PyTime_AsSecondsDouble(_PyTime_GetPerfCounter() - t1); PySys_WriteStderr( "gc: done, %zd unreachable, %zd uncollectable, %.4fs elapsed\n", n+m, n, d); } // Clear free lists in all threads _PyGC_ClearAllFreeLists(interp); if (_PyErr_Occurred(tstate)) { if (reason == _Py_GC_REASON_SHUTDOWN) { _PyErr_Clear(tstate); } else { PyErr_FormatUnraisable("Exception ignored in garbage collection"); } } /* Update stats */ struct gc_generation_stats *stats = &gcstate->generation_stats[generation]; stats->collections++; stats->collected += m; stats->uncollectable += n; GC_STAT_ADD(generation, objects_collected, m); #ifdef Py_STATS if (_Py_stats) { GC_STAT_ADD(generation, object_visits, _Py_stats->object_stats.object_visits); _Py_stats->object_stats.object_visits = 0; } #endif if (PyDTrace_GC_DONE_ENABLED()) { PyDTrace_GC_DONE(n + m); } if (reason != _Py_GC_REASON_SHUTDOWN) { invoke_gc_callback(tstate, "stop", generation, m, n); } assert(!_PyErr_Occurred(tstate)); _Py_atomic_store_int(&gcstate->collecting, 0); return n + m; } struct get_referrers_args { struct visitor_args base; PyObject *objs; struct worklist results; }; static int referrersvisit(PyObject* obj, void *arg) { PyObject *objs = arg; Py_ssize_t i; for (i = 0; i < PyTuple_GET_SIZE(objs); i++) { if (PyTuple_GET_ITEM(objs, i) == obj) { return 1; } } return 0; } static bool visit_get_referrers(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, true); if (op == NULL) { return true; } struct get_referrers_args *arg = (struct get_referrers_args *)args; if (Py_TYPE(op)->tp_traverse(op, referrersvisit, arg->objs)) { op->ob_tid = 0; // we will restore the refcount later worklist_push(&arg->results, op); } return true; } PyObject * _PyGC_GetReferrers(PyInterpreterState *interp, PyObject *objs) { PyObject *result = PyList_New(0); if (!result) { return NULL; } _PyEval_StopTheWorld(interp); // Append all objects to a worklist. This abuses ob_tid. We will restore // it later. NOTE: We can't append to the PyListObject during // gc_visit_heaps() because PyList_Append() may reclaim an abandoned // mimalloc segments while we are traversing them. struct get_referrers_args args = { .objs = objs }; gc_visit_heaps(interp, &visit_get_referrers, &args.base); bool error = false; PyObject *op; while ((op = worklist_pop(&args.results)) != NULL) { gc_restore_tid(op); if (op != objs && PyList_Append(result, op) < 0) { error = true; break; } } // In case of error, clear the remaining worklist while ((op = worklist_pop(&args.results)) != NULL) { gc_restore_tid(op); } _PyEval_StartTheWorld(interp); if (error) { Py_DECREF(result); return NULL; } return result; } struct get_objects_args { struct visitor_args base; struct worklist objects; }; static bool visit_get_objects(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, true); if (op == NULL) { return true; } struct get_objects_args *arg = (struct get_objects_args *)args; op->ob_tid = 0; // we will restore the refcount later worklist_push(&arg->objects, op); return true; } PyObject * _PyGC_GetObjects(PyInterpreterState *interp, Py_ssize_t generation) { PyObject *result = PyList_New(0); if (!result) { return NULL; } _PyEval_StopTheWorld(interp); // Append all objects to a worklist. This abuses ob_tid. We will restore // it later. NOTE: We can't append to the list during gc_visit_heaps() // because PyList_Append() may reclaim an abandoned mimalloc segment // while we are traversing it. struct get_objects_args args = { 0 }; gc_visit_heaps(interp, &visit_get_objects, &args.base); bool error = false; PyObject *op; while ((op = worklist_pop(&args.objects)) != NULL) { gc_restore_tid(op); if (op != result && PyList_Append(result, op) < 0) { error = true; break; } } // In case of error, clear the remaining worklist while ((op = worklist_pop(&args.objects)) != NULL) { gc_restore_tid(op); } _PyEval_StartTheWorld(interp); if (error) { Py_DECREF(result); return NULL; } return result; } static bool visit_freeze(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, true); if (op != NULL) { op->ob_gc_bits |= _PyGC_BITS_FROZEN; } return true; } void _PyGC_Freeze(PyInterpreterState *interp) { struct visitor_args args; _PyEval_StopTheWorld(interp); gc_visit_heaps(interp, &visit_freeze, &args); _PyEval_StartTheWorld(interp); } static bool visit_unfreeze(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, true); if (op != NULL) { op->ob_gc_bits &= ~_PyGC_BITS_FROZEN; } return true; } void _PyGC_Unfreeze(PyInterpreterState *interp) { struct visitor_args args; _PyEval_StopTheWorld(interp); gc_visit_heaps(interp, &visit_unfreeze, &args); _PyEval_StartTheWorld(interp); } struct count_frozen_args { struct visitor_args base; Py_ssize_t count; }; static bool visit_count_frozen(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, true); if (op != NULL && (op->ob_gc_bits & _PyGC_BITS_FROZEN) != 0) { struct count_frozen_args *arg = (struct count_frozen_args *)args; arg->count++; } return true; } Py_ssize_t _PyGC_GetFreezeCount(PyInterpreterState *interp) { struct count_frozen_args args = { .count = 0 }; _PyEval_StopTheWorld(interp); gc_visit_heaps(interp, &visit_count_frozen, &args.base); _PyEval_StartTheWorld(interp); return args.count; } /* C API for controlling the state of the garbage collector */ int PyGC_Enable(void) { GCState *gcstate = get_gc_state(); int old_state = gcstate->enabled; gcstate->enabled = 1; return old_state; } int PyGC_Disable(void) { GCState *gcstate = get_gc_state(); int old_state = gcstate->enabled; gcstate->enabled = 0; return old_state; } int PyGC_IsEnabled(void) { GCState *gcstate = get_gc_state(); return gcstate->enabled; } /* Public API to invoke gc.collect() from C */ Py_ssize_t PyGC_Collect(void) { PyThreadState *tstate = _PyThreadState_GET(); GCState *gcstate = &tstate->interp->gc; if (!gcstate->enabled) { return 0; } Py_ssize_t n; PyObject *exc = _PyErr_GetRaisedException(tstate); n = gc_collect_main(tstate, NUM_GENERATIONS - 1, _Py_GC_REASON_MANUAL); _PyErr_SetRaisedException(tstate, exc); return n; } Py_ssize_t _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason) { return gc_collect_main(tstate, generation, reason); } Py_ssize_t _PyGC_CollectNoFail(PyThreadState *tstate) { /* Ideally, this function is only called on interpreter shutdown, and therefore not recursively. Unfortunately, when there are daemon threads, a daemon thread can start a cyclic garbage collection during interpreter shutdown (and then never finish it). See http://bugs.python.org/issue8713#msg195178 for an example. */ return gc_collect_main(tstate, NUM_GENERATIONS - 1, _Py_GC_REASON_SHUTDOWN); } void _PyGC_DumpShutdownStats(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; if (!(gcstate->debug & _PyGC_DEBUG_SAVEALL) && gcstate->garbage != NULL && PyList_GET_SIZE(gcstate->garbage) > 0) { const char *message; if (gcstate->debug & _PyGC_DEBUG_UNCOLLECTABLE) { message = "gc: %zd uncollectable objects at shutdown"; } else { message = "gc: %zd uncollectable objects at shutdown; " \ "use gc.set_debug(gc.DEBUG_UNCOLLECTABLE) to list them"; } /* PyErr_WarnFormat does too many things and we are at shutdown, the warnings module's dependencies (e.g. linecache) may be gone already. */ if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0, "gc", NULL, message, PyList_GET_SIZE(gcstate->garbage))) { PyErr_WriteUnraisable(NULL); } if (gcstate->debug & _PyGC_DEBUG_UNCOLLECTABLE) { PyObject *repr = NULL, *bytes = NULL; repr = PyObject_Repr(gcstate->garbage); if (!repr || !(bytes = PyUnicode_EncodeFSDefault(repr))) { PyErr_WriteUnraisable(gcstate->garbage); } else { PySys_WriteStderr( " %s\n", PyBytes_AS_STRING(bytes) ); } Py_XDECREF(repr); Py_XDECREF(bytes); } } } void _PyGC_Fini(PyInterpreterState *interp) { GCState *gcstate = &interp->gc; Py_CLEAR(gcstate->garbage); Py_CLEAR(gcstate->callbacks); /* We expect that none of this interpreters objects are shared with other interpreters. See https://github.com/python/cpython/issues/90228. */ } /* for debugging */ #ifdef Py_DEBUG static int visit_validate(PyObject *op, void *parent_raw) { PyObject *parent = _PyObject_CAST(parent_raw); if (_PyObject_IsFreed(op)) { _PyObject_ASSERT_FAILED_MSG(parent, "PyObject_GC_Track() object is not valid"); } return 0; } #endif /* extension modules might be compiled with GC support so these functions must always be available */ void PyObject_GC_Track(void *op_raw) { PyObject *op = _PyObject_CAST(op_raw); if (_PyObject_GC_IS_TRACKED(op)) { _PyObject_ASSERT_FAILED_MSG(op, "object already tracked " "by the garbage collector"); } _PyObject_GC_TRACK(op); #ifdef Py_DEBUG /* Check that the object is valid: validate objects traversed by tp_traverse() */ traverseproc traverse = Py_TYPE(op)->tp_traverse; (void)traverse(op, visit_validate, op); #endif } void PyObject_GC_UnTrack(void *op_raw) { PyObject *op = _PyObject_CAST(op_raw); /* Obscure: the Py_TRASHCAN mechanism requires that we be able to * call PyObject_GC_UnTrack twice on an object. */ if (_PyObject_GC_IS_TRACKED(op)) { _PyObject_GC_UNTRACK(op); } } int PyObject_IS_GC(PyObject *obj) { return _PyObject_IS_GC(obj); } void _Py_ScheduleGC(PyInterpreterState *interp) { _Py_set_eval_breaker_bit(interp, _PY_GC_SCHEDULED_BIT, 1); } void _PyObject_GC_Link(PyObject *op) { PyThreadState *tstate = _PyThreadState_GET(); GCState *gcstate = &tstate->interp->gc; gcstate->generations[0].count++; /* number of allocated GC objects */ if (gcstate->generations[0].count > gcstate->generations[0].threshold && gcstate->enabled && gcstate->generations[0].threshold && !_Py_atomic_load_int_relaxed(&gcstate->collecting) && !_PyErr_Occurred(tstate)) { _Py_ScheduleGC(tstate->interp); } } void _Py_RunGC(PyThreadState *tstate) { gc_collect_main(tstate, GENERATION_AUTO, _Py_GC_REASON_HEAP); } static PyObject * gc_alloc(PyTypeObject *tp, size_t basicsize, size_t presize) { PyThreadState *tstate = _PyThreadState_GET(); if (basicsize > PY_SSIZE_T_MAX - presize) { return _PyErr_NoMemory(tstate); } size_t size = presize + basicsize; char *mem = _PyObject_MallocWithType(tp, size); if (mem == NULL) { return _PyErr_NoMemory(tstate); } ((PyObject **)mem)[0] = NULL; ((PyObject **)mem)[1] = NULL; PyObject *op = (PyObject *)(mem + presize); _PyObject_GC_Link(op); return op; } PyObject * _PyObject_GC_New(PyTypeObject *tp) { size_t presize = _PyType_PreHeaderSize(tp); PyObject *op = gc_alloc(tp, _PyObject_SIZE(tp), presize); if (op == NULL) { return NULL; } _PyObject_Init(op, tp); return op; } PyVarObject * _PyObject_GC_NewVar(PyTypeObject *tp, Py_ssize_t nitems) { PyVarObject *op; if (nitems < 0) { PyErr_BadInternalCall(); return NULL; } size_t presize = _PyType_PreHeaderSize(tp); size_t size = _PyObject_VAR_SIZE(tp, nitems); op = (PyVarObject *)gc_alloc(tp, size, presize); if (op == NULL) { return NULL; } _PyObject_InitVar(op, tp, nitems); return op; } PyObject * PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *tp, size_t extra_size) { size_t presize = _PyType_PreHeaderSize(tp); PyObject *op = gc_alloc(tp, _PyObject_SIZE(tp) + extra_size, presize); if (op == NULL) { return NULL; } memset(op, 0, _PyObject_SIZE(tp) + extra_size); _PyObject_Init(op, tp); return op; } PyVarObject * _PyObject_GC_Resize(PyVarObject *op, Py_ssize_t nitems) { const size_t basicsize = _PyObject_VAR_SIZE(Py_TYPE(op), nitems); const size_t presize = _PyType_PreHeaderSize(((PyObject *)op)->ob_type); _PyObject_ASSERT((PyObject *)op, !_PyObject_GC_IS_TRACKED(op)); if (basicsize > (size_t)PY_SSIZE_T_MAX - presize) { return (PyVarObject *)PyErr_NoMemory(); } char *mem = (char *)op - presize; mem = (char *)_PyObject_ReallocWithType(Py_TYPE(op), mem, presize + basicsize); if (mem == NULL) { return (PyVarObject *)PyErr_NoMemory(); } op = (PyVarObject *) (mem + presize); Py_SET_SIZE(op, nitems); return op; } void PyObject_GC_Del(void *op) { size_t presize = _PyType_PreHeaderSize(((PyObject *)op)->ob_type); if (_PyObject_GC_IS_TRACKED(op)) { #ifdef Py_DEBUG PyObject *exc = PyErr_GetRaisedException(); if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0, "gc", NULL, "Object of type %s is not untracked before destruction", ((PyObject*)op)->ob_type->tp_name)) { PyErr_WriteUnraisable(NULL); } PyErr_SetRaisedException(exc); #endif } GCState *gcstate = get_gc_state(); if (gcstate->generations[0].count > 0) { gcstate->generations[0].count--; } PyObject_Free(((char *)op)-presize); } int PyObject_GC_IsTracked(PyObject* obj) { if (_PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)) { return 1; } return 0; } int PyObject_GC_IsFinalized(PyObject *obj) { if (_PyObject_IS_GC(obj) && _PyGC_FINALIZED(obj)) { return 1; } return 0; } struct custom_visitor_args { struct visitor_args base; gcvisitobjects_t callback; void *arg; }; static bool custom_visitor_wrapper(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *args) { PyObject *op = op_from_block(block, args, false); if (op == NULL) { return true; } struct custom_visitor_args *wrapper = (struct custom_visitor_args *)args; if (!wrapper->callback(op, wrapper->arg)) { return false; } return true; } void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg) { PyInterpreterState *interp = _PyInterpreterState_GET(); struct custom_visitor_args wrapper = { .callback = callback, .arg = arg, }; _PyEval_StopTheWorld(interp); gc_visit_heaps(interp, &custom_visitor_wrapper, &wrapper.base); _PyEval_StartTheWorld(interp); } /* Clear all free lists * All free lists are cleared during the collection of the highest generation. * Allocated items in the free list may keep a pymalloc arena occupied. * Clearing the free lists may give back memory to the OS earlier. * Free-threading version: Since freelists are managed per thread, * GC should clear all freelists by traversing all threads. */ void _PyGC_ClearAllFreeLists(PyInterpreterState *interp) { _PyDict_ClearFreeList(interp); HEAD_LOCK(&_PyRuntime); _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)interp->threads.head; while (tstate != NULL) { _Py_ClearFreeLists(&tstate->freelist_state, 0); tstate = (_PyThreadStateImpl *)tstate->base.next; } HEAD_UNLOCK(&_PyRuntime); } #endif // Py_GIL_DISABLED