【问题标题】:In python why does raising the same exception take more memory than throwing a new exception?在 python 中,为什么引发相同的异常比引发新的异常占用更多的内存?
【发布时间】:2021-09-17 08:43:39
【问题描述】:

我正在分析一些引发异常的代码,以确定两种方法中的哪一种更好,当我遇到一些似乎违反直觉的内存使用情况时。也许有人可以提供一些启示。下面的 Test1 引发了 10K 次新异常。与引发相同异常 10K 次相比,它占用的内存更少。 ???

Python 3.9

from memory_profiler import profile

TEST_COUNT = 10000


class ApplicationException(Exception):
    def __init__(self):
        self.code = 0

@profile
def test1():
    for x in range(TEST_COUNT):
        try:
            raise ApplicationException()
        except:
            pass


@profile
def test2():
    application_exception = ApplicationException()
    for x in range(TEST_COUNT):
        try:
            raise application_exception
        except:
            pass


test1()
test2()

结果是:

Line #    Mem usage    Increment  Occurences   Line Contents
_____________________________________________________________
    10     14.1 MiB     14.1 MiB           1   @profile
    11                                         def test1():
    12     14.1 MiB      0.0 MiB       10001       for x in range(TEST_COUNT):
    13     14.1 MiB      0.0 MiB       10000           try:
    14     14.1 MiB      0.0 MiB       10000               raise ApplicationException()
    15     14.1 MiB      0.0 MiB       10000           except:
    16     14.1 MiB      0.0 MiB       10000               pass
Line #    Mem usage    Increment  Occurences   Line Contents
_____________________________________________________________
    19     14.2 MiB     14.2 MiB           1   @profile
    20                                         def test2():
    21     14.2 MiB      0.0 MiB           1       application_exception = ApplicationException()
    22     14.7 MiB      0.0 MiB       10001       for x in range(TEST_COUNT):
    23     14.7 MiB      0.0 MiB       10000           try:
    24     14.7 MiB      0.5 MiB       10000               raise application_exception
    25     14.7 MiB      0.0 MiB       10000           except:
    26     14.7 MiB      0.0 MiB       10000               pass

不确定这里发生了什么。第 24 行在内存中产生了一些开销。谁能解释一下?

【问题讨论】:

  • 你试过在test1之前运行test2吗?我怀疑您正在测量进程使用的内存总量,而不是每个函数使用的内存总量。颠倒测试的顺序,我怀疑你会看到相同的结果。
  • 报告很奇怪,但也发生在我的系统上(MacOS 10.14.6,Python 3.9.5)。增量实际上发生在application_exception = ... 行上,但稍后会报告。 application_exception = ... 确实 应该会占用内存,因为您在这里存储了一个新对象。
  • @MisterMiyagi 如果我增加测试计数,第 24 行的内存使用增量会增加。 (MacOS 11.3.1,Python 3.9.1),所以不确定分配是否发生。

标签: python python-3.x exception


【解决方案1】:

在第一个测试中,您正在引发 ApplicationException 类的新实例,但您没有为其分配任何变量。

然而,在第二个测试中,您将 ApplicationException 实例分配给一个变量,而不是引发它。分配的变量具有名称等额外信息。 因此,它会使用更多内存。

【讨论】:

  • 您所描述的内容会导致如此大量的内存增加,这似乎很奇怪。在 CPython 中,局部变量存储在帧上的指针数组中——一个额外的名称应该只是从调用函数的那一刻起分配的 8 个字节,而不是提升时的 500kB。 (显然甚至有一种优化可以在代码对象上缓存一帧,因此在这种情况下可能不会创建新的帧/局部变量。)
  • 我刚查了一下,内存增加与循环运行的次数成正比。我在想循环外的上下文和循环的上下文之间可能会发生某种编组。我不知道足够多的 python 内部说。
  • 我的另一个想法是 raise 调用会添加一些堆栈跟踪信息,对于已经引发的异常实例,这些信息会导致添加一些新的东西。又说不清楚。
猜你喜欢
  • 2014-07-09
  • 1970-01-01
  • 2012-10-13
  • 2020-05-26
  • 2021-10-04
  • 2016-01-15
  • 2010-11-24
  • 2015-12-06
  • 1970-01-01
相关资源
最近更新 更多