Sam*_*ufi 3 python windows memory-leaks shared-memory multiprocessing
我正在使用 Python 3.8 的新shared_memory模块并且无法在不终止使用它的进程的情况下释放共享内存。
在创建和使用shm共享内存块后,我shm.close()在所有进程中关闭它,最后shm.unlink在主进程中释放它。但是,资源监视器显示内存在程序终止之前不会被释放。这对我来说是一个严重的问题,因为我的程序需要运行很长时间。可以使用以下程序在 Windows/Python 3.8 上重现该问题:
from multiprocessing import shared_memory, Pool
from itertools import repeat
from time import sleep
def fun(dummy, name):
# access shared memory
shm = shared_memory.SharedMemory(name=name)
# do work
sleep(1)
# release shared memory
shm.close()
return dummy
def meta_fun(pool):
# create shared array
arr = shared_memory.SharedMemory(create=True, size=500000000)
# compute result
result = sum(pool.starmap(fun, zip(range(10), repeat(arr.name))))
# release and free memory
arr.close()
arr.unlink()
return result
if __name__ == '__main__':
# use one Pool for many method calls to save the time for repeatedly
# creating processes
with Pool() as pool:
for i in range(100):
print(meta_fun(pool))
Run Code Online (Sandbox Code Playgroud)
注意:执行此脚本时,您可能会很快填满整个内存!观看资源监视器中的“虚拟内存”面板。
经过一番研究,我发现 (1) 该unlink()函数在 Windows 上没有任何作用:
def unlink(self):
"""Requests that the underlying shared memory block be destroyed.
In order to ensure proper cleanup of resources, unlink should be
called once (and only once) across all processes which have access
to the shared memory block."""
if _USE_POSIX and self._name:
from .resource_tracker import unregister
_posixshmem.shm_unlink(self._name)
unregister(self._name, "shared_memory")
Run Code Online (Sandbox Code Playgroud)
(2) Windows 似乎在创建/使用它的进程停止后释放共享内存(请参阅此处和此处的评论)。这可能是 Python 没有明确处理这个的原因。
作为回应,我通过重复保存和重用相同的共享内存块而不取消链接来构建一个丑陋的解决方法。显然,这不是一个令人满意的解决方案,尤其是当所需内存块的大小动态变化时。
有没有办法可以手动释放 Windows 上的共享内存?
这是multiprocessing模块中的一个错误,报告为问题 40882。有一个开放的拉取请求可以修复它,PR 20684,尽管显然合并速度很慢。
错误如下:在 中SharedMemory.__init__,我们有一个MapViewOfFile没有相应的API调用UnmapViewOfFile,并且mmap对象也不拥有它的所有权(它自己再次映射块)。
同时,您可以对shared_memory模块进行猴子补丁,以便UnmapViewOfFile在mmap构建后添加缺失的调用。您可能不得不依赖ctypes,因为尽管导出(!) ,但_winapi模块不会导出。像这样的东西(未测试):UnmapViewOfFileMapViewOfFile
import ctypes, ctypes.wintypes
import multiprocessing, multiprocessing.shared_memory
UnmapViewOfFile = ctypes.windll.kernel32.UnmapViewOfFile
UnmapViewOfFile.argtypes = (ctypes.wintypes.LPCVOID,)
UnmapViewOfFile.restype = ctypes.wintypes.BOOL
def _SharedMemory_init(self, name=None, create=False, size=0):
... # copy from SharedMemory.__init__ in the original module
try:
p_buf = _winapi.MapViewOfFile(
h_map,
_winapi.FILE_MAP_READ,
0,
0,
0
)
finally:
_winapi.CloseHandle(h_map)
try:
size = _winapi.VirtualQuerySize(p_buf)
self._mmap = mmap.mmap(-1, size, tagname=name)
finally:
UnmapViewOfFile(p_buf)
... # copy from SharedMemory.__init__ in the original module
multiprocessing.shared_memory.SharedMemory.__init__ = _SharedMemory_init
Run Code Online (Sandbox Code Playgroud)
将上述代码放入一个模块中,并记住在使用multiprocessing模块中的任何内容之前加载它。另外,您也可以直接编辑shared_memory.py在文件multiprocessing模块的目录包含所需的UnmapViewOfFile呼叫。这不是最干净的解决方案,但无论如何它都是暂时的(著名的遗言);长期的解决方案是将这个固定在上游(显然正在进行中)。
| 归档时间: |
|
| 查看次数: |
272 次 |
| 最近记录: |