89 lines
3.1 KiB
Python
89 lines
3.1 KiB
Python
# Compute and gather statistics about garbage collection in this process.
|
|
# This module depends on the CPython gc module and garbage collection behavior.
|
|
|
|
import gc, logging, pprint
|
|
|
|
|
|
verbose = False
|
|
|
|
|
|
# A mapping from type objects to a count of instances of those types in the
|
|
# garbage collectors all objects list on the previous call to
|
|
# _log_garbage_collector_stats().
|
|
_previous_obj_type_map = {}
|
|
|
|
|
|
# A set of object ids for everything in the all objects list on the
|
|
# previous call to _log_garbage_collector_stats().
|
|
_previous_obj_ids = set()
|
|
|
|
|
|
def _log_garbage_collector_stats(minimum_count=10):
|
|
"""
|
|
Log statistics about how many of what type of Python object exist in this
|
|
process.
|
|
|
|
@param minimum_count: The minimum number of instances of a type for it
|
|
to be considered worthy of logging.
|
|
"""
|
|
global _previous_obj_type_map
|
|
global _previous_obj_ids
|
|
|
|
# We get all objects -before- creating any new objects within this function.
|
|
# to avoid having our own local instances in the list.
|
|
all_objects = gc.get_objects()
|
|
obj = None
|
|
new_objects = []
|
|
try:
|
|
obj_type_map = {}
|
|
object_ids = set()
|
|
for obj in all_objects:
|
|
obj_type = type(obj)
|
|
obj_type_map.setdefault(obj_type, 0)
|
|
obj_type_map[obj_type] += 1
|
|
object_ids.add(id(obj))
|
|
whats_new_big_str = ''
|
|
if verbose and _previous_obj_ids:
|
|
new_object_ids = object_ids - _previous_obj_ids
|
|
for obj in all_objects:
|
|
if id(obj) in new_object_ids:
|
|
new_objects.append(obj)
|
|
whats_new_big_str = pprint.pformat(new_objects, indent=1)
|
|
finally:
|
|
# Never keep references to stuff returned by gc.get_objects() around
|
|
# or it'll just make the future cyclic gc runs more difficult.
|
|
del all_objects
|
|
del obj
|
|
del new_objects
|
|
|
|
|
|
delta = {}
|
|
for obj_type, count in obj_type_map.iteritems():
|
|
if obj_type not in _previous_obj_type_map:
|
|
delta[obj_type] = count
|
|
elif _previous_obj_type_map[obj_type] != count:
|
|
delta[obj_type] = count - _previous_obj_type_map[obj_type]
|
|
|
|
sorted_stats = reversed(sorted(
|
|
(count, obj_type) for obj_type, count in obj_type_map.iteritems()))
|
|
sorted_delta = reversed(sorted(
|
|
(count, obj_type) for obj_type, count in delta.iteritems()))
|
|
|
|
logging.debug('Garbage collector object type counts:')
|
|
for count, obj_type in sorted_stats:
|
|
if count >= minimum_count:
|
|
logging.debug(' %d\t%s', count, obj_type)
|
|
|
|
logging.info('Change in object counts since previous GC stats:')
|
|
for change, obj_type in sorted_delta:
|
|
if obj_type_map[obj_type] > minimum_count:
|
|
logging.info(' %+d\t%s\tto %d', change, obj_type,
|
|
obj_type_map[obj_type])
|
|
|
|
if verbose and whats_new_big_str:
|
|
logging.debug('Pretty printed representation of the new objects:')
|
|
logging.debug(whats_new_big_str)
|
|
|
|
_previous_obj_type_map = obj_type_map
|
|
if verbose:
|
|
_previous_obj_ids = object_ids
|