Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Include/internal/pycore_gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,14 @@ static inline void _PyObject_GC_TRACK(
"object is in generation which is garbage collected",
filename, lineno, __func__);

PyGC_Head *generation0 = _PyInterpreterState_GET()->gc.generation0;
struct _gc_runtime_state *gcstate = &_PyInterpreterState_GET()->gc;
PyGC_Head *generation0 = gcstate->generation0;
PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev);
_PyGCHead_SET_NEXT(last, gc);
_PyGCHead_SET_PREV(gc, last);
_PyGCHead_SET_NEXT(gc, generation0);
generation0->_gc_prev = (uintptr_t)gc;
gcstate->heap_size++;
#endif
}

Expand Down Expand Up @@ -263,6 +265,8 @@ static inline void _PyObject_GC_UNTRACK(
_PyGCHead_SET_PREV(next, prev);
gc->_gc_next = 0;
gc->_gc_prev &= _PyGC_PREV_MASK_FINALIZED;
struct _gc_runtime_state *gcstate = &_PyInterpreterState_GET()->gc;
gcstate->heap_size--;
#endif
}

Expand Down
6 changes: 5 additions & 1 deletion Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ struct _gc_runtime_state {
/* a list of callbacks to be invoked when collection is performed */
PyObject *callbacks;

/* The number of live objects. */
Py_ssize_t heap_size;

/* This is the number of objects that survived the last full
collection. It approximates the number of long lived objects
tracked by the GC.
Expand Down Expand Up @@ -261,7 +264,8 @@ struct _gc_runtime_state {
{ .threshold = 2000, }, \
{ .threshold = 10, }, \
{ .threshold = 10, }, \
},
}, \
.heap_size = 0,
#else
#define GC_GENERATION_INIT \
.young = { .threshold = 2000, }, \
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_gc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,15 @@ def test_tuple_untrack_counts(self):
# Use n // 2 just in case some other objects were collected.
self.assertTrue(new_count - count > (n // 2))

@requires_gil_enabled('need generational GC')
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
def test_heap_size(self):
count = _testinternalcapi.get_tracked_heap_size()
l = []
self.assertEqual(count + 1, _testinternalcapi.get_tracked_heap_size())
del l
self.assertEqual(count, _testinternalcapi.get_tracked_heap_size())


class GCCallbackTests(unittest.TestCase):
def setUp(self):
Expand Down
3 changes: 1 addition & 2 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -2353,8 +2353,7 @@ has_deferred_refcount(PyObject *self, PyObject *op)
static PyObject *
get_tracked_heap_size(PyObject *self, PyObject *Py_UNUSED(ignored))
{
// Generational GC doesn't track heap_size, return -1.
return PyLong_FromInt64(-1);
return PyLong_FromInt64(PyInterpreterState_Get()->gc.heap_size);
}

static PyObject *
Expand Down
2 changes: 2 additions & 0 deletions Python/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1981,6 +1981,8 @@ PyObject_GC_Del(void *op)
PyGC_Head *g = AS_GC(op);
if (_PyObject_GC_IS_TRACKED(op)) {
gc_list_remove(g);
GCState *gcstate = get_gc_state();
gcstate->heap_size--;
#ifdef Py_DEBUG
PyObject *exc = PyErr_GetRaisedException();
if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0,
Expand Down
Loading