Numpy阵列内存管理

时间:2013-02-13 23:13:45

标签: arrays memory numpy

我对Numpy阵列内存管理有疑问。假设我使用以下内容从缓冲区创建一个numpy数组:

>>> s = "abcd"
>>> arr = numpy.frombuffer(buffer(s), dtype = numpy.uint8)
>>> arr.flags
  C_CONTIGUOUS : True
  F_CONTIGUOUS : True
  OWNDATA : False
  WRITEABLE : False
  ALIGNED : True
  UPDATEIFCOPY : False
>>> del s # What happens to arr?

在上面的情况中,'arr'是否持有's'的引用?如果我删除's',这会释放为's'分配的内存,从而使'arr'可能引用未分配的内存吗?

我有其他一些问题:

  • 如果这是有效的,Python如何知道何时释放's'分配的内存? gc.get_referrents(arr)函数似乎没有显示'arr'包含对's'的引用。
  • 如果这是无效的,我如何将's'的引用注册到'arr',以便Python GC在所有引用都消失后自动获得's'?

2 个答案:

答案 0 :(得分:6)

以下内容应该澄清一点:

>>> s = 'abcd'
>>> arr = np.frombuffer(buffer(s), dtype='uint8')
>>> arr.base
<read-only buffer for 0x03D1BA60, size -1, offset 0 at 0x03D1BA00>
>>> del s
>>> arr.base
<read-only buffer for 0x03D1BA60, size -1, offset 0 at 0x03D1BA00>

在第一种情况下del s没有效果,因为数组指向的是从它创建的buffer,在其他地方没有引用。

>>> t = buffer('abcd')
>>> arr = np.frombuffer(t, dtype='uint8')
>>> arr.base
<read-only buffer for 0x03D1BA60, size -1, offset 0 at 0x03C8D920>
>>> arr.base is t
True
>>> del t
>>> arr.base
<read-only buffer for 0x03D1BA60, size -1, offset 0 at 0x03C8D920>

在第二种情况下,当你del t时,你摆脱了指向t对象的变量buffer,但因为数组仍然引用了同一个buffer del arr 1}},它不会被删除。虽然我不确定如何检查它,但如果您现在buffer,则{{1}}对象应该丢失其最后一个引用并自动进行垃圾回收。

答案 1 :(得分:0)

为了补充@seberg 的评论:

    import ctypes
    import sys

    import numpy as np

    b = bytearray([1, 2, 3])
    b_addr = id(b)
    print(sys.getrefcount(b) - 1, ctypes.c_long.from_address(b_addr).value)  # => 1 1
    a1 = np.frombuffer(b, dtype=np.int8)
    assert b[0] == a1[0]
    b[0] = b[0] + 1
    assert b[0] == a1[0]
    print(sys.getrefcount(b) - 1, ctypes.c_long.from_address(b_addr).value)  # => 2 2
    a2 = np.frombuffer(b, dtype=np.int8)
    print(sys.getrefcount(b) - 1, ctypes.c_long.from_address(b_addr).value)  # => 3 3
    del a2
    print(sys.getrefcount(b) - 1, ctypes.c_long.from_address(b_addr).value)  # => 2 2
    del b
    print(ctypes.c_long.from_address(b_addr).value)  # => 1
    del a1
    print(ctypes.c_long.from_address(b_addr).value)  # => 0

sys.getrefcount(b) 返回更高的值“因为它包含(临时)引用作为 getrefcount() 的参数”