为了账号安全,请及时绑定邮箱和手机立即绑定

如何在Python中分析内存使用情况?

/ 猿问

如何在Python中分析内存使用情况?

料青山看我应如是 2019-07-22 15:35:01

如何在Python中分析内存使用情况?

最近,我开始对算法感兴趣,并开始探索算法,编写一个简单的实现,然后以各种方式对其进行优化。

我已经熟悉了用于分析运行时的标准Python模块(对于大多数情况,我发现IPython中的timeit魔术函数已经足够了),但我也对内存的使用感兴趣,因此我也可以探索这些权衡(例如,缓存一个表的先前计算的值与根据需要重新计算它们的成本)。是否有一个模块可以为我分析给定函数的内存使用情况?


查看完整描述

3 回答

?
倚天杖

这个问题已经在这里得到了答复:Python内存分析器

基本上你做了类似的事情(引用自格皮):

>>> from guppy import hpy; h=hpy()

>>> h.heap()

Partition of a set of 48477 objects. Total size = 3265516 bytes.

 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)

     0  25773  53  1612820  49   1612820  49 str

     1  11699  24   483960  15   2096780  64 tuple

     2    174   0   241584   7   2338364  72 dict of module

     3   3478   7   222592   7   2560956  78 types.CodeType

     4   3296   7   184576   6   2745532  84 function

     5    401   1   175112   5   2920644  89 dict of class

     6    108   0    81888   3   3002532  92 dict (no owner)

     7    114   0    79632   2   3082164  94 dict of type

     8    117   0    51336   2   3133500  96 type

     9    667   1    24012   1   3157512  97 __builtin__.wrapper_descriptor

<76 more rows. Type e.g. '_.more' to view.>

>>> h.iso(1,[],{})

Partition of a set of 3 objects. Total size = 176 bytes.

 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)

     0      1  33      136  77       136  77 dict (no owner)

     1      1  33       28  16       164  93 list

     2      1  33       12   7       176 100 int

>>> x=[]

>>> h.iso(x).sp

 0: h.Root.i0_modules['__main__'].__dict__['x']

>>> 


查看完整回答
反对 回复 2019-07-22
?
慕仙森

Python 3.4包括一个新模块:tracemalloc..它提供了关于哪些代码分配内存最多的详细统计信息。下面是一个显示分配内存的前三行的示例。

from collections import Counterimport linecacheimport osimport tracemallocdef display_top(snapshot, key_type='lineno', limit=3):
    snapshot = snapshot.filter_traces((
        tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
        tracemalloc.Filter(False, "<unknown>"),
    ))
    top_stats = snapshot.statistics(key_type)

    print("Top %s lines" % limit)
    for index, stat in enumerate(top_stats[:limit], 1):
        frame = stat.traceback[0]
        # replace "/path/to/module/file.py" with "module/file.py"
        filename = os.sep.join(frame.filename.split(os.sep)[-2:])
        print("#%s: %s:%s: %.1f KiB"
              % (index, filename, frame.lineno, stat.size / 1024))
        line = linecache.getline(frame.filename, frame.lineno).strip()
        if line:
            print('    %s' % line)

    other = top_stats[limit:]
    if other:
        size = sum(stat.size for stat in other)
        print("%s other: %.1f KiB" % (len(other), size / 1024))
    total = sum(stat.size for stat in top_stats)
    print("Total allocated size: %.1f KiB" % (total / 1024))tracemalloc.start()counts = Counter()fname = '/usr/share/dict/american-english'with open(fname) as words:
    words = list(words)
    for word in words:
        prefix = word[:3]
        counts[prefix] += 1print('Top prefixes:', counts.most_common(3))snapshot = tracemalloc.take_snapshot()display_top(snapshot)

结果如下:

