什么是python';s管理大变量分配/释放的策略?
作为后续研究,在(C)Python中,对于小变量和大变量似乎有不同的分配/解除分配策略。什么是python';s管理大变量分配/释放的策略?,python,numpy,cpython,Python,Numpy,Cpython,作为后续研究,在(C)Python中,对于小变量和大变量似乎有不同的分配/解除分配策略。 更准确地说,对象大小中似乎有一个边界,在该边界之上,分配对象使用的内存可以返回给操作系统。低于此大小时,内存不会返回给操作系统 引用Numpy释放内存策略中的答案: 例外情况是,对于大型单次分配(例如,如果您创建了一个多兆字节数组),则使用不同的机制。这样大的内存分配可以释放回操作系统。因此,可能是程序中的非numpy部分产生了您看到的问题 事实上,这两种分配策略很容易表现出来。例如: 第一个策略:不向操
更准确地说,对象大小中似乎有一个边界,在该边界之上,分配对象使用的内存可以返回给操作系统。低于此大小时,内存不会返回给操作系统 引用Numpy释放内存策略中的答案: 例外情况是,对于大型单次分配(例如,如果您创建了一个多兆字节数组),则使用不同的机制。这样大的内存分配可以释放回操作系统。因此,可能是程序中的非numpy部分产生了您看到的问题 事实上,这两种分配策略很容易表现出来。例如:
- 第一个策略:不向操作系统返回内存
将numpy导入为np
导入psutil
导入gc
#分配数组
x=np.随机.均匀(0,1,大小=(10**4))
#gc
德尔克斯
gc.collect()
#我们从41295.872kb到41295.872kb
#使用psutil.Process().memory_info().rss/10**3;虚拟机也有同样的行为
=>没有返回操作系统的内存
- 第二种策略:释放的内存返回操作系统
x=np.random.uniform(0,1,大小=(10**5))
德尔克斯
gc.collect()
#我们从41582.592KB到41017.344KB
=>内存已释放到操作系统
似乎使用第二种策略可以分配大约大于8*10**4
字节的对象
因此:
- 这种行为有记录吗?(分配策略改变的确切界限是什么?)
- 这些策略的内部结构是什么(不仅仅是假设使用
/mmap
将内存释放回操作系统)munmap
- 这是由Python运行时100%完成的,还是Numpy有一种特定的处理方法?(提到在内存分配器之间切换的
)NPY\u USE\u PYMEM
malloc/free
分配/释放内存时,它不直接与底层操作系统通信,而是与内存分配器的具体实现通信。就我在Linux上的情况而言,它是
GNU分配器有不同的所谓Arena,其中内存不会返回到操作系统,而是保留下来,这样就可以重用,而无需与操作系统通信。但是,如果请求了大量内存(无论“大”的定义如何),分配器不会使用arenas提供的内存,而是从操作系统请求内存,因此,一旦调用了free
,就可以直接将内存返回给操作系统
CPython有自己的内存分配器-,它构建在C-runtime-allocator之上。它针对生活在特殊竞技场中的小物体进行了优化;与底层C-runtime-allocator相比,创建/释放这些对象的开销更小。但是,大于512字节的对象不使用此竞技场,而是由C-runtime-allocator直接管理 numpy的数组的情况更加复杂,因为元数据(如形状、数据类型和其他标志)和实际数据本身使用了不同的内存分配器:
NPY\u否\u导出无效*
PyDataMem_新(大小)
{
无效*结果;
结果=malloc(大小);
...
返回结果;
}
我不确定,这种设计背后的确切想法是什么:很明显,人们希望从pymalloc的小对象优化中获益,对于数据,这种优化永远不会起作用,但是人们可以使用它来代替malloc
。也许我们的目标是能够围绕C例程分配的内存包装numpy数组,并接管内存的所有权(但这在某些情况下不起作用,请参阅本文末尾我的评论)
这解释了您观察到的行为:对于数据(其大小根据中传递的size参数而变化)PyDataMem\u使用NEW
,它绕过了CPython的内存分配器,您可以看到C-runtime的分配器的原始行为
我们应该尽量避免混合使用不同的分配/解除分配例程
PyDataMem\u malloc
/PyDataMem\u NEW'/
malloc和
PyArray\u free/PyDataMem\u free//code>free`:即使它在手头的OS+Python版本下工作,它也可能因其他组合而失败
例如,在Windows上,当使用不同的编译器版本构建扩展时,一个可执行文件可能具有来自不同C运行时的不同内存分配器,malloc/free
可能与不同的C内存分配器通信,这可能导致难以跟踪错误;这是一个实现细节。请理解gc.collect()
在这里完全是一个骗局。gc
模块仅适用于CPython中的循环垃圾收集机制。您还没有创建任何引用周期,因此它不需要做任何事情。内存是否返回到操作系统取决于运行时附带的内存管理器(在我的Linux上是glibc)。这些管理器通常不会费心将小块内存返回到操作系统,并在以后将该内存重新用于类似大小的分配。pymalloc可以依赖不同的分配函数,mmap
/munmap
或malloc
/free
():而malloc是一个C运行时分配器(这本身可以称为