如何在iPython中检查对象的内存使用情况?

42

我正在使用iPython运行代码。我想知道是否有任何模块或命令可以让我检查对象的内存使用情况。例如:

In [1]: a = range(10000)
In [2]: %memusage a
Out[2]: 1MB

类似于%memusage <object>这样的语句,返回对象使用的内存。

重复

查找Python中对象使用的内存量


重复:https://dev59.com/1XVD5IYBdhLWcg3wRpaX,https://dev59.com/TkbRa4cB1Zd3GeqP0XOI - S.Lott
还有相关内容:https://dev59.com/4XVC5IYBdhLWcg3w9GHM#159844 - Constantin
1
抱歉,我想问一下在IPython中是否有这些功能的实现或者添加“magic”函数的模块(因为我经常用它进行测试)。 - Ross
可能是如何在Python中确定对象的大小?的重复问题。 - Ciro Santilli OurBigBook.com
4个回答

65

很不幸,这是不可能的,但有一些方法可以近似计算答案:

  1. 对于非常简单的对象(例如 int、字符串、浮点数、双精度浮点数等),它们更多地表示为简单的 C 语言类型,您可以像 John Mulder 的解决方案一样简单地计算字节数。

  2. 对于更复杂的对象,一个很好的近似值是使用 cPickle.dumps 将对象序列化为字符串。该字符串的长度是存储对象所需的内存量的很好近似。

解决方案2存在一个大问题,即对象通常包含对其他对象的引用。例如,dict 包含字符串键和其他对象作为值。那些其他对象可以是共享的。由于 pickle 总是尝试完成对对象的完整序列化,因此它将始终高估存储对象所需的内存量。


2
但是,如果您将包含所有感兴趣的根对象的列表进行pickle处理,则不会出现过度估计。 - Constantin
非常感谢。但我想知道pickle是否会进行任何压缩。 - Ross
不,pickle不会压缩数据。它只是消除冗余。 - Salim Fadhley
3
SO上没有“以上”的解决方案。 - Pierre Mourlanne
10
Salim说:消除冗余是压缩的一种定义 :) 我最近尝试创建一个由10,000个相似字符串组成的数组,并使用Pickle查看内存消耗情况。它只存储一次该字符串,然后为每个重复存储一个字节。但是,这并没有告诉我关于该字符串的内部内存表示方式(它可能相同,也可能不同)。 - Stian Håklev

24

如果您正在使用numpy数组,则可以使用属性ndarray.nbytes来评估其在内存中的大小:

from pylab import *   
d = array([2,3,4,5])   
d.nbytes
#Output: 32

14

更新:这里有另一个更加详细的用于估计Python对象大小的方法。

这里有一个线程讨论类似的问题。

提出的解决方案是编写自己的代码,使用已知原始类型大小的估计、Python对象开销和内置容器类型的大小来实现。

由于代码不是很长,所以这里直接将其复制:

def sizeof(obj):
    """APPROXIMATE memory taken by some Python objects in 
    the current 32-bit CPython implementation.

    Excludes the space used by items in containers; does not
    take into account overhead of memory allocation from the
    operating system, or over-allocation by lists and dicts.
    """
    T = type(obj)
    if T is int:
        kind = "fixed"
        container = False
        size = 4
    elif T is list or T is tuple:
        kind = "variable"
        container = True
        size = 4*len(obj)
    elif T is dict:
        kind = "variable"
        container = True
        size = 144
        if len(obj) > 8:
            size += 12*(len(obj)-8)
    elif T is str:
        kind = "variable"
        container = False
        size = len(obj) + 1
    else:
        raise TypeError("don't know about this kind of object")
    if kind == "fixed":
        overhead = 8
    else: # "variable"
        overhead = 12
    if container:
        garbage_collector = 8
    else:
        garbage_collector = 0
    malloc = 8 # in most cases
    size = size + overhead + garbage_collector + malloc
    # Round to nearest multiple of 8 bytes
    x = size % 8
    if x != 0:
        size += 8-x
        size = (size + 8)
    return size

7
这是一个非常具体的解决方案。如果我们有一个大型字典列表或其他数据结构,它将无法正常工作! - user2685079

3

我一直在尝试弄清楚如何为自己做到这一点。我在此页面和其他页面上尝试了几种解决方案。然后,我进行了一些搜索,并发现了https://ipython-books.github.io/44-profiling-the-memory-usage-of-your-code-with-memory_profiler/,该网页似乎提供了一种替代方案。解决方案的要点:在ipython中使用%mprun

  1. 首先,安装memory_profiler:pip install memory_profiler
  2. 启动ipython并加载memory_profiler:%load_ext memory_profiler
  3. 创建一个物理文件中的函数,例如myfunc.py(重要提示:%mprun只能用于在物理文件中定义的函数)。在函数中创建所需对象,例如:
# myfunc.py
def myfunc():
    # create the object, e.g.
    a = [*range(10000)]
  1. 运行
from myfunc import myfunc
%mprun -T mprof -f myfunc myfunc()

生成文件mprof,同时显示内容:

Line #    Mem usage    Increment   Line Contents
================================================
     1     49.1 MiB     49.1 MiB   def myfunc():
     2                                 # create the object, e.g.
     3     49.4 MiB      0.3 MiB       a = [*range(10000)]

从第3行的增量中,我们知道a使用的内存为0.3 MiB。

让我们尝试a = [*range(100000)]

# myfunc1.py
def myfunc1():
    # create the object, e.g.
    a = [*range(100000)]

运行
from myfunc1 import myfunc1
%mprun -T mprof1 -f myfunc1 myfunc1()

Line #    Mem usage    Increment   Line Contents
================================================
     1     49.2 MiB     49.2 MiB   def myfunc1():
     2                                 # create the object, e.g.
     3     52.3 MiB      3.0 MiB       a = [*range(100000)]

看起来符合我们的预期。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接