summaryrefslogtreecommitdiffstats
path: root/Include/internal/mem.h
diff options
context:
space:
mode:
Diffstat (limited to 'Include/internal/mem.h')
-rw-r--r--Include/internal/mem.h197
1 files changed, 197 insertions, 0 deletions
diff --git a/Include/internal/mem.h b/Include/internal/mem.h
new file mode 100644
index 0000000..1624f37
--- /dev/null
+++ b/Include/internal/mem.h
@@ -0,0 +1,197 @@
+#ifndef Py_INTERNAL_MEM_H
+#define Py_INTERNAL_MEM_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "objimpl.h"
+#include "pymem.h"
+
+#ifdef WITH_PYMALLOC
+#include "internal/pymalloc.h"
+#endif
+
+/* Low-level memory runtime state */
+
+struct _pymem_runtime_state {
+ struct _allocator_runtime_state {
+ PyMemAllocatorEx mem;
+ PyMemAllocatorEx obj;
+ PyMemAllocatorEx raw;
+ } allocators;
+#ifdef WITH_PYMALLOC
+ /* Array of objects used to track chunks of memory (arenas). */
+ struct arena_object* arenas;
+ /* The head of the singly-linked, NULL-terminated list of available
+ arena_objects. */
+ struct arena_object* unused_arena_objects;
+ /* The head of the doubly-linked, NULL-terminated at each end,
+ list of arena_objects associated with arenas that have pools
+ available. */
+ struct arena_object* usable_arenas;
+ /* Number of slots currently allocated in the `arenas` vector. */
+ unsigned int maxarenas;
+ /* Number of arenas allocated that haven't been free()'d. */
+ size_t narenas_currently_allocated;
+ /* High water mark (max value ever seen) for
+ * narenas_currently_allocated. */
+ size_t narenas_highwater;
+ /* Total number of times malloc() called to allocate an arena. */
+ size_t ntimes_arena_allocated;
+ poolp usedpools[MAX_POOLS];
+ Py_ssize_t num_allocated_blocks;
+ size_t serialno; /* incremented on each debug {m,re}alloc */
+#endif /* WITH_PYMALLOC */
+};
+
+PyAPI_FUNC(void) _PyMem_Initialize(struct _pymem_runtime_state *);
+
+
+/* High-level memory runtime state */
+
+struct _pyobj_runtime_state {
+ PyObjectArenaAllocator allocator_arenas;
+};
+
+PyAPI_FUNC(void) _PyObject_Initialize(struct _pyobj_runtime_state *);
+
+
+/* GC runtime state */
+
+/* If we change this, we need to change the default value in the
+ signature of gc.collect. */
+#define NUM_GENERATIONS 3
+
+/*
+ NOTE: about the counting of long-lived objects.
+
+ 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
+ <constant number> 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
+*/
+
+/*
+ NOTE: about untracking of mutable objects.
+
+ Certain types of container 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 collections. 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:
+
+ i) When the container is created.
+ ii) When the container is examined by the garbage collector.
+
+ 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 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.
+
+ Untracking of certain containers was introduced in issue #4688, and
+ the algorithm was refined in response to issue #14775.
+*/
+
+struct gc_generation {
+ PyGC_Head head;
+ int threshold; /* collection threshold */
+ int count; /* count of allocations or collections of younger
+ generations */
+};
+
+/* Running stats per generation */
+struct gc_generation_stats {
+ /* total number of collections */
+ Py_ssize_t collections;
+ /* total number of collected objects */
+ Py_ssize_t collected;
+ /* total number of uncollectable objects (put into gc.garbage) */
+ Py_ssize_t uncollectable;
+};
+
+struct _gc_runtime_state {
+ /* List of objects that still need to be cleaned up, singly linked
+ * via their gc headers' gc_prev pointers. */
+ PyObject *trash_delete_later;
+ /* Current call-stack depth of tp_dealloc calls. */
+ int trash_delete_nesting;
+
+ int enabled;
+ int debug;
+ /* linked lists of container objects */
+ struct gc_generation generations[NUM_GENERATIONS];
+ PyGC_Head *generation0;
+ struct gc_generation_stats generation_stats[NUM_GENERATIONS];
+ /* true if we are currently running the collector */
+ int collecting;
+ /* list of uncollectable objects */
+ PyObject *garbage;
+ /* a list of callbacks to be invoked when collection is performed */
+ PyObject *callbacks;
+ /* This is the number of objects that survived the last full
+ collection. It approximates the number of long lived objects
+ tracked by the GC.
+
+ (by "full collection", we mean a collection of the oldest
+ generation). */
+ Py_ssize_t long_lived_total;
+ /* This is the number of objects that survived all "non-full"
+ collections, and are awaiting to undergo a full collection for
+ the first time. */
+ Py_ssize_t long_lived_pending;
+};
+
+PyAPI_FUNC(void) _PyGC_Initialize(struct _gc_runtime_state *);
+
+#define _PyGC_generation0 _PyRuntime.gc.generation0
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* !Py_INTERNAL_MEM_H */