大师兄的Python源码学习笔记(五十一): Python的内存管理机制(六)

大师兄的Python源码学习笔记(五十): Python的内存管理机制(五)
大师兄的Python源码学习笔记(五十二): Python的内存管理机制(七)

三、内存池

3. block的释放
  • block的释放实际上就是将一块block归还给pool
  • 我们知道pool可能有三种状态,在不同状态,他们的位置是不同的。
  • 当释放block后,可能会引起pool的状态转变,这种转变可分为两种情况:
  • used状态转变为empty状态。
  • full状态转变为used状态。
  • 最多的情况是pool中尽管释放并收回了一个block,但他仍处于used状态,这是最简单的情况:
Objects\obmalloc.c

/* Free a memory block allocated by pymalloc_alloc().
   Return 1 if it was freed.
   Return 0 if the block was not allocated by pymalloc_alloc(). */
static int
pymalloc_free(void *ctx, void *p)
{
    poolp pool;
    block *lastfree;
    poolp next, prev;
    uint size;

    assert(p != NULL);

    ... ...

    pool = POOL_ADDR(p);
    if (!address_in_range(p, pool)) {
        return 0;
    }
    /* We allocated this address. */

    LOCK();

    /* Link p to the start of the pool's freeblock list.  Since
     * the pool had at least the p block outstanding, the pool
     * wasn't empty (so it's already in a usedpools[] list, or
     * was full and is in no list -- it's not in the freeblocks
     * list in any case).
     */
    assert(pool->ref.count > 0);            /* else it was empty */
    *(block **)p = lastfree = pool->freeblock;
    pool->freeblock = (block *)p;
    if (!lastfree) {
        ... ...
    }

    struct arena_object* ao;
    uint nf;  /* ao->nfreepools */

    /* freeblock wasn't NULL, so the pool wasn't full,
     * and the pool is in a usedpools[] list.
     */
    if (--pool->ref.count != 0) {
        /* pool isn't empty:  leave it in usedpools */
        goto success;
    }
    /* Pool is now empty:  unlink from usedpools, and
     * link to the front of freepools.  This ensures that
     * previously freed pools will be allocated later
     * (being not referenced, they are perhaps paged out).
     */
    next = pool->nextpool;
    prev = pool->prevpool;
    next->prevpool = prev;
    prev->nextpool = next;

    /* Link the pool to freepools.  This is a singly-linked
     * list, and pool->prevpool isn't used there.
     */
    ao = &arenas[pool->arenaindex];
    pool->nextpool = ao->freepools;
    ao->freepools = pool;
    nf = ++ao->nfreepools;

   ... ...
}
  • pool的状态保持used状态的情况下,Python仅仅将block重新放入到自由block链表中,并调整poolref.count引用计数。
  • 如果释放block之前,其所属的pool处于full状态,则将pool重新链回usedpools中即可。
  • 最复杂的情况发生在pool在收回block前后状态从used状态转为empty的情况:
  • 在这种情况下,首先需要将empty状态的pool链入到freepools中。
Objects\obmalloc.c

static int
pymalloc_free(void *ctx, void *p)
{
   poolp pool;
   block *lastfree;
   poolp next, prev;
   uint size;
... ...
    pool = POOL_ADDR(p);
   if (!address_in_range(p, pool)) {
       return 0;
   }
   /* We allocated this address. */
... ...
   /* Link p to the start of the pool's freeblock list.  Since
    * the pool had at least the p block outstanding, the pool
    * wasn't empty (so it's already in a usedpools[] list, or
    * was full and is in no list -- it's not in the freeblocks
    * list in any case).
    */
   assert(pool->ref.count > 0);            /* else it was empty */
   *(block **)p = lastfree = pool->freeblock;
   pool->freeblock = (block *)p;
... ....
}
  • 到这里又分成四种情况:
Objects\obmalloc.c

static int
pymalloc_free(void *ctx, void *p)
{
   poolp pool;
   block *lastfree;
   poolp next, prev;
   uint size;

   ... ...
   /* Link the pool to freepools.  This is a singly-linked
    * list, and pool->prevpool isn't used there.
    */
   ao = &arenas[pool->arenaindex];
   pool->nextpool = ao->freepools;
   ao->freepools = pool;
   nf = ++ao->nfreepools;

   /* All the rest is arena management.  We just freed
    * a pool, and there are 4 cases for arena mgmt:
    * 1. If all the pools are free, return the arena to
    *    the system free().
    * 2. If this is the only free pool in the arena,
    *    add the arena back to the `usable_arenas` list.
    * 3. If the "next" arena has a smaller count of free
    *    pools, we have to "slide this arena right" to
    *    restore that usable_arenas is sorted in order of
    *    nfreepools.
    * 4. Else there's nothing more to do.
    */
   if (nf == ao->ntotalpools) {
       /* Case 1.  First unlink ao from usable_arenas.
        */
       assert(ao->prevarena == NULL ||
              ao->prevarena->address != 0);
       assert(ao ->nextarena == NULL ||
              ao->nextarena->address != 0);

       /* Fix the pointer in the prevarena, or the
        * usable_arenas pointer.
        */
       if (ao->prevarena == NULL) {
           usable_arenas = ao->nextarena;
           assert(usable_arenas == NULL ||
                  usable_arenas->address != 0);
       }
       else {
           assert(ao->prevarena->nextarena == ao);
           ao->prevarena->nextarena =
               ao->nextarena;
       }
       /* Fix the pointer in the nextarena. */
       if (ao->nextarena != NULL) {
           assert(ao->nextarena->prevarena == ao);
           ao->nextarena->prevarena =
               ao->prevarena;
       }
       /* Record that this arena_object slot is
        * available to be reused.
        */
       ao->nextarena = unused_arena_objects;
       unused_arena_objects = ao;

       /* Free the entire arena. */
       _PyObject_Arena.free(_PyObject_Arena.ctx,
                            (void *)ao->address, ARENA_SIZE);
       ao->address = 0;                        /* mark unassociated */
       --narenas_currently_allocated;

       goto success;
   }
... ...
}
    1. 如果arena中所有的pool全都是empty的,则释放pool集合占用的内存,并将arena的状态调整为unused
    1. 如果之前arena中没有emptypool,那么usable_arenas链表中就找不到该arena,由于现在arena中有了一个pool,所以需要将这个arena链入到usable_arenas链表的表头。
    1. 如果arena中的emptypool个数为n,则从usable_arenas开始寻找arena可以插入的位置,并将arena插入到usable_arenas。(这个操作的原因是usable_arenas实际是一个有序链表,每一个arena中的emptypool个数,即nfreepools,都不能大于前面的arena,也不能小于前面的arena。保持这种有序性的原因是分配block时,是从usable_arenas的表头开始寻找可用的arena,这样可以保证当一个arenaempty pool数量越多,被使用的机会就越少,因此最终释放其维护的pool集合内存的机会就越大,可以保证多余的内存会被归还给系统。)
    1. 其他情况,不对arena进行任何处理。
4. 内存池全景
  • 对于一个C开发的庞大软件,其内存管理可能是最复杂最繁琐的部分。
  • 尽管各种不同的链表变幻无常,但所有的内存都在arena的掌控之中:
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容