Python,为什么mmap.move()会填满内存?

时间:2016-06-14 11:45:59

标签: python performance memory mmap

编辑:使用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()
  • 重写函数以小块移动。

1 个答案:

答案 0 :(得分: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来映射文件之前,可能值得自己寻找启动文件。

(下面的所有内容都是错误的,但是因为有评论而留下来。)

通常,使用mmap()后,必须使用munmap()撤消映射。简单地关闭文件描述符无效。 Linux documentation明确地将其调出:

  

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

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

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