python源码分析----内存分配(2)

本文探讨Python内存管理,重点关注PyObject_Malloc和PyObject_Free方法。讲解了内存池如何在arena上分配,usedpools数组如何管理内存池,并通过双链表组织。此外,还介绍了内存分配中freeblock单链表的实现,以及对象释放的过程。
摘要由CSDN通过智能技术生成

早就应该写部分的内容了。。。。最近比较负能量。。。伤不起啊。。

上一篇说到了,在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
         */
         
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值