什么是python管理大变量分配/释放的策略?

问题描述 投票:3回答:1

作为this question的后续行动,似乎在(C)Python中存在针对小变量和大变量的不同分配/解除分配策略。 更确切地说,在对象大小上似乎存在边界,在该边界之上,分配的对象使用的存储器可以被返回给OS。低于此大小,内存不会返回给操作系统。

引用Numpy释放记忆政策的答案:

例外情况是,对于大型单个分配(例如,如果您创建一个多兆字节数组),则使用不同的机制。这样大的内存分配可以释放回操作系统。所以它可能特别是你的程序的非numpy部分产生你看到的问题。

实际上,这两种分配策略很容易显示出来。例如:

  • 第一个策略:没有内存返回给操作系统
import numpy as np
import psutil
import gc

# Allocate  array
x = np.random.uniform(0,1, size=(10**4))

# gc
del x
gc.collect()
# We go from 41295.872 KB to 41295.872 KB
# using psutil.Process().memory_info().rss / 10**3; same behavior for VMS

=>没有内存返回给操作系统

  • 第二个策略:释放的内存被返回给操作系统

在进行相同的实验时,但是使用更大的数组:

x = np.random.uniform(0,1, size=(10**5))

del x
gc.collect()
# We go from 41582.592 KB to 41017.344 KB

=>内存被释放到操作系统

似乎使用第二策略分配大约比8*10**4字节大的对象。

所以:

  • 这种行为是否有记录? (分配策略改变的确切边界是什么?)
  • 这些策略的内部结构是什么(比假设使用mmap / munmap将内存释放回操作系统更多)
  • 这是100%由Python运行时完成还是Numpy有一个特定的方法来处理它? (numpy doc提到在内存分配器之间切换的NPY_USE_PYMEM
python numpy cpython
1个回答
2
投票

您观察到的不是CPython的策略,而是CPython版本使用的C-runtime附带的内存分配器策略。

当CPython通过malloc/free分配/释放内存时,它不会直接与底层操作系统通信,而是具有内存分配器的具体实现。在我的Linux案例中,它是the GNU Allocator

GNU分配器具有不同的所谓竞技场,其中内存不会返回到操作系统,但保留以便可以在不需要与操作系统通信的情况下重复使用。但是,如果请求大量内存(无论“大”的定义),分配器不使用竞技场中的内存,而是从操作系统请求内存,因此一旦free是,就可以直接将其返回给操作系统调用。


CPython有自己的内存分配器--pymalloc,它是在C-runtime-allocator的基础上构建的。它针对小型物体进行了优化,这些物体生活在一个特殊的舞台上;与底层C-runtime-allocator相比,创建/释放这些对象的开销更少。但是,大于512字节的对象不使用此竞技场,而是由C-runtime-allocator直接管理。

numpy的数组情况更加复杂,因为不同的内存分配器用于元数据(如形状,数据类型和其他标志)以及实际数据本身:

  1. 对于元数据PyArray_malloc,使用CPython的内存分配器(即pymalloc)。
  2. 对于数据本身,使用PyDataMem_NEW,它直接利用底层的C-runtimme功能:
NPY_NO_EXPORT void *
PyDataMem_NEW(size_t size)
{
    void *result;

    result = malloc(size);
    ...
    return result;
}

我不确定,这个设计背后的确切想法是什么:显然,人们希望从pymalloc的小对象优化中获益,对于数据,这种优化永远不会起作用,但是人们可以使用PyMem_RawMalloc而不是malloc。也许目标是能够将Numpy数组包装在由C例程分配的内存中并接管内存的所有权(但这在某些情况下不起作用,请参阅本文末尾的评论)。

这解释了您正在观察的行为:对于数据(其大小根据传递的size-argument而改变),使用PyDataMem_NEW,绕过CPython的内存分配器,您可以看到C-runtime的分配器的原始行为。


人们应该尽量避免混合不同的分配/解除分配例程PyArray_malloc / PyDataMem_NEW'/mallocandPyArray_free/PyDataMem_FREE/free`:即使它适用于OS + Python版本,它也可能因其他组合而失败。

例如,在Windows上,当使用不同的编译器版本构建扩展时,一个可执行文件可能具有来自不同C运行时的不同内存分配器,而malloc/free可能与不同的C内存分配器通信,这可能导致难以跟踪错误。

© www.soinside.com 2019 - 2024. All rights reserved.