对Python内存管理的认识(重点usedpool的一个trick的理解)http://blog.csdn.net/wangyuquanliuli/article/details/8606072
2016-02-15 22:08
651 查看
对Python内存管理的认识(重点usedpool的一个trick的理解)
2013-02-24 04:01 1177人阅读 评论(0) 收藏 举报分类:
python(11)
版权声明:本文为博主原创文章,未经博主允许不得转载。
关于python内存的东西实在很多,这里只记录一些比较重要或者我认为值得注意的点。
在python中,内存管理机制被抽象成一种层次似得结果,如果所示
第0层是基于c语言的malloc,第1层主要是对第0层的内存管理接口进行包装,因为如果第0层操作系统不一样可能接口会不统一,第2层是对通用对象内存管理接口,第三层则是对int,string等常用对象进行内存管理接口的封装,里面做了不少优化工作。
前面的不少文章也都提到过内存池这个概念,不错,针对常用的Int,string,list这些类型,python都会有相应的内存池来提高效率。
整个小块内存的内存池可以视为一个层次结构,从小到大依次为block->pool->arena->内存池。
block和内存池在python源码里面并没有相对应的实体结构,只是一个概念,pool和arena则确实存在。
block是一个确定大小的内存块。block的长度都是8字节对齐的,可以有很多不同大小的block,比如8,16,24,32.。。。。。
pool是一组block的集合.一个pool通常为4KB(系统内存页大小).一个pool管理的所有block,它们的大小是一样的,不同的pool可以不一样。一个pool申请的时候会pool_header和相应的内存一起申请,而arena则不一样,它申请的时候可以理解为只申请了一个header,然后会在以后的某个步骤和pool产生联系,进行管理。
arena是一组pool的集合。arena通常为256KB,也就是一个arena含64个pool.arena对pool进行管理。arena分为“可用的”和“未用的”,通过链表来管理。具体实现细节下面的trick的地方会提到。值得注意的是,虽然arena管理pool,但是实际上python 的操作都是在pool上进行的。
毫无疑问,内存池就是由多个arena组成的。内存池有三种状态:used,full,empty.意思都是字面意思,很好理解。所有处于used状态下的pool都会被usedpools数组所管理。接下来就是比较trick的实现了,有点难理解,书上讲的不详细,另外网上查了资料,来说说我的理解。
下面是usedpools的定义:
[cpp] view
plain copy
#define PTA(x) ((poolp )((uchar *)&(usedpools[2*(x)]) - 2*sizeof(block *)))
#define PT(x) PTA(x), PTA(x)
static poolp usedpools[2 * ((NB_SMALL_SIZE_CLASSES + 7) / 8) * 8] = {
PT(0), PT(1), PT(2), PT(3), PT(4), PT(5), PT(6), PT(7)。。。。//省略后面的}
我们可以看到usedpools是一个数组。这个数组是不同区间大小的used状态的内存池的头结点数组,但是类型不是pool_header,而是它的指针(这一点书上没有提及,不太容易理解),它只是指向头结点。
这就是不同区间大小的表现。0表示申请1-8的内存,1表示申请9-16的内存。。。。也就是说,数组里面每个指针指向的是一定大小的pool_header.
头结点的定义:
[cpp] view
plain copy
struct pool_header {
union { block *_padding;
uint count; } ref; /* number of allocated blocks */
block *freeblock; /* pool's free list head */
struct pool_header *nextpool; /* next pool of this size class */
struct pool_header *prevpool; /* previous pool "" */
假设我们要申请28的内存,换算下来应该是数组中表现3的位置,具体申请代码:
pool = usedpools[3+3];
[cpp] view
plain copy
if(pool != pool->nextpool)
{
//有可用的pool
}
trick的地方就在于为什么可以这么写?
[cpp] view
plain copy
#define PTA(x) ((poolp )((uchar *)&(usedpools[2*(x)]) - 2*sizeof(block *)))
这个宏说明数组里面存的元素是指向他们本身地址再向前2个指针大小的。而pool_header里面nextpool前面刚好有两个指针:_padding和freeblock,根据指针偏移,这样刚好是的数组元素地址对应nextpool。也就是如果将usedpool当中两个一组的指针看过是pool_header当中的nextpool跟prevpool域的时候,这段初始化代码正好是指回了”自身”,也就是初始化成为了一个空的带头结点双循环链表。换句话说就是初始化了一个节点node,node里面的一个指针指向了它自己(根据上面的指针偏移+数组来实现的)。所以这段代码的trick在于使用了两个指针大小就实现了pool_header的双循环链表头的功能,如果要访问大小区间索引为x的usedpool双链表,只需要这样的形式即可:usedpool[2*x],这就是它的头结点。注意这个头结点就真的只是用来构建双链表用的,所以_padding和freeblock两个指针被概念上的覆盖是没关系的(这点昨晚一直没想通,彻夜难眠=
=,果然还是太菜了)。所以如果pool这个头结点不等于pool->nextpool的话,就说明头结点后面还有节点,说明该nextpool已经被赋值过了,也就是说有可用的pool.
好了,上面就是我认为有点难理解的地方,如此写一遍也更加加深了我的理解。
最后上张图来看看几个层次之间的关系:
如果什么不对的地方,欢迎指出:)
相关文章推荐
- hdu acm 1532 Drainage Ditches
- Match:DNA repair(POJ 3691)
- Nessus6.5.4安装及Plugins Download Fail 解决方法
- Table 'hd_online' is marked as crashed and should be repaired索引损坏
- LeetCode 217. Contains Duplicate 哈希
- 程序运行出现process launch failed: Security
- POJ 3250 Bad Hair Day 单调
- Leetcode(2) -Contains Duplicate
- LINUX wait 和 waitpid详解
- UVA253 Cube painting
- 安装epel以及遇到的问题:Cannot retrieve matalink for repository:epel.Please verify its path and try again
- 贪心 HDU 1789 Doing Homework again
- POJ 3216 Repairing Company(FLOYD+DAG最小路径覆盖)
- 五子棋AI算法第四篇-启发式搜索函数
- git push error: failed to push some refs to 'ssh://git@ip:8850/Out/Afuyigou.
- handler的obtainMessage
- 五子棋AI算法第三篇-Alpha Beta剪枝
- 《FAQ:OpenCV Haartraining》——使用OpenCV训练Haar like+Adaboost分类器的常见问题
- MFC中OnDraw与OnPaint的区别
- raid功能中spanning和striping模式有什么区别?