【问题标题】:Memory leak(ish?) when using re and mmap使用 re 和 mmap 时的内存泄漏(ish?)
【发布时间】:2015-11-04 22:32:20
【问题描述】:

我在 python 3.4(64 位 Windows)中跨一个大(30-ish GB)mmapped 文件运行正则表达式搜索时遇到“问题”。

基本上,我观察到的是 between 匹配命中,内存占用大致上升到匹配之间的字节数。它实际上并没有崩溃,但占用空间大到足以减慢其他进程的速度(因为映射文件的大小)。

我的正则表达式来自字节字符串,它非常具体,所有量词都是有界的。在我的表达式中没有*+,所以这不是一个可怕的超出正则表达式的情况(最坏的情况下匹配将是2200 字节长,大多数命中更小)。我将匹配的字符串存储在一个列表中,但通常最多只有几千个匹配项,因此占用所有空间的不是匹配项。

我目前的假设是正则表达式引擎(sre 对吗?)将所有匹配项之间的字符串保存在内存中,这对于小型数据集很好,但对我来说并非如此。所以我的问题真的是:这个假设是否正确,如果是,我可以改变这种行为(最好不重新编译库!)

代码基本上是这样的:

pattern = re.compile(b"PATTERN.{1,20}", re.DOTALL)
f = open("file.bin", "rb")
mem = mmap.map(f.fileno(), 0, access=mmap.ACCESS_READ)

results = []
for match in pattern.finditer(mem):
    results.append(match.group(0))

f.close()

【问题讨论】:

  • 很难说。微软喜欢滥用缓存,所以可能部分文件被windows缓存在内存中。
  • 我不确定这是 Windows 的错(或不完全是 Windows 的错)。每次匹配时内存占用量都会下降的行为感觉就像它来自库中......我需要在 unix 风格的盒子上运行这个测试......

标签: python regex python-3.x mmap


【解决方案1】:

我不确定有没有办法解决这个问题。您正在以磁盘所能提供的速度读取大量数据。除非你有大量的 RAM。如果您在某些时候不这样做,那么您将用完 RAM,必须释放一些内存。大多数操作系统将使用 LRU(最近最少使用)算法来决定从 RAM 中踢出什么。由于您正在尽可能快地访问数据,因此内存映射文件使用的大部分内存将具有最近的访问时间。因此,这意味着他们是被踢出 RAM 的“差”候选人(至少根据操作系统)。

基本上,操作系统在可用内存不足时选择从 RAM 中取出什么内容的选择很糟糕。

但是,您更了解可以释放哪些内存。因此,您可以分块扫描文件。当您不再需要文件的早期部分时,这将显式地让操作系统,并允许释放该内存。当然,这会在块的边界产生问题。

作为一个示例,说明您可以采取哪些措施来提高程序的内存性能:

import re
import mmap
import os

filename = "some_file.txt"
file_size = os.stat(filename).st_size
chunk_size = 2**32
# chunk_size = 50 # smaller chunk_size I used for testing
regex = re.compile(rb"PATTERN\d{1,20}\n")
max_length = len("PATTERN") + 20 + len("\n")

matches = []
f = open(filename, "rb")    
for i in range(0, file_size, chunk_size - max_length + 1):
    # compute length of data to search over
    length = chunk_size if i + chunk_size <= file_size else file_size - i 

    m = mmap.mmap(f.fileno(), length=length, offset=i, access=mmap.ACCESS_READ)
    # f.seek(i) # used for testing
    # m = f.read(length)

    for match in regex.finditer(m):
        if not (match.end() == len(m) and len(match.group()) < max_length and length == chunk_size):
            # if match ends at end of string
            # and not maximum length of regex
            # but not also at the end of the file
            # THEN there *may* be a cross chunk-boundary match
            # THUS, defer match to next loop iteration
            matches.append(match.group())
    m.close()
f.close()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-18
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    • 2013-10-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多