【问题标题】:Python, why does mmap.move() fill up the memory?Python,为什么 mmap.move() 会填满内存?
【发布时间】:2016-10-15 03:11:04
【问题描述】:

编辑:使用 Win10 和 python 3.5

我有一个函数,它使用 mmap 从文件中删除某个偏移量的字节:

def delete_bytes(fobj, offset, size):
    fobj.seek(0, 2)
    filesize = fobj.tell()
    move_size = filesize - offset - size

    fobj.flush()
    file_map = mmap.mmap(fobj.fileno(), filesize)
    file_map.move(offset, offset + size, move_size)
    file_map.close()

    fobj.truncate(filesize - size)
    fobj.flush()

它运行得非常快,但是当我在大量文件上运行它时,内存很快就会填满,我的系统变得无响应。

经过一些实验,我发现 move() 方法是这里的罪魁祸首,尤其是被移动的数据量(move_size)。 正在使用的内存量等于mmap.move() 移动的数据总量。 如果我有 100 个文件,每移动约 30 MB,则内存将充满约 3GB。

为什么移动的数据没有从内存中释放出来?

我尝试过的没有效果的事情:

  • 在函数结束时调用gc.collect()
  • 重写函数以小块移动。

【问题讨论】:

  • 您使用的是什么操作系统? Python 版本也是如此。
  • 能否请您检查一下您的python进程或操作系统是否使用了内存?
  • 对不起,忘了说:我在 Win10 和 python 3.5 上。如何检查内存是否被python或OS使用?
  • Windows 10 不包含任务管理器系统实用程序吗?
  • 操作系统倾向于将已经使用过的页面保留在缓冲区缓存中,因为通常它们很可能会再次使用。也许您的缓冲区缓存已满。还;你真的在某处关闭文件对象吗?

标签: python performance memory mmap


【解决方案1】:

这似乎应该工作。我确实在 mmapmodule.c 源代码#ifdef MS_WINDOWS 中发现了一处可疑之处。具体来说,在解析参数的所有设置之后,代码会执行以下操作:

if (fileno != -1 && fileno != 0) {
    /* Ensure that fileno is within the CRT's valid range */
    if (_PyVerify_fd(fileno) == 0) {
        PyErr_SetFromErrno(PyExc_OSError);
        return NULL;
    }
    fh = (HANDLE)_get_osfhandle(fileno);
    if (fh==(HANDLE)-1) {
        PyErr_SetFromErrno(PyExc_OSError);
        return NULL;
    }
    /* Win9x appears to need us seeked to zero */
    lseek(fileno, 0, SEEK_SET);
}

将底层文件对象的偏移量从“文件结尾”移动到“文件开头”,然后将其保留在那里。这似乎不应该破坏任何东西,但在调用mmap.mmap 映射文件之前可能值得自己寻找文件开始。

(下面都是错的,因为上面有cmets所以留下了。)


一般情况下,使用mmap()后,必须使用munmap()来撤消映射。简单地关闭文件描述符没有任何效果。 Linux documentation 明确指出这一点:

munmap()
munmap() 系统调用会删除指定地址范围的映射,并导致对该范围内地址的进一步引用生成无效的内存引用。当进程终止时,该区域也会自动取消映射。另一方面,关闭文件描述符并不会取消映射该区域。

(BSD 文档类似。Windows 的行为可能与此处的类 Unix 系统不同,但您所看到的表明它们的工作方式相同。)

不幸的是,至少在 2.7.11 和 3.4.4 中,Python 的 mmap 模块不绑定 munmap 系统调用(也不绑定 mprotect)。作为一种解决方法,您可以使用ctypes 模块。有关示例,请参见 this question(它调用 reboot,但相同的技术适用于所有 C 库函数)。或者,对于更好的方法,您可以在 中编写包装器。

【讨论】:

  • mmap.close() 不是在下面执行unmap() 吗?
  • mmap.close() 确实调用 UnmapViewOfFile (windows) 或 munmap (unix) (python 3.4, mmapmodule.c)。
  • 映射本身没有问题。如果我删除带有mmap.move() 的行或将其替换为另一种方法(如mmap.resize()),则完全没有问题。
  • @J.J.Hakala:有趣;我的首选 Python (FreeBSD) 根本没有 mmap.close。 @mahkitah:如果您不调用 mmap.move(我也没有,但我想它会变成 C 库 memmove 调用)或以其他方式“触摸”内存,它不会出现页面错误首先。在 Unix-y 系统上,我会尝试 stracektrace 或任何其他系统调用跟踪工具,以查看是否正在调用 OS un-map 函数。
  • @J.J.Hakala:哦,我错过了明显的:不是mmap.close(),而是mmap().close()
猜你喜欢
  • 1970-01-01
  • 2016-12-04
  • 1970-01-01
  • 2015-09-14
  • 2014-10-28
  • 2020-10-26
  • 1970-01-01
  • 1970-01-01
  • 2018-03-06
相关资源
最近更新 更多