Files
purple-electrumwallet/electrum/utils/memory_leak.py
T
SomberNight 5d41891554 utils/memory_leak: factor out count_objects_in_memory
and use weakrefs, to properly allow using this to test garbage collection
2026-01-20 22:33:07 +00:00

91 lines
2.7 KiB
Python

from collections import defaultdict
import datetime
import os
import time
from typing import Sequence, Mapping, TypeVar, Optional
import weakref
from electrum.util import ThreadJob
_U = TypeVar('_U')
def count_objects_in_memory(mclasses: Sequence[type[_U]]) -> Mapping[type[_U], Sequence[weakref.ref[_U]]]:
import gc
gc.collect()
objmap = defaultdict(list)
for obj in gc.get_objects():
for class_ in mclasses:
try:
_isinstance = isinstance(obj, class_)
except AttributeError:
_isinstance = False
if _isinstance:
objmap[class_].append(weakref.ref(obj))
return objmap
class DebugMem(ThreadJob):
'''A handy class for debugging GC memory leaks
In Qt console:
>>> from electrum.utils.memory_leak import DebugMem
>>> from electrum.wallet import Abstract_Wallet
>>> plugins.add_jobs([DebugMem([Abstract_Wallet,], interval=5)])
'''
def __init__(self, classes, interval=30):
ThreadJob.__init__(self)
self.next_time = 0
self.classes = classes
self.interval = interval
def mem_stats(self):
self.logger.info("Start memscan")
objmap = count_objects_in_memory(self.classes)
for class_, objs in objmap.items():
self.logger.info(f"{class_.__name__}: {len(objs)}")
self.logger.info("Finish memscan")
def run(self):
if time.time() > self.next_time:
self.mem_stats()
self.next_time = time.time() + self.interval
def debug_memusage_list_all_objects(limit: int = 50) -> list[tuple[str, int]]:
"""Return a string listing the most common types in memory."""
import objgraph # 3rd-party dependency
return objgraph.most_common_types(
limit=limit,
shortnames=False,
)
def debug_memusage_dump_random_backref_chain(objtype: str) -> Optional[str]:
"""Writes a dotfile to cwd, containing the backref chain
for a randomly selected object of type objtype.
Warning: very slow!
In Qt console:
>>> debug_memusage_dump_random_backref_chain("Standard_Wallet")
To convert to image:
$ dot -Tps filename.dot -o outfile.ps
"""
import objgraph # 3rd-party dependency
import random
timestamp = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%dT%H%M%SZ")
fpath = os.path.abspath(f"electrum_backref_chain_{timestamp}.dot")
objects = objgraph.by_type(objtype)
if not objects:
return None
random_obj = random.choice(objects)
with open(fpath, "w") as f:
objgraph.show_chain(
objgraph.find_backref_chain(
random_obj,
objgraph.is_proper_module),
output=f)
return fpath