gpt4 book ai didi

python - CPython的静态对象地址和分片

转载 作者:太空宇宙 更新时间:2023-11-04 00:03:29 25 4
gpt4 key购买 nike

我读了

For CPython, id(x) is the memory address where x is stored.

而且对象的 id 是给定的,它永远不会改变,这意味着对象在其生命周期中始终存储在给定的内存地址中。这就引出了一个问题:(虚拟)内存碎片怎么办?

假设一个对象A在地址1(有id 1),占用10个字节,所以它占用地址1-10。对象 Bid 为 11,占用字节 11-12,对象 C 占用地址 13-22。一旦 B 超出范围并获得 GC,我们就会产生碎片。

这个难题是如何解决的?

最佳答案

CPython 为小对象使用它自己的内存分配器,pymalloc-allocator .在 code itself 中可以找到很好的描述.

这个分配器非常擅长避免内存碎片,因为它有效地重用了释放的内存。然而,这只是一种启发式方法,人们可能会想出导致内存碎片的场景。

让我们来玩一下当我们分配一个大小为 1 字节的对象时会发生什么。

对于小于 512 字节的对象,CPython 有自己所谓的 arena。显然,1 字节请求将由其分配器管理。

请求的大小分为 64 个不同的类别:第 0 类是 1..8 字节的大小,第 1 类是 9..16 字节的大小等等 - 这是由于需要对齐 8 字节.上述每个类都有自己或多或少的独立/专用内存。我们要求的是 0 等舱。

让我们假设这是对该尺寸等级的第一个请求。将创建一个新的“池”或重新使用一个空池。池是 4KB big ,因此有 512 个 8 字节“ block ”的空间。尽管请求只有 1 个字节,但我们将阻塞占用 block 的另外 7 个字节,因此它们不能用于其他对象。所有空闲 block 都保存在一个列表中——一开始所有 512 个 block 都在这个列表中。分配器从这个空闲 block 列表中删除第一个 block 并将其地址作为指针返回。

池本身被标记为“已用”并添加到第 0 级已用池列表中。

现在,分配另一个大小 <=8 字节的对象发生如下。首先,我们查看 0 级已用池列表,并会找到一个已在使用的池,即有一些已用 block 和一些空闲 block 。分配器使用第一个空闲 block ,将其从空闲 block 列表中删除并返回其地址作为指针。

删除第一个对象很容易——我们将占用的 block 添加为(到目前为止是单个)已用池中空闲 block 列表的头部。

当创建一个 8 字节的新对象时,将使用空闲 block 列表中的第一个 block ,这是第一个现在已删除的对象使用的 block 。

如您所见,内存得到重用,因此内存碎片大大减少。这并不意味着不能有内存碎片:

分配 512 个 1 字节对象后,第一个池变“满”,将创建/使用第 0 类大小的新池。我们还添加了另外 512 个对象,第二个池也变得“满”。等等。

现在,如果前 511 个元素被删除 - 仍然会有一个字节阻塞整个 4KB,不能用于其他类。

只有当最后一个 block 被释放时,池才变为“空”,因此可以重新用于其他大小级别。


空池不会返回给操作系统,而是留在竞技场中并被重用。但是,pymalloc manages multiple arenas ,并且如果竞技场变为“未使用”,则它可能会被释放并将占用的内存(即池)返回给操作系统。

关于python - CPython的静态对象地址和分片,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54998210/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com