summaryrefslogtreecommitdiffstats
path: root/Include
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2024-11-18 14:31:26 (GMT)
committerGitHub <noreply@github.com>2024-11-18 14:31:26 (GMT)
commitb0fcc2c47a34a69c35c1a8031cd0589d3747c1af (patch)
treeaa9d4bfaf49dca8d3ce47aa18a03cf2d5374a8e2 /Include
parenta1d9c8aa800dd7c9eb634f89646be10e9cfc9c8d (diff)
downloadcpython-b0fcc2c47a34a69c35c1a8031cd0589d3747c1af.zip
cpython-b0fcc2c47a34a69c35c1a8031cd0589d3747c1af.tar.gz
cpython-b0fcc2c47a34a69c35c1a8031cd0589d3747c1af.tar.bz2
GH-126491: GC: Mark objects reachable from roots before doing cycle collection (GH-126502)
* Mark almost all reachable objects before doing collection phase * Add stats for objects marked * Visit new frames before each increment * Remove lazy dict tracking * Update docs * Clearer calculation of work to do.
Diffstat (limited to 'Include')
-rw-r--r--Include/cpython/pystats.h2
-rw-r--r--Include/internal/pycore_dict.h2
-rw-r--r--Include/internal/pycore_frame.h3
-rw-r--r--Include/internal/pycore_gc.h11
-rw-r--r--Include/internal/pycore_object.h4
-rw-r--r--Include/internal/pycore_runtime_init.h1
6 files changed, 17 insertions, 6 deletions
diff --git a/Include/cpython/pystats.h b/Include/cpython/pystats.h
index f1ca548..2ae4800 100644
--- a/Include/cpython/pystats.h
+++ b/Include/cpython/pystats.h
@@ -99,6 +99,8 @@ typedef struct _gc_stats {
uint64_t collections;
uint64_t object_visits;
uint64_t objects_collected;
+ uint64_t objects_transitively_reachable;
+ uint64_t objects_not_transitively_reachable;
} GCStats;
typedef struct _uop_stats {
diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h
index c5399ad..f7d747c 100644
--- a/Include/internal/pycore_dict.h
+++ b/Include/internal/pycore_dict.h
@@ -43,8 +43,6 @@ extern int _PyDict_Next(
extern int _PyDict_HasOnlyStringKeys(PyObject *mp);
-extern void _PyDict_MaybeUntrack(PyObject *mp);
-
// Export for '_ctypes' shared extension
PyAPI_FUNC(Py_ssize_t) _PyDict_SizeOf(PyDictObject *);
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index 8c01003..b786c5f 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -75,6 +75,7 @@ typedef struct _PyInterpreterFrame {
_PyStackRef *stackpointer;
uint16_t return_offset; /* Only relevant during a function call */
char owner;
+ char visited;
/* Locals and stack */
_PyStackRef localsplus[1];
} _PyInterpreterFrame;
@@ -207,6 +208,7 @@ _PyFrame_Initialize(
#endif
frame->return_offset = 0;
frame->owner = FRAME_OWNED_BY_THREAD;
+ frame->visited = 0;
for (int i = null_locals_from; i < code->co_nlocalsplus; i++) {
frame->localsplus[i] = PyStackRef_NULL;
@@ -389,6 +391,7 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int
frame->instr_ptr = _PyCode_CODE(code);
#endif
frame->owner = FRAME_OWNED_BY_THREAD;
+ frame->visited = 0;
frame->return_offset = 0;
#ifdef Py_GIL_DISABLED
diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h
index 38a1c56..c81e7a1 100644
--- a/Include/internal/pycore_gc.h
+++ b/Include/internal/pycore_gc.h
@@ -10,11 +10,11 @@ extern "C" {
/* GC information is stored BEFORE the object structure. */
typedef struct {
- // Pointer to next object in the list.
+ // Tagged pointer to next object in the list.
// 0 means the object is not tracked
uintptr_t _gc_next;
- // Pointer to previous object in the list.
+ // Tagged pointer to previous object in the list.
// Lowest two bits are used for flags documented later.
uintptr_t _gc_prev;
} PyGC_Head;
@@ -302,6 +302,11 @@ struct gc_generation_stats {
Py_ssize_t uncollectable;
};
+enum _GCPhase {
+ GC_PHASE_MARK = 0,
+ GC_PHASE_COLLECT = 1
+};
+
struct _gc_runtime_state {
/* List of objects that still need to be cleaned up, singly linked
* via their gc headers' gc_prev pointers. */
@@ -325,10 +330,12 @@ struct _gc_runtime_state {
/* a list of callbacks to be invoked when collection is performed */
PyObject *callbacks;
+ Py_ssize_t prior_heap_size;
Py_ssize_t heap_size;
Py_ssize_t work_to_do;
/* Which of the old spaces is the visited space */
int visited_space;
+ int phase;
#ifdef Py_GIL_DISABLED
/* This is the number of objects that survived the last full
diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h
index c7af720..0cba7bc 100644
--- a/Include/internal/pycore_object.h
+++ b/Include/internal/pycore_object.h
@@ -466,8 +466,8 @@ static inline void _PyObject_GC_TRACK(
PyGC_Head *last = (PyGC_Head*)(generation0->_gc_prev);
_PyGCHead_SET_NEXT(last, gc);
_PyGCHead_SET_PREV(gc, last);
- /* Young objects will be moved into the visited space during GC, so set the bit here */
- gc->_gc_next = ((uintptr_t)generation0) | (uintptr_t)interp->gc.visited_space;
+ uintptr_t not_visited = 1 ^ interp->gc.visited_space;
+ gc->_gc_next = ((uintptr_t)generation0) | not_visited;
generation0->_gc_prev = (uintptr_t)gc;
#endif
}
diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h
index 8a8f476..ce116ad 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -134,6 +134,7 @@ extern PyTypeObject _PyExc_MemoryError;
{ .threshold = 0, }, \
}, \
.work_to_do = -5000, \
+ .phase = GC_PHASE_MARK, \
}, \
.qsbr = { \
.wr_seq = QSBR_INITIAL, \