Top prefixes: [('con', 1220), ('dis', 1002), ('pro', 809)]Top 3 lines#1: scratches/memory_test.py:37: 6527.1 KiB
    words = list(words)#2: scratches/memory_test.py:39: 247.7 KiB
    prefix = word[:3]#3: scratches/memory_test.py:40: 193.0 KiB
    counts[prefix] += 14 other: 4.3 KiBTotal allocated size: 6972.1 KiB

什么时候是内存泄漏而不是泄漏?

当内存仍然保存在计算的末尾时,这个例子是很棒的,但是有时您的代码分配了大量内存,然后全部释放出来。从技术上讲,这不是内存泄漏,但它使用的内存比您认为的要多。当所有内存都被释放时,如何跟踪内存的使用情况?如果是您的代码,您可能可以在运行时添加一些调试代码来获取快照。如果没有,则可以在主线程运行时启动后台线程来监视内存使用情况。

下面是前面的示例,代码都已移到count_prefixes()功能。当该函数返回时,将释放所有内存。我也加了一些sleep()调用来模拟长期运行的计算。

from collections import Counterimport linecacheimport osimport tracemallocfrom time import sleepdef count_prefixes():
    sleep(2)  # Start up time.
    counts = Counter()
    fname = '/usr/share/dict/american-english'
    with open(fname) as words:
        words = list(words)
        for word in words:
            prefix = word[:3]
            counts[prefix] += 1
            sleep(0.0001)
    most_common = counts.most_common(3)
    sleep(3)  # Shut down time.
    return most_commondef main():
    tracemalloc.start()

    most_common = count_prefixes()
    print('Top prefixes:', most_common)

    snapshot = tracemalloc.take_snapshot()
    display_top(snapshot)def display_top(snapshot, key_type='lineno', limit=3):
    snapshot = snapshot.filter_traces((
        tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
        tracemalloc.Filter(False, "<unknown>"),
    ))
    top_stats = snapshot.statistics(key_type)

    print("Top %s lines" % limit)
    for index, stat in enumerate(top_stats[:limit], 1):
        frame = stat.traceback[0]
        # replace "/path/to/module/file.py" with "module/file.py"
        filename = os.sep.join(frame.filename.split(os.sep)[-2:])
        print("#%s: %s:%s: %.1f KiB"
              % (index, filename, frame.lineno, stat.size / 1024))
        line = linecache.getline(frame.filename, frame.lineno).strip()
        if line:
            print('    %s' % line)

    other = top_stats[limit:]
    if other:
        size = sum(stat.size for stat in other)
        print("%s other: %.1f KiB" % (len(other), size / 1024))
    total = sum(stat.size for stat in top_stats)
    print("Total allocated size: %.1f KiB" % (total / 1024))main()

当我运行该版本时,内存使用量从6MB降到了4KB,因为函数完成时释放了它的所有内存。

Top prefixes: [('con', 1220), ('dis', 1002), ('pro', 809)]Top 3 lines#1: collections/__init__.py:537: 0.7 KiB
    self.update(*args, **kwds)#2: collections/__init__.py:555: 0.6 KiB
    return _heapq.nlargest(n, self.items(), key=_itemgetter(1))#3: python3.6/heapq.py:569: 0.5 KiB
    result = [(key(elem), i, elem) for i, elem in zip(range(0, -n, -1), it)]10 other: 2.2 KiBTotal allocated size: 4.0 KiB

下面是一个灵感来自另一个答案这将启动第二个线程来监视内存使用情况。

