Python:MemoryError 与 OverflowError 与即时系统冻结
Python: MemoryError vs OverflowError vs instant-system-freeze
最后一天,解决了另一个 Project Euler 我在管理 大 for i in range(n)
周期时遇到了麻烦。
我观察到 python
抛出不同的错误,取决于 x
变量 的大小。
这是一个 mcve 示例:
for i in range(x):
pass
其中:
如果x = 10**20
我得到一个OverflowError
,
确切地说:OverflowError: range() result has too many items
.
else if x = 10**15
我得到一个 MemoryError
.
else if x = 10**9
我有一个 instant-system-freeze
,我必须硬重置我的电脑。在极少数情况下,我的电脑没有死机,而是填满了大约 5GB 的交换空间,变得非常慢。
我试图理解pythonbuilt-in exception的意思:
-
Raised when the result of an arithmetic operation is too large to be represented. This cannot occur for long integers [...] and for most operations with plain integers, which return a long integer instead. [...]
这意味着整数不应该抛出这个异常;这个异常的原因是 range()
有太多的项目,所以我想 10**15
也会抛出同样的异常,但我有一个不同的......
-
Raised when an operation runs out of memory but the situation may still be rescued (by deleting some objects). [...]
我应该删除哪个对象来挽救这种情况?直接退出,无可救药...
如果它 感觉 有太多 ram
使用,为什么它冻结我的电脑 10**9
?
最后,我的问题是:
为什么我得到 3 个不同的结果,仅取决于存储在 x
变量中的值?
注意:
好吧,range
尝试构建一个 完整的 列表,其中包含与 x
.
一样多的项目
对于死亡即时交换情况,你可以估计,假设64位和一个固定的8字节整数,x = 10**9
是值得的大约8点。因此,如果您的内存不多(考虑到已使用的 RAM),您就会明白系统交换的原因。
再往下看,如果函数无法分配足够大的块来保存结果(在 10**15
的情况下看起来相当大),则可能会引发 MemoryError
。
我不知道 range
的实现细节,但它可能使用 OverflowError
来确保元素的绝对最大数量(可能是为了防止MemoryError
,基于实际可用内存)。
正如@ShadowRanger 在评论中提到的,如果结果的长度不能放入 size_t
变量(2**31
(32 位)或 2**63
( 64 位)) 因为它无法初始化这样的列表。
正如您所提到的,xrange 没有这个问题,因为它不会生成整个列表,而是在您每次迭代时生成一个值。这就是 iterator/generator 内存效率高的原因。
所以我快速浏览了一下,您可以看到 in the 2.7 source 为什么会抛出 OverflowError
。
range(x)
将首先创建一个大小为 x
的列表,然后用整数填充它。效果是:
x = 10**20
:OverflowError, because a list can only hold sys.maxsize
(通常 2**31 - 1
或 2**63 - 1
分别用于 32 位和 64 位版本)项。
x = 10**15
: MemoryError,因为内存不足,无法分配大小为 x 的(未初始化的)列表。
x = 10**9
:将创建一个大小为 x 的列表;然后对于每个整数i < x
,一个新的int对象将被创建并写入磁盘;每个都需要内存分配。 (这最终可能会或可能不会导致 MemoryError。)这会给 CPU 和内存带来很大的负担,从而降低您的机器速度。
总而言之,对于前两种情况,python 很明显任务无法完成,它会引发异常。在第三种情况下,它认为它可能能够完成任务,尽管速度很慢。
最后一天,解决了另一个 Project Euler 我在管理 大 for i in range(n)
周期时遇到了麻烦。
我观察到 python
抛出不同的错误,取决于 x
变量 的大小。
这是一个 mcve 示例:
for i in range(x):
pass
其中:
如果
x = 10**20
我得到一个OverflowError
, 确切地说:OverflowError: range() result has too many items
.else if
x = 10**15
我得到一个MemoryError
.else if
x = 10**9
我有一个instant-system-freeze
,我必须硬重置我的电脑。在极少数情况下,我的电脑没有死机,而是填满了大约 5GB 的交换空间,变得非常慢。
我试图理解pythonbuilt-in exception的意思:
-
Raised when the result of an arithmetic operation is too large to be represented. This cannot occur for long integers [...] and for most operations with plain integers, which return a long integer instead. [...]
这意味着整数不应该抛出这个异常;这个异常的原因是
range()
有太多的项目,所以我想10**15
也会抛出同样的异常,但我有一个不同的...... -
Raised when an operation runs out of memory but the situation may still be rescued (by deleting some objects). [...]
我应该删除哪个对象来挽救这种情况?直接退出,无可救药...
如果它 感觉 有太多
ram
使用,为什么它冻结我的电脑10**9
?
最后,我的问题是:
为什么我得到 3 个不同的结果,仅取决于存储在 x
变量中的值?
注意:
好吧,range
尝试构建一个 完整的 列表,其中包含与 x
.
对于死亡即时交换情况,你可以估计,假设64位和一个固定的8字节整数,x = 10**9
是值得的大约8点。因此,如果您的内存不多(考虑到已使用的 RAM),您就会明白系统交换的原因。
再往下看,如果函数无法分配足够大的块来保存结果(在 10**15
的情况下看起来相当大),则可能会引发 MemoryError
。
我不知道 range
的实现细节,但它可能使用 OverflowError
来确保元素的绝对最大数量(可能是为了防止MemoryError
,基于实际可用内存)。
正如@ShadowRanger 在评论中提到的,如果结果的长度不能放入 size_t
变量(2**31
(32 位)或 2**63
( 64 位)) 因为它无法初始化这样的列表。
正如您所提到的,xrange 没有这个问题,因为它不会生成整个列表,而是在您每次迭代时生成一个值。这就是 iterator/generator 内存效率高的原因。
所以我快速浏览了一下,您可以看到 in the 2.7 source 为什么会抛出 OverflowError
。
range(x)
将首先创建一个大小为 x
的列表,然后用整数填充它。效果是:
x = 10**20
:OverflowError, because a list can only holdsys.maxsize
(通常2**31 - 1
或2**63 - 1
分别用于 32 位和 64 位版本)项。x = 10**15
: MemoryError,因为内存不足,无法分配大小为 x 的(未初始化的)列表。x = 10**9
:将创建一个大小为 x 的列表;然后对于每个整数i < x
,一个新的int对象将被创建并写入磁盘;每个都需要内存分配。 (这最终可能会或可能不会导致 MemoryError。)这会给 CPU 和内存带来很大的负担,从而降低您的机器速度。
总而言之,对于前两种情况,python 很明显任务无法完成,它会引发异常。在第三种情况下,它认为它可能能够完成任务,尽管速度很慢。