Jupyter 笔记本内存管理

Jupyter Notebook Memory Management

我目前正在使用 kaggle 开发 jupyter notebook。在我的 numpy 阵列上执行所需的转换后,我将其腌制以便可以将其存储在磁盘上。我这样做的原因是我可以释放大数组消耗的内存。

酸洗阵列后消耗的内存约为 8.7 GB。

我决定 运行 @jan-glx here 提供的这个代码片段,找出哪些变量在消耗我的内存:

import sys

def sizeof_fmt(num, suffix='B'):
    ''' by Fred Cirera,   modified'''
    for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
        if abs(num) < 1024.0:
            return "%3.1f %s%s" % (num, unit, suffix)
        num /= 1024.0
    return "%.1f %s%s" % (num, 'Yi', suffix)

for name, size in sorted(((name, sys.getsizeof(value)) for name, value in locals().items()),
                         key= lambda x: -x[1])[:10]:
    print("{:>30}: {:>8}".format(name, sizeof_fmt(size)))

执行此步骤后,我注意到我的数组大小为 3.3 gb,所有其他变量加在一起的大小约为 0.1 gb。

我决定通过执行以下操作删除数组并查看是否可以解决问题:

del my_array
gc.collect()

执行此操作后,内存消耗从 8.7 GB 减少到 5.4 GB。这在理论上是有道理的,但仍然没有解释剩余内存被什么消耗了。

我决定无论如何都要继续并重置所有变量以查看这是否会释放内存:

%reset

正如预期的那样,它释放了上面函数中打印出的变量的内存,我仍然有 5.3 GB 的内存在使用中。

需要注意的一件事是,我注意到在 pickle 文件本身时出现内存峰值,因此该过程的摘要如下所示:

  1. 对数组执行操作 -> 内存消耗从大约 1.9 gb 增加到 5.6 gb
  2. pickled 文件 -> 内存消耗从 5.6 gb 增加到大约 8.7 gb
  3. 当文件被 pickle 到 15.2 GB 然后回落到 8.7 GB 时,内存突然飙升。
  4. 已删除数组 -> 内存消耗从 8.7 gb 减少到 5.4 gb
  5. 执行重置 -> 内存消耗从 5.4 gb 减少到 5.3 gb

请注意,以上内容粗略地基于对 kaggle 内存的监控,可能不准确。 我也检查了这个 但它对我的情况没有帮助。

这会被认为是内存泄漏吗?如果是这样,在这种情况下我该怎么办?

编辑 1:

经过进一步挖掘,我注意到有 others 面临这个问题。这个问题源于酸洗过程,酸洗会在内存中创建一个副本,但由于某种原因不会释放它。酸洗过程完成后有没有办法释放内存。

编辑 2:

从磁盘中删除 pickled 文件时,使用:

!rm my_array 

它最终释放了磁盘 space 并释放了 space 内存。我不知道上面的花絮是否有用,但我还是决定把它包括在内,因为每一点信息都可能有所帮助。

您应该注意一个基本缺点:CPython 解释器实际上 can actually barely free memory and return it to the OS. For most workloads, you can assume that memory is not freed during the lifetime of the interpreter's process. However, the interpreter can re-use the memory internally. So looking at the memory consumption of the CPython process from the operating system's perspective really does not help at all. A rather common work-around is to run memory intensive jobs in a sub-process / worker process (via multiprocessing 例如)并且“仅”return 将结果发送给主进程。一旦 worker 死亡,内存实际上被释放了。

其次,在 ndarray 上使用 sys.getsizeof 可能会产生令人印象深刻的误导。请改用 ndarray.nbytes 属性 并注意在处理 views.

时这也可能会产生误导

此外,我不完全确定您为什么“挑选”numpy 数组。这项工作有更好的工具。仅举两个例子:h5py (a classic, based on HDF5) and zarr. Both libraries allow you to work with ndarray-like objects directly on disk (and compression) - essentially eliminating the pickling step. Besides, zarr also allows you to create compressed ndarray-compatible data structures in memory。必须 ufunc 来自 numpy,scipy & 朋友们会很乐意接受它们作为输入参数。