如何在Python中分析多线程程序的内存?

10 投票
4 回答
4792 浏览
提问于 2025-04-16 10:37

有没有办法对Python中的多线程程序进行内存分析?

对于CPU分析,我使用cProfile为每个线程创建单独的分析统计数据,然后再把它们合并。不过,我找不到用内存分析工具做到这一点的方法。我现在用的是heapy。

在heapy中,有没有办法像cProfile那样合并统计数据?或者你有什么其他更适合这个任务的内存分析工具推荐吗?

还有一个相关的问题是关于多线程程序的CPU使用分析:如何对Python中的多线程程序进行分析?

还有一个关于内存分析工具的问题:Python内存分析工具

4 个回答

1

好的。我想要的东西似乎不存在。所以,我找到了一种解决方案——一个变通的方法来解决这个问题。

我决定不去分析内存,而是分析对象。这样,我就能看到在程序的某个特定时刻,有多少个对象存在。为了实现这个目标,我使用了元类,并对已有的代码做了最小的修改。

下面这个元类在类的 __init____del__ 函数中添加了一个非常简单的子程序。__init__ 的子程序会把该类名的对象数量加一,而 __del__ 则会减一。

class ObjectProfilerMeta(type):
    #Just set metaclass of a class to ObjectProfilerMeta to profile object
    def __new__(cls, name, bases, attrs):
        if name.startswith('None'):
            return None

        if "__init__" in attrs:
            attrs["__init__"]=incAndCall(name,attrs["__init__"])
        else:
            attrs["__init__"]=incAndCall(name,dummyFunction)

        if "__del__" in attrs:
            attrs["__del__"]=decAndCall(name,attrs["__del__"])
        else:
            attrs["__del__"]=decAndCall(name,dummyFunction)

        return super(ObjectProfilerMeta, cls).__new__(cls, name, bases, attrs)

    def __init__(self, name, bases, attrs):
        super(ObjectProfilerMeta, self).__init__(name, bases, attrs)


    def __add__(self, other):
        class AutoClass(self, other):
            pass
        return AutoClass

incAndCall 和 decAndCall 函数使用了它们所在模块的全局变量。

counter={}
def incAndCall(name,func):
    if name not in counter:
        counter[name]=0

    def f(*args,**kwargs):
        counter[name]+=1
        func(*args,**kwargs)

    return f

def decAndCall(name,func):
    if name not in counter:
        counter[name]=0

    def f(*args,**kwargs):
        counter[name]-=1
        func(*args,**kwargs)

    return f

def dummyFunction(*args,**kwargs):
    pass

dummyFunction 只是一个非常简单的变通方法。我相信还有更好的方法可以做到这一点。

最后,每当你想查看当前存在的对象数量时,只需要查看计数器字典就可以了。举个例子;

>>> class A:
    __metaclass__=ObjectProfilerMeta
    def __init__(self):
        pass


>>> class B:
    __metaclass__=ObjectProfilerMeta


>>> l=[]
>>> for i in range(117):
    l.append(A())


>>> for i in range(18):
    l.append(B())


>>> counter
{'A': 117, 'B': 18}
>>> l.pop(15)
<__main__.A object at 0x01210CB0>
>>> counter
{'A': 116, 'B': 18}
>>> l=[]
>>> counter
{'A': 0, 'B': 0}

希望这对你有帮助。对我来说,这个方法已经足够了。

3

有一些方法可以让valgrind来分析Python程序的内存使用情况:http://www.python.org/dev/faq/#can-i-run-valgrind-against-python

8

如果你愿意分析对象而不是直接分析内存的话,可以使用 gc.get_objects() 这个函数,这样就不需要自己写一个特殊的元类了。在最近的Python版本中,sys.getsizeof() 也可以帮助你了解这些对象占用了多少底层内存。

撰写回答