从Python的源码来解析Python下的freeblock

yipeiwu_com5年前Python基础

1 引言

在python内存管理中,有一个block的概念。它比较类似于SGI次级空间配置器。
首先申请一块大的空间(4KB),然后把它切割成一小份(8, 16 一直到512)。
当有内存申请的请求时候,简单的流程是:根据大小找到对应的block,然后在freeblock 上给它一份。

2 问题

整个过程是一种比较自然的slab分配方式。但当我读到这段代码时,却感到疑惑:

static void* _PyObject_Malloc(void* ctx, size_t nbytes)
{
    ...
  pool->freeblock = (block*)pool + pool->nextoffset;

    pool->nextoffset += INDEX2SIZE(size);
    *(block **)(pool->freeblock) = NULL; // [1]
    ...
}

freeblock指向空闲的链表,为它赋值很好理解。但是为什么要加上代码1处那一句!
对C比较熟悉的童鞋很容易能看出它的作用,它在为*freeblock赋值为NULL。

但是为什么要这么做?
直到看到内存回收的代码:

static void _PyObject_Free(void* ctx, void*p)
{
  ...
  *(block**)p = lastfree = pool->freeblock;
  pool->freeblock = (block*)p;
  ...
}

回想一下SGI次级空间配置,它需要一个链表,指向block中可用的小块。因为这些快,是离散的,只有用指针才能索引它。
在SGI次级空间配置中,是用一个union,达到了节省空间的目的:有数据时,它存储着真正的数据;没有数据时,它就变成指向下一块可用内存的指针:

union __Obj {
  union __Obj* free_list_link;
  char client_data[];
};

这样一想,问题就变得很明显了。freeblock指向一个链表,链表的next域就由它自己来索引。
在_PyObject_Free中,内存p是要被回收的,它应该插在freeblock的链表头,freeblock被更新指向它。同时,p指向原来freeblock指向的内容,这是一个很简单的链表插入操作。
这样在遍历的时候,我们就可以用freeblock = * freeblock的方式来工作了。
如下图所示:

2015511115319124.png (473×154)

相关文章

Python的面向对象编程方式学习笔记

类与实例 类与实例相互关联着:类是对象的定义,而实例是“真正的实物”,它存放了类中所定义的对象的具体信息。 下面的示例展示了如何创建一个类: class MyNewObjectTyp...

django 2.0更新的10条注意事项总结

前言 备受期待的django 2.0已经发布了,最大的一个变化就是不再支持python2.x版本了,这也为我们还在保守使用的2.x的同学们敲响了警钟,赶紧学习python3.x吧,虽然大...

Python的Django框架使用入门指引

Python的Django框架使用入门指引

 前言 传统 Web 开发方式常常需要编写繁琐乏味的重复性代码,不仅页面表现与逻辑实现的代码混杂在一起,而且代码编写效率不高。对于开发者来说,选择一个功能强大并且操作简洁的开发...

基于numpy.random.randn()与rand()的区别详解

numpy 中有一些常用的用来产生随机数的函数,randn()和rand()就属于这其中。 numpy.random.randn(d0, d1, …, dn) 是从标准正态分布中返回一个...

Python脚本在Appium库上对移动应用实现自动化测试

 采用Appium进行自动化的功能性测试最酷的一点是,你可以使用具有最适合你的测试工具的任何一门语言来写你的测试代码。大家选择最多的一个测试编程语言就是Python。 使用Ap...