from collections import Counterimport linecacheimport osimport tracemallocfrom datetime import datetimefrom queue import Queue, Emptyfrom resource import getrusage, RUSAGE_SELFfrom threading import Threadfrom time import sleepdef memory_monitor(command_queue: Queue, poll_interval=1):
    tracemalloc.start()
    old_max = 0
    snapshot = None
    while True:
        try:
            command_queue.get(timeout=poll_interval)
            if snapshot is not None:
                print(datetime.now())
                display_top(snapshot)

            return
        except Empty:
            max_rss = getrusage(RUSAGE_SELF).ru_maxrss            if max_rss > old_max:
                old_max = max_rss
                snapshot = tracemalloc.take_snapshot()
                print(datetime.now(), 'max RSS', max_rss)def count_prefixes():
    sleep(2)  # Start up time.
    counts = Counter()
    fname = '/usr/share/dict/american-english'
    with open(fname) as words:
        words = list(words)
        for word in words:
            prefix = word[:3]
            counts[prefix] += 1
            sleep(0.0001)
    most_common = counts.most_common(3)
    sleep(3)  # Shut down time.
    return most_commondef main():
    queue = Queue()
    poll_interval = 0.1
    monitor_thread = Thread(target=memory_monitor, args=(queue, poll_interval))
    monitor_thread.start()
    try:
        most_common = count_prefixes()
        print('Top prefixes:', most_common)
    finally:
        queue.put('stop')
        monitor_thread.join()def display_top(snapshot, key_type='lineno', limit=3):
    snapshot = snapshot.filter_traces((
        tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
        tracemalloc.Filter(False, "<unknown>"),
    ))
    top_stats = snapshot.statistics(key_type)

    print("Top %s lines" % limit)
    for index, stat in enumerate(top_stats[:limit], 1):
        frame = stat.traceback[0]
        # replace "/path/to/module/file.py" with "module/file.py"
        filename = os.sep.join(frame.filename.split(os.sep)[-2:])
        print("#%s: %s:%s: %.1f KiB"
              % (index, filename, frame.lineno, stat.size / 1024))
        line = linecache.getline(frame.filename, frame.lineno).strip()
        if line:
            print('    %s' % line)

    other = top_stats[limit:]
    if other:
        size = sum(stat.size for stat in other)
        print("%s other: %.1f KiB" % (len(other), size / 1024))
    total = sum(stat.size for stat in top_stats)
    print("Total allocated size: %.1f KiB" % (total / 1024))main()

这个resource模块允许您检查当前内存使用情况,并从峰值内存使用量中保存快照。队列允许主线程告诉内存监视器线程何时打印其报表并关闭。运行时,它将显示list()呼叫:

2018-05-29 10:34:34.441334 max RSS 101882018-05-29 10:34:36.475707 max RSS 235882018-05-29 10:34:36.616524 max RSS 381042018-05-29 10:34:36.772978 max RSS 459242018-05-29 10:34:36.929688 max RSS 468242018-05-29 10:34:37.087554 max RSS 46852Top prefixes: [('con', 1220), ('dis', 1002), ('pro', 809)]2018-05-29 10:34:56.281262Top 3 lines#1: scratches/scratch.py:36: 6527.0 KiB
    words = list(words)#2: scratches/scratch.py:38: 16.4 KiB
    prefix = word[:3]#3: scratches/scratch.py:39: 10.1 KiB
    counts[prefix] += 119 other: 10.8 KiBTotal allocated size: 6564.3 KiB

如果你在Linux上,你会发现/proc/self/statmresource模块。




查看完整回答
反对 回复 2019-07-22
?
函数式编程

如果只想查看对象的内存使用情况,(对其他问题的回答)

有一个模块名为打火机包含asizeof模块。

用途如下:

from pympler import asizeof
asizeof.asizeof(my_object)

不像sys.getsizeof,它适用于您自己创建的对象。.

>>> asizeof.asizeof(tuple('bcd'))200>>> asizeof.asizeof({'foo': 'bar', 'baz': 'bar'})400>>> asizeof.asizeof({})280>>> asizeof.asizeof({'foo':'bar'})360>>> asizeof.asizeof('foo')40>>> asizeof.asizeof(Bar())352>>> asizeof.asizeof(Bar().__dict__)280
>>> help(asizeof.asizeof)Help on function asizeof in module pympler.asizeof:asizeof(*objs, **opts)
    Return the combined size in bytes of all objects passed as positional arguments.



查看完整回答
反对 回复 2019-07-22

添加回答

回复

举报

0/150
提交
取消
意见反馈 邀请有奖 帮助中心 APP下载
官方微信