Python内存错误未被引发,而是触发了OOM-Killer?

8
我有一个使用大量内存的Python应用程序,这应该是可以处理的,因为我在循环中使用try/except MemoryError。但不幸的是,在此之前,Python会被Debian Linux上的OOM Killer杀死,而不会引发MemoryError异常。
问题是为什么......以及我如何在Python中捕获错误。如果我能捕获它,那就很容易进行缓解,但如果没有异常,我就无法调用我的缓解措施。
提供信息,该应用程序正在处理视频,每个帧都是约15MB的numpy对象。如果我耗尽了内存,我很高兴降低帧速率并重试。
我还尝试了当我加载每个帧时跟踪内存使用情况,使用psutil.available,但该进程被杀死时仍显示约350MB可用内存(总共2GB)。我认为这是一个碎片问题。
因此,我遇到的问题是我可以任意设置一些限制,例如,如果我剩余的空闲内存小于500MB,则减慢帧速率并重新开始,但这似乎有点随意而且不太健壮。如果应用程序或可能是操作系统或硬件发生变化,则下次可能在剩余501MB时崩溃......这就是为什么我宁愿通过MemoryError异常来处理它的原因。
不幸的是,这似乎不是一个常见的问题,“python invoked oom-killer exception”只给我提供了两页谷歌搜索结果!在这里以前大多数答案都是“不要使用太多内存”,这并没有什么帮助——在我的情况下,我想尽可能使用可用的内存,但如果需要,我很乐意使用更少。只是Python在被杀死之前不给我这个机会!
非常感谢任何想法。

https://backdrift.org/oom-killer-how-to-create-oom-exclusions-in-linux 的翻译内容如下:如何在Linux中创建OOM排除项。 - user2864740
1
很遗憾,MemoryError异常从未被触发...只是猜测,但Linux会超额分配内存。在分配的时候,即使没有足够的虚拟内存可用,通常也会成功。稍后,当您尝试使用内存并且系统发现短缺时,它会调用OOM killer。如果您想在分配点失败,则可以使用Solaris。Solaris不会超额分配内存。 - jww
此外,在LWN上还有用户空间内存耗尽处理智能OOM killer的发展驯服OOM killer改进OOM killer使用mem_notify避免OOM killer等文章。LWN的文章源源不断 :) - jww
谢谢你们两位,我现在对Linux内存分配的理解比我需要的要多得多!经过阅读相关主题,我认为真正的答案实际上是没有一个“正确”的解决方案,所以我必须绕过它。Python的一个所谓的优点是跨平台性、易于移植等等,因此虽然我可以在自己的系统上进行一些更改,使其以“正确的方式”失败,但我不能期望在部署到其他地方时也能如此。因此,我将不得不制定一些其他指标作为最大内存使用量的代理... - DaveWalker
1个回答

0

NumPy倾向于将所有内容放入内存中,因此您可以检查同时可以在内存中拥有多少视频文件。

测试可加载到内存中的文件数量的“简单”方法

import numpy as np
import sys
import math
import random


def real_size(obj):
    size_bytes = sys.getsizeof(obj)
    if size_bytes == 0:
        return "0B"
    size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
    i = int(math.floor(math.log(size_bytes, 1024)))
    p = math.pow(1024, i)
    s = round(size_bytes / p, 2)
    return "%s %s" % (s, size_name[i])


def object_15():
    """
    This creates an numpy object with ~15MB
    """
    row = [random.random()] * 2080000
    data = np.array(row).astype(float)
    return data


def my_read_file(number_of_files):
    data = object_15()
    for i in range(number_of_files):
        data = np.append(data, object_15())
    return data

print(f'Just checking that each object has ~15MB: {real_size(object_15())}')
number_of_files = 100
print(f'Objects loaded into memory = {number_of_files}\nTotal memory_used = {real_size(my_read_file(number_of_files))}')

def object_15(): """ 这将创建一个大约为15MB的numpy对象 """ row = [random.random()] * 2080000 data = np.array(row).astype(float) return data

def my_read_file(number_of_files): data = object_15() for i in range(number_of_files): data = np.append(data, object_15()) return data

print(f'仅检查每个对象的大小是否约为15MB: {real_size(object_15())}') number_of_files = 100 print(f'加载到内存中的对象数= {number_of_files}\n总内存使用量= {real_size(my_read_file(number_of_files))}')

根据此测试,你应该能够拥有大约100个numpy对象,每个大小约为15MB,而不超过2GB。请根据你的特定需求/对象类型调整代码并尝试。

更多信息请参见:

你可能想考虑的其他主题包括:


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