Python源码阅读-内存管理机制(二)

Python 的内存分配策略

arena

arena: 多个pool聚合的结果

arena size

pool的大小默认值位4KB

arena的大小默认值256KB, 能放置 256/4=64 个pool

obmalloc.c中代码

arena 结构

一个完整的arena = arena_object + pool集合

arena_object的作用

pool_headerarena_object

arena的两种状态

arena存在两种状态: 未使用(没有建立联系)/可用(建立了联系)

全局由两个链表维护着

arena的初始化

首先, 来看下初始化相关的一些参数定义

代码obmalloc.c

然后, 看下obmalloc.carena初始化的代码

图示: 初始化arenas数组, 初始化后的所有arena都在unused_arena_objects单链表里面

图示: 从arenas取一个arena进行初始化

没有可用的arena?

此时

然后, 假设第一次分配了16个, 发现没有arena之后, 第二次处理结果: numarenas = 32

即, 数组扩大了一倍

arena分配

new了一个全新的 arena之后,

图示: 从全新的arena中获取一个pool

假设arena是旧的, 怎么分配的pool

这个arena->freepools是何方神圣?

当arena中一整块pool被释放的时候

也就是说, 在pool整块被释放的时候, 会将pool加入到arena->freepools作为单链表的表头, 然后, 在从非全新arena中分配pool时, 优先从arena->freepools里面取, 如果取不到, 再从arena内存块里面获取

图示

一个arena满了之后呢

很自然, 从下一个arena中获取

注意: 这里有个逻辑, 就是每分配一个pool, 就检查是不是用到了最后一个, 如果是, 需要变更usable_arenas到下一个可用的节点, 如果没有可用的, 那么下次进行内存分配的时候, 会判定从arenas数组中取一个

arena回收

内存分配和回收最小单位是block, 当一个block被回收的时候, 可能触发pool被回收, pool被回收, 将会触发arena的回收机制

四种情况

具体可以看PyObject_Free的代码

内存分配步骤

好的, 到这里, 我们已经知道了block和pool的关系(包括pool怎么管理block的), 以及arena和pool的关系(怎么从arena中拉到可用的pool)

那么, 在分析PyObject_Malloc(size_t nbytes)如何进行内存分配的时候, 我们就刨除掉这些管理代码

关注: 如何寻找得到一块可用的nbytes的block内存

其实代码那么多, 寻址得到对应的block也就这么几行代码, 其他代码都是pool没有, 找arena, 申请arena, arena没有, 找arenas, 最终的到一块pool, 初始化, 返回第一个block

如果有的情况, 用现成的

从上面这个判断逻辑来看, 内存分配其实主要操作的是pool, 跟arena并不是基本的操作单元(只是用来管理pool的)

结论: 进行内存分配和销毁, 所有操作都是在pool上进行的

usedpools 是什么鬼? 其实是可用pool缓冲池, 后面说

内存池

arena 内存池的大小

取决于用户, Python提供的编译符号, 用于决定是否控制

obmalloc.c

具体使用中, python并不直接与arenas和arena打交道, 当Python申请内存时, 最基本的操作单元并不是arena, 而是pool

问题: pool中所有block的size一样, 但是在arena中, 每个pool的size都可能不一样, 那么最终这些pool是怎么维护的? 怎么根据大小找到需要的block所在的pool? => usedpools

pool在内存池中的三种状态

usedpools

usedpools数组: 维护着所有处于used状态的pool, 当申请内存的时候, 会通过usedpools寻找到一块可用的(处于used状态的)pool, 从中分配一个block

结构:

解开看(obmalloc.c)

为了看懂这步的trick, 心好累>_

直接上图

new一个pool时维护

init获得的情况, 其实就是将刚刚从arena中获取的pool加入到 usedpools 对应的双向链表中, 然后初始化, 然后返回block

从现有pool中获取block

从现有的pool, 其实就是 usedpools得到双向链表头部, 判断是不是空链表, 不是的话代表有可用的pool, 直接从里面获取

全局结构


先这样吧, Python中整个内存池基本结构和机制大概如此, 是不是发现有好多数组/链表等等, 在分配/回收上处理下做成各种池…..

后面还有内存相关的就是垃圾收集了, 后面再说了吧

wklken

2015-08-29

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

任选一种支付方式

1 1 收藏 评论

关于作者:wklken

Pythonista/vimer 个人主页 · 我的文章 · 37 ·   

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部