eryksun已经回答了问题1,而我已经回答了问题3(原始的#4),但是现在让我们回答问题2:
为什么特别释放50.5mb-释放量基于多少?
最终,它基于的是Python内部的一系列巧合,而malloc
这些巧合很难预测。
首先,根据测量内存的方式,您可能只在测量实际映射到内存的页面。在这种情况下,每当页面被调页器换出时,内存将显示为“已释放”,即使尚未释放也是如此。
或者您可能正在测量使用中的页面,这些页面可能会或可能不会计算已分配但从未接触过的页面(在乐观地过度分配的系统(例如linux)上),已分配但已标记的页面MADV_FREE
等。
如果您确实在测量分配的页面(这实际上不是一件非常有用的事情,但这似乎是您要问的问题),并且页面实际上已经被释放了,则可能会发生两种情况:您曾经使用过brk
或等效方法来缩小数据段(如今非常少见),或者您曾经使用过munmap
或类似方法来释放映射的段。(从理论上讲,后者也有一个较小的变体,因为有一些方法可以释放一部分已映射的段,例如,将其窃取MAP_FIXED
用于MADV_FREE
立即取消映射的段。)
但是大多数程序并不直接在内存页面中分配内容。他们使用malloc
-style分配器。当您调用时free
,如果您恰巧free
正在映射中的最后一个活动对象(或数据段的最后N个页面),则分配器只能将页面释放到OS 。您的应用程序无法合理地预测甚至提前检测到它。
CPython使这一过程变得更加复杂-它在的顶部具有一个自定义的2级对象分配器,而在的顶部具有一个自定义的内存分配器malloc
。(有关更详细的解释,请参见源注释。)此外,即使在C API级别上,Python也要少得多,您甚至不直接控制顶级对象的释放时间。
因此,当您释放一个对象时,如何知道它是否将向OS释放内存?好吧,首先,您必须知道已发布了最后一个引用(包括您不知道的任何内部引用),从而允许GC对其进行分配。(与其他实现不同,至少CPython会在允许的情况下立即释放对象。)这通常会在下一级向下释放至少两件事(例如,对于一个字符串,您释放该PyString
对象和字符串缓冲区) )。
如果确实要释放对象,则要知道这是否导致下一级别的释放对象存储块,您必须知道对象分配器的内部状态及其实现方式。(除非您要取消分配块中的最后一件事,否则显然不会发生,即使那样,也可能不会发生。)
如果确实要释放对象存储块,要知道这是否导致free
调用,则必须知道PyMem分配器的内部状态及其实现方式。(同样,您必须在malloc
ed区域中释放最后一个使用中的块,即使那样,也可能不会发生。)
如果你做 free
一个malloc
版区,要知道这是否会导致munmap
或同等学历(或brk
),你必须知道的内部状态malloc
,以及它是如何实现的。而且,这个不同于其他,它是高度特定于平台的。(同样,您通常必须malloc
在一个mmap
网段中释放最后一次使用的资源,即使那样,也可能不会发生。)
因此,如果您想了解为什么它恰好释放了50.5mb,则必须从下至上进行跟踪。malloc
当您进行一次或多次free
通话(可能超过50.5mb)时,为什么不映射50.5mb的页面?您必须阅读平台的malloc
,然后遍历各种表和列表以查看其当前状态。(在某些平台上,它甚至可能利用系统级信息,而如果不制作系统快照以进行脱机检查几乎是不可能捕获的,但是幸运的是,这通常不是问题。)然后,您必须在以上三个级别上执行相同的操作。
因此,对该问题唯一有用的答案是“因为”。
除非您正在进行资源有限的(例如嵌入式)开发,否则您没有理由关心这些细节。
如果你正在做资源有限的发展,了解这些细节是无用的; 您几乎必须在所有这些级别上进行最终运行,尤其mmap
是在应用程序级别上可能需要的内存(可能在两者之间使用一个简单的,易于理解的,特定于应用程序的区域分配器)。