Skip to content

Commit 45294a9

Browse files
committed
Remove types from type_list if they have no objects
and unlist_types_without_objects is set. Give dump_counts a FILE* argument.
1 parent 041669f commit 45294a9

File tree

4 files changed

+47
-9
lines changed

4 files changed

+47
-9
lines changed

Include/object.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ typedef struct _typeobject {
339339
Py_ssize_t tp_allocs;
340340
Py_ssize_t tp_frees;
341341
Py_ssize_t tp_maxalloc;
342+
struct _typeobject *tp_prev;
342343
struct _typeobject *tp_next;
343344
#endif
344345
} PyTypeObject;
@@ -598,8 +599,9 @@ PyAPI_FUNC(Py_ssize_t) _Py_GetRefTotal(void);
598599

599600
#ifdef COUNT_ALLOCS
600601
PyAPI_FUNC(void) inc_count(PyTypeObject *);
602+
PyAPI_FUNC(void) dec_count(PyTypeObject *);
601603
#define _Py_INC_TPALLOCS(OP) inc_count((OP)->ob_type)
602-
#define _Py_INC_TPFREES(OP) (OP)->ob_type->tp_frees++
604+
#define _Py_INC_TPFREES(OP) dec_count((OP)->ob_type)
603605
#define _Py_DEC_TPFREES(OP) (OP)->ob_type->tp_frees--
604606
#define _Py_COUNT_ALLOCS_COMMA ,
605607
#else

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ What's New in Python 2.5 alpha 2?
1212
Core and builtins
1313
-----------------
1414

15+
- Under COUNT_ALLOCS, types are not necessarily immortal anymore.
16+
1517
- All uses of PyStructSequence_InitType have been changed to initialize
1618
the type objects only once, even if the interpreter is initialized
1719
multiple times.

Objects/object.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,23 +74,30 @@ _Py_AddToAllObjects(PyObject *op, int force)
7474

7575
#ifdef COUNT_ALLOCS
7676
static PyTypeObject *type_list;
77+
/* All types are added to type_list, atleast when
78+
they get one object created. That makes them
79+
immortal, which unfortunately contributes to
80+
garbage itself. If unlist_types_without_objects
81+
is set, they will be removed from the type_list
82+
once the last object is deallocated. */
83+
int unlist_types_without_objects;
7784
extern int tuple_zero_allocs, fast_tuple_allocs;
7885
extern int quick_int_allocs, quick_neg_int_allocs;
7986
extern int null_strings, one_strings;
8087
void
81-
dump_counts(void)
88+
dump_counts(FILE* f)
8289
{
8390
PyTypeObject *tp;
8491

8592
for (tp = type_list; tp; tp = tp->tp_next)
86-
fprintf(stderr, "%s alloc'd: %d, freed: %d, max in use: %d\n",
93+
fprintf(f, "%s alloc'd: %d, freed: %d, max in use: %d\n",
8794
tp->tp_name, tp->tp_allocs, tp->tp_frees,
8895
tp->tp_maxalloc);
89-
fprintf(stderr, "fast tuple allocs: %d, empty: %d\n",
96+
fprintf(f, "fast tuple allocs: %d, empty: %d\n",
9097
fast_tuple_allocs, tuple_zero_allocs);
91-
fprintf(stderr, "fast int allocs: pos: %d, neg: %d\n",
98+
fprintf(f, "fast int allocs: pos: %d, neg: %d\n",
9299
quick_int_allocs, quick_neg_int_allocs);
93-
fprintf(stderr, "null strings: %d, 1-strings: %d\n",
100+
fprintf(f, "null strings: %d, 1-strings: %d\n",
94101
null_strings, one_strings);
95102
}
96103

@@ -124,10 +131,12 @@ get_counts(void)
124131
void
125132
inc_count(PyTypeObject *tp)
126133
{
127-
if (tp->tp_allocs == 0) {
134+
if (tp->tp_next == NULL && tp->tp_prev == NULL) {
128135
/* first time; insert in linked list */
129136
if (tp->tp_next != NULL) /* sanity check */
130137
Py_FatalError("XXX inc_count sanity check");
138+
if (type_list)
139+
type_list->tp_prev = tp;
131140
tp->tp_next = type_list;
132141
/* Note that as of Python 2.2, heap-allocated type objects
133142
* can go away, but this code requires that they stay alive
@@ -150,6 +159,24 @@ inc_count(PyTypeObject *tp)
150159
if (tp->tp_allocs - tp->tp_frees > tp->tp_maxalloc)
151160
tp->tp_maxalloc = tp->tp_allocs - tp->tp_frees;
152161
}
162+
163+
void dec_count(PyTypeObject *tp)
164+
{
165+
tp->tp_frees++;
166+
if (unlist_types_without_objects &&
167+
tp->tp_allocs == tp->tp_frees) {
168+
/* unlink the type from type_list */
169+
if (tp->tp_prev)
170+
tp->tp_prev->tp_next = tp->tp_next;
171+
else
172+
type_list = tp->tp_next;
173+
if (tp->tp_next)
174+
tp->tp_next->tp_prev = tp->tp_prev;
175+
tp->tp_next = tp->tp_prev = NULL;
176+
Py_DECREF(tp);
177+
}
178+
}
179+
153180
#endif
154181

155182
#ifdef Py_REF_DEBUG

Python/pythonrun.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ Py_Initialize(void)
311311

312312

313313
#ifdef COUNT_ALLOCS
314-
extern void dump_counts(void);
314+
extern void dump_counts(FILE*);
315315
#endif
316316

317317
/* Undo the effect of Py_Initialize().
@@ -373,6 +373,13 @@ Py_Finalize(void)
373373
* XXX I haven't seen a real-life report of either of these.
374374
*/
375375
PyGC_Collect();
376+
#ifdef COUNT_ALLOCS
377+
/* With COUNT_ALLOCS, it helps to run GC multiple times:
378+
each collection might release some types from the type
379+
list, so they become garbage. */
380+
while (PyGC_Collect() > 0)
381+
/* nothing */;
382+
#endif
376383

377384
/* Destroy all modules */
378385
PyImport_Cleanup();
@@ -401,7 +408,7 @@ Py_Finalize(void)
401408

402409
/* Debugging stuff */
403410
#ifdef COUNT_ALLOCS
404-
dump_counts();
411+
dump_counts(stdout);
405412
#endif
406413

407414
PRINT_TOTAL_REFS();

0 commit comments

Comments
 (0)