mirror of
https://github.com/python/cpython.git
synced 2025-07-30 22:54:16 +00:00
Remove types from type_list if they have no objects
and unlist_types_without_objects is set. Give dump_counts a FILE* argument.
This commit is contained in:
parent
041669fa67
commit
45294a9562
4 changed files with 47 additions and 9 deletions
|
@ -339,6 +339,7 @@ typedef struct _typeobject {
|
|||
Py_ssize_t tp_allocs;
|
||||
Py_ssize_t tp_frees;
|
||||
Py_ssize_t tp_maxalloc;
|
||||
struct _typeobject *tp_prev;
|
||||
struct _typeobject *tp_next;
|
||||
#endif
|
||||
} PyTypeObject;
|
||||
|
@ -598,8 +599,9 @@ PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void);
|
|||
|
||||
#ifdef COUNT_ALLOCS
|
||||
PyAPI_FUNC(void) inc_count(PyTypeObject *);
|
||||
PyAPI_FUNC(void) dec_count(PyTypeObject *);
|
||||
#define _Py_INC_TPALLOCS(OP) inc_count((OP)->ob_type)
|
||||
#define _Py_INC_TPFREES(OP) (OP)->ob_type->tp_frees++
|
||||
#define _Py_INC_TPFREES(OP) dec_count((OP)->ob_type)
|
||||
#define _Py_DEC_TPFREES(OP) (OP)->ob_type->tp_frees--
|
||||
#define _Py_COUNT_ALLOCS_COMMA ,
|
||||
#else
|
||||
|
|
|
@ -12,6 +12,8 @@ What's New in Python 2.5 alpha 2?
|
|||
Core and builtins
|
||||
-----------------
|
||||
|
||||
- Under COUNT_ALLOCS, types are not necessarily immortal anymore.
|
||||
|
||||
- All uses of PyStructSequence_InitType have been changed to initialize
|
||||
the type objects only once, even if the interpreter is initialized
|
||||
multiple times.
|
||||
|
|
|
@ -74,23 +74,30 @@ _Py_AddToAllObjects(PyObject *op, int force)
|
|||
|
||||
#ifdef COUNT_ALLOCS
|
||||
static PyTypeObject *type_list;
|
||||
/* All types are added to type_list, atleast when
|
||||
they get one object created. That makes them
|
||||
immortal, which unfortunately contributes to
|
||||
garbage itself. If unlist_types_without_objects
|
||||
is set, they will be removed from the type_list
|
||||
once the last object is deallocated. */
|
||||
int unlist_types_without_objects;
|
||||
extern int tuple_zero_allocs, fast_tuple_allocs;
|
||||
extern int quick_int_allocs, quick_neg_int_allocs;
|
||||
extern int null_strings, one_strings;
|
||||
void
|
||||
dump_counts(void)
|
||||
dump_counts(FILE* f)
|
||||
{
|
||||
PyTypeObject *tp;
|
||||
|
||||
for (tp = type_list; tp; tp = tp->tp_next)
|
||||
fprintf(stderr, "%s alloc'd: %d, freed: %d, max in use: %d\n",
|
||||
fprintf(f, "%s alloc'd: %d, freed: %d, max in use: %d\n",
|
||||
tp->tp_name, tp->tp_allocs, tp->tp_frees,
|
||||
tp->tp_maxalloc);
|
||||
fprintf(stderr, "fast tuple allocs: %d, empty: %d\n",
|
||||
fprintf(f, "fast tuple allocs: %d, empty: %d\n",
|
||||
fast_tuple_allocs, tuple_zero_allocs);
|
||||
fprintf(stderr, "fast int allocs: pos: %d, neg: %d\n",
|
||||
fprintf(f, "fast int allocs: pos: %d, neg: %d\n",
|
||||
quick_int_allocs, quick_neg_int_allocs);
|
||||
fprintf(stderr, "null strings: %d, 1-strings: %d\n",
|
||||
fprintf(f, "null strings: %d, 1-strings: %d\n",
|
||||
null_strings, one_strings);
|
||||
}
|
||||
|
||||
|
@ -124,10 +131,12 @@ get_counts(void)
|
|||
void
|
||||
inc_count(PyTypeObject *tp)
|
||||
{
|
||||
if (tp->tp_allocs == 0) {
|
||||
if (tp->tp_next == NULL && tp->tp_prev == NULL) {
|
||||
/* first time; insert in linked list */
|
||||
if (tp->tp_next != NULL) /* sanity check */
|
||||
Py_FatalError("XXX inc_count sanity check");
|
||||
if (type_list)
|
||||
type_list->tp_prev = tp;
|
||||
tp->tp_next = type_list;
|
||||
/* Note that as of Python 2.2, heap-allocated type objects
|
||||
* can go away, but this code requires that they stay alive
|
||||
|
@ -150,6 +159,24 @@ inc_count(PyTypeObject *tp)
|
|||
if (tp->tp_allocs - tp->tp_frees > tp->tp_maxalloc)
|
||||
tp->tp_maxalloc = tp->tp_allocs - tp->tp_frees;
|
||||
}
|
||||
|
||||
void dec_count(PyTypeObject *tp)
|
||||
{
|
||||
tp->tp_frees++;
|
||||
if (unlist_types_without_objects &&
|
||||
tp->tp_allocs == tp->tp_frees) {
|
||||
/* unlink the type from type_list */
|
||||
if (tp->tp_prev)
|
||||
tp->tp_prev->tp_next = tp->tp_next;
|
||||
else
|
||||
type_list = tp->tp_next;
|
||||
if (tp->tp_next)
|
||||
tp->tp_next->tp_prev = tp->tp_prev;
|
||||
tp->tp_next = tp->tp_prev = NULL;
|
||||
Py_DECREF(tp);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef Py_REF_DEBUG
|
||||
|
|
|
@ -311,7 +311,7 @@ Py_Initialize(void)
|
|||
|
||||
|
||||
#ifdef COUNT_ALLOCS
|
||||
extern void dump_counts(void);
|
||||
extern void dump_counts(FILE*);
|
||||
#endif
|
||||
|
||||
/* Undo the effect of Py_Initialize().
|
||||
|
@ -373,6 +373,13 @@ Py_Finalize(void)
|
|||
* XXX I haven't seen a real-life report of either of these.
|
||||
*/
|
||||
PyGC_Collect();
|
||||
#ifdef COUNT_ALLOCS
|
||||
/* With COUNT_ALLOCS, it helps to run GC multiple times:
|
||||
each collection might release some types from the type
|
||||
list, so they become garbage. */
|
||||
while (PyGC_Collect() > 0)
|
||||
/* nothing */;
|
||||
#endif
|
||||
|
||||
/* Destroy all modules */
|
||||
PyImport_Cleanup();
|
||||
|
@ -401,7 +408,7 @@ Py_Finalize(void)
|
|||
|
||||
/* Debugging stuff */
|
||||
#ifdef COUNT_ALLOCS
|
||||
dump_counts();
|
||||
dump_counts(stdout);
|
||||
#endif
|
||||
|
||||
PRINT_TOTAL_REFS();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue