NumPy数组内存管理

7
我有一个关于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个回答

7
以下内容应该能够澄清一些问题:
>>> 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>

在第二种情况下,当你删除变量t指向的buffer对象时,但由于数组仍然引用同一buffer对象,它并没有被删除。虽然我不确定如何检查它,但如果你现在删除arrbuffer对象应该失去它的最后一个引用并自动进行垃圾回收。

在CPython中,您可以使用sys.getrefcount来观察s在两种情况下的引用计数增加。虽然这并不重要,但它当然有效。 - seberg

1
为了补充@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() 中"


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