早就应该写部分的内容了。。。。最近比较负能量。。。伤不起啊。。
上一篇说到了,在python的内存分配中两个非常重要的方法:PyObject_Malloc和PyObject_Free
在具体的来这两个方法之前,先要看看别的一些东西
//这里用usedpool构成了一个双向链表
//只用了两个指针就搞定了。。我擦。。。
//这里将保存的地址减去了两个指针的大小,那么根据pool结构体的定义,那么将地址加上两个指针,正好就是next,加上3个指针正好就是prev
//比较的巧妙
#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)
#if NB_SMALL_SIZE_CLASSES > 8
, PT(8), PT(9), PT(10), PT(11), PT(12), PT(13), PT(14), PT(15)
#if NB_SMALL_SIZE_CLASSES > 16
, PT(16), PT(17), PT(18), PT(19), PT(20), PT(21), PT(22), PT(23)
#if NB_SMALL_SIZE_CLASSES > 24
, PT(24), PT(25), PT(26), PT(27), PT(28), PT(29), PT(30), PT(31)
#if NB_SMALL_SIZE_CLASSES > 32
, PT(32), PT(33), PT(34), PT(35), PT(36), PT(37), PT(38), PT(39)
#if NB_SMALL_SIZE_CLASSES > 40
, PT(40), PT(41), PT(42), PT(43), PT(44), PT(45), PT(46), PT(47)
#if NB_SMALL_SIZE_CLASSES > 48
, PT(48), PT(49), PT(50), PT(51), PT(52), PT(53), PT(54), PT(55)
#if NB_SMALL_SIZE_CLASSES > 56
, PT(56), PT(57), PT(58), PT(59), PT(60), PT(61), PT(62), PT(63)
#if NB_SMALL_SIZE_CLASSES > 64
#error "NB_SMALL_SIZE_CLASSES should be less than 64"
#endif /* NB_SMALL_SIZE_CLASSES > 64 */
#endif /* NB_SMALL_SIZE_CLASSES > 56 */
#endif /* NB_SMALL_SIZE_CLASSES > 48 */
#endif /* NB_SMALL_SIZE_CLASSES > 40 */
#endif /* NB_SMALL_SIZE_CLASSES > 32 */
#endif /* NB_SMALL_SIZE_CLASSES > 24 */
#endif /* NB_SMALL_SIZE_CLASSES > 16 */
#endif /* NB_SMALL_SIZE_CLASSES > 8 */
};
前面我们就应该可以知道了,pool其实是在arena结构上分配的,但是pool并不是由arena管理,而且前面我们已经知道pool其实是有类型的,每一种类型的pool只用来分配固定大小的内存,例如,pool的szidx编号如果是0,那么它就是用来分配8字节内存的。。。
好了,pool是在arena上面分配,但是并不是由他来管理,那么是由什么东西来管理的呢。。。?上面贴出来的代码定义了一个usedpools数组,嗯,就是用它来定义的。。。
而且,每种类型的pool都构成了一个双链表,这个数组就保存了这个双链表的头部。。。
呵呵,没有搞错吧,这个事怎么实现的呢。。?这个实现真的时够trick的,我也是看了很久才理解到,真是才疏学浅,以前还真没有见过这样子的实现。。。。
有前面的宏PTA和PT就可以知道,PT(0)其实是定义了两个指针,而且保存的值正好是第一个第一个指针的地址再减去两个指针的地址,嗯,再看看pool的定义:
struct pool_header {
union { block *_padding;
uint count; } ref; /* number of allocated blocks */ //当前pool上面分配的block的数量
block *freeblock; /* pool's free list head */ //指向下一个可用的block,这里构成了一个链表, 它是一个离散的链表,很有意思
struct pool_header *nextpool; /* next pool of this size class */ //通过这两个指针形成pool的双链表
struct pool_header *prevpool; /* previous pool "" */
uint arenaindex; /* index into arenas of base adr */ //在arena里面的索引
uint szidx; /* block size class index */ //分配内存的类别,8字节,16或者。。。
uint nextoffset; /* bytes to virgin block */ //下一个可用的block的内存偏移量
uint maxnextoffset; /* largest valid nextoffset */ //最后一个block距离开始位置的距离
};
typedef struct pool_header *poolp; //头部
嗯,看看加上两个指针大小是不是正好指向nextpool指针,加上三个指针大小是不是正好指向prevpool指针。。。这里的实现就不多说啦。。其实上面的注释也解释了一些东西。。。如果不能理解,那就自己再好好看。。这个过程一定是要自己经过努力去理解才好。。。。。
那么接下来来看看PyObject_Malloc的定义吧:
void *
PyObject_Malloc(size_t nbytes)
{
block *bp; //将会用这个指针指向分配的内存地址
poolp pool;
poolp next;
uint size;
#ifdef WITH_VALGRIND
if (UNLIKELY(running_on_valgrind == -1))
running_on_valgrind = RUNNING_ON_VALGRIND;
if (UNLIKELY(running_on_valgrind))
goto redirect;
#endif
/*
* Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes.
* Most python internals blindly use a signed Py_ssize_t to track
* things without checking for overflows or negatives.
* As size_t is unsigned, checking for nbytes < 0 is not required.
*/
if (nbytes > PY_SSIZE_T_MAX) //这个难道 都会出现。。?
return NULL;
/*
* This implicitly redirects malloc(0).
*/
if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { //如果这里可以用小地址分配模式
LOCK();
/*
* Most frequent paths first
*/