Python源码--整数对象(PyIntObject)的内存池

【背景】

原文链接:http://blog.csdn.net/ordeder/article/details/25343633

Python整数对象是不可变对象,什么意思呢?例如执行如下python语句

>>>a = 1023

>>>a = 1024

>>>b = a

>>>c = 1024

>>>d = 195

>>>e = 195

python的整数对象结构为:

typedef struct {

PyObject_HEAD

long ob_ival;

} PyIntObject;

第一条命令执行后,python vm 创建了一个PyIntObject A,其中的ob_ival=1023记录了该整数对象的值,名字a引用该对象,即A 的 ob_refcnt=1。

当执行第二条语句的时候,python vm 又建立了新的PyIntObject B,其ob_ival值为1024.且名字a解引用AA的ob_refcnt-1变成0,系统将其回收。名字a引用对象B,B的ob_refcnt=1;

执行第三条语句,名字b引用名字a引用的对象,故而B的ob_refcnt+1,即为2;

第四条语句:c引用了不同于B的另一个整数对象

第五条语句:d引用了小整数对象195

第六条语句: e和d引用的是同一个对象,及小整数对象

p.s. 小整数的范围为[-5,257)

在python中的PyIntObject对象ob_ival内容是不可变的。

【Python中整数对象的存储优化】

由于python中的整数对象记录的整数值是不可变的,所以在名字a的值不断变化的过程中,就就涉及到了多次对象的创建和销毁。所以python为整数对象申请空间进行了两种优化:

优化1:为通用整数对象存储池

优化2:为小整数对象构建特殊的缓冲

PyIntObject分为小整数对象[-5~257)及大整数对象。小整数对象在py启动过程中初始化,从而实现小整数对象的缓存,缓冲中的小整数对象在py运行期间不会被销毁。        大整数对象需要程序员动态申请,对象在运行过程中根据ob_refcnt引用计数确定是否销毁(计数为0)。

其次,py为了优化整数对象的申请工作,为大整数对象引入了缓冲池的概念。为何引入缓冲池?我的理解是:对于系统来说,alloc一个PyIntObject对象,需要一次系统调用,为了避免每次创建对象都去调用alloc,便引入整数缓冲池的概念。

【小整数缓冲】

看着名字感觉挺神奇,其实就是在vm启动的时候预先将[-5~257)这些整数构建相应的整数对象。这些整数

对象的构建所在的内存空间同样是在:通用整数对象的缓冲池。只不过这些个小整数对象的ob_refcnt不会改变

且永远>0,所以在vm运行过程中不会被销毁,所以起到了缓冲的作用。

【通用整数对象的缓冲池】

为了减少alloc系统调用申请空间,内存池一次性申请的空间不是当个PyIntObject大小,而是一个以PyIntBlock块为结构的大小的空间,每个PyIntBlock块容纳了n个PyIntObject对象。内存池的基本数据结构如下:

#define BLOCK_SIZE      1000    /* 1K less typical malloc overhead */
#define BHEAD_SIZE      8       /* Enough for a 64-bit pointer */
#define N_INTOBJECTS    ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))

struct _intblock {
    struct _intblock *next;
    PyIntObject objects[N_INTOBJECTS];
};
typedef struct _intblock PyIntBlock;

static PyIntBlock *block_list = NULL;
static PyIntObject *free_list = NULL;

系统在启动的时候,PyIntBlock *block_list为空的,在运行过程中,如果需要创建整数对象,系统会先判定block_list是否有空闲的空间供创建对象,通过fill_free_list()函数从缓冲池中获取可用的PyIntObject。

如果free_list有空闲的PyIntObject可用,则直接在缓冲池中获取该空闲空间,你懂得。

如果没得,系统将通过alloc申请一个PyIntBlock挂入block_list中,同时将该块分为N_INTOBJECTS整数对象PyIntObject挂入到free_list中。

1. fill_free_list()的函数实现

static PyIntObject * fill_free_list(void)
{
    PyIntObject *p, *q;
    /* Python‘s object allocator isn‘t appropriate for large blocks. */
    p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
    if (p == NULL)
        return (PyIntObject *) PyErr_NoMemory();
    ((PyIntBlock *)p)->next = block_list;
    block_list = (PyIntBlock *)p;
    /* Link the int objects together, from rear to front, then return
       the address of the last int object in the block. */
    p = &((PyIntBlock *)p)->objects[0];
    q = p + N_INTOBJECTS;
    while (--q > p)
        Py_TYPE(q) = (struct _typeobject *)(q-1); //[1]
    Py_TYPE(q) = NULL;
    return p + N_INTOBJECTS - 1;
}

说明[1]

py将PyIntObject->ob_type作为free_list的临时next指针,使用了指针强制转换,虽然破坏了指针的安全原则,但是重用了>ob_type内存空间,不失为一种好方法!下图描绘了两个PyIntBlock构成的通用整数缓冲池:

2. 其余两个构建和删除整数对象相关函数:

//构建intobj
PyObject * PyInt_FromLong(long ival)
{
    register PyIntObject *v;
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
    if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
        v = small_ints[ival + NSMALLNEGINTS];
        Py_INCREF(v);
#ifdef COUNT_ALLOCS
        if (ival >= 0)
            quick_int_allocs++;
        else
            quick_neg_int_allocs++;
#endif
        return (PyObject *) v;
    }
#endif
    if (free_list == NULL) { //[1]
        if ((free_list = fill_free_list()) == NULL)
            return NULL;
    }
    /* Inline PyObject_New */
    v = free_list;
    //[2]
    free_list = (PyIntObject *)Py_TYPE(v);
    PyObject_INIT(v, &PyInt_Type);
    v->ob_ival = ival;
    return (PyObject *) v;
}
[1]缓冲池的空闲链表为空,通过fill_free_list()去申请新的PyIntBlock
[2](PyIntObject *)Py_TYPE(v)相当于是PyIntObject在free_list中的next指针。

//删除intobj
static void int_dealloc(PyIntObject *v)
{
    if (PyInt_CheckExact(v)) { //[1]
        Py_TYPE(v) = (struct _typeobject *)free_list;
        free_list = v;
    }
    else //[2]
        Py_TYPE(v)->tp_free((PyObject *)v);
}
[1] 判定如果v的引用计数为1(经过本次解引用变为0),则将该PyIntObject空间加入到缓冲池的空闲队列,以便重用
[2]引用计数>2 将该对象引用计数减1

Python源码--整数对象(PyIntObject)的内存池

时间: 2024-10-19 14:53:57

Python源码--整数对象(PyIntObject)的内存池的相关文章

[python 源码]整数对象的创建和维护

刚开始学python时候,发现一个很迷惑的现象,一直到看了源码后才知道了: >>> a=6 >>> b=6 >>> a is b True 想用同样的参数初始化两个对象,结果却是,这两个对象其实是同样的对象????逗我呢? >>> a=666 >>> b=666 >>> a is b False 这又是怎么回事?为什么现在又是False了??? 这些不同,主要来自于python中对待小整数和大整数的

Python源代码--整数对象(PyIntObject)的内存池

[背景] 原文链接:http://blog.csdn.net/ordeder/article/details/25343633 Python整数对象是不可变对象,什么意思呢?比如运行例如以下python语句 >>>a = 1023 >>>a = 1024 >>>b = a >>>c = 1024 >>>d = 195 >>>e = 195 python的整数对象结构为: typedef struct

《python源码剖析》笔记 pythonm内存管理机制

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.内存管理架构 Python的内存管理机制都有两套实现:debug模式和release模式 Python内存管理机制的层次结构: 图16-1 第0层是操作系统提供的内存管理接口,如malloc.free 第1层是Python基于第0层操作系统的内存管理接口包装而成的,主要是为了处理与平台相关的内存分配行为. 实现是一组以PyMem_为前缀的函数族 两套接口:函数和宏. 宏,可以避免函数调

[python 源码]字符串对象的实现

还是带着问题上路吧,和整数对象的实现同样的问题: >>> a='abc' >>> b='abc' >>> a is b True >>> c='abc'*10 >>> d='abc'*10 >>> d is c False why?在整数对象的实现中,对待小整数有小整数对象池,对待大整数对申请内存,字符串对象的实验也是这样的吗??? NO 先看下字符串对象的定义: typedef struct{ P

python源码分析----对象结构

虽然自己会吐槽python这语言让人受不了的慢,不过也不得不说python语言在语法上的精炼,在写代码的时候确实会比java方便一些...不过由于python无法像java一样项目的开始先定义一套强类型限制的接口,所以总感觉在写python的时候有点不踏实...不过pycharm从某种程度上通过注释也能减轻一点这方面的担忧.. 好了.....决定了要看一下python的实现...这里就先从python的对象结构来说吧.... 在python中,所有的东西都是对象,整数是,方法也是,..总之什么都

《python源码剖析》笔记 python中的整数对象

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1. PyIntObject --> long的一个简单包装 typedef struct{ PyObject_HEAD long ob_ival; } PyIntObject; PyInt_Type --> PyIntObject的类型对象.与对象相关的元信息实际上都是保存在与对象对应的类型对象中的 PyTypeObject PyInt_Type = { PyObject_HEAD_I

Python源码剖析笔记2-Python整数对象

Python源码剖析笔记2-Python整数对象 本文简书地址: http://www.jianshu.com/p/0136ed90cd46 千里之行始于足下,从简单的类别开始分析,由浅入深也不至于自己丧失信心.先来看看Python整数对象,也就是python中的PyIntObject对象,对应的类型对象是PyInt_Type. 1 Python整数对象概览 为了性能考虑,python中对小整数有专门的缓存池,这样就不需要每次使用小整数对象时去用malloc分配内存以及free释放内存.pyth

《python源码剖析》笔记 python对象初探

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 1.      在python中,对象就是为C中的结构体在堆上申请的一块内存.对象不能被静态初始化,也不能在栈空间生存.但内建的类型对象都是被静态初始化的. 2.      对象创建后大小不变.可变对象有一指针指向可变大小的内存区域. 3.      对象机制的基石:PyObject 定长对象: typedef struct _object{ PyObject_HEAD }PyObject

python源码剖析笔记1——Python对象初见

python源码剖析笔记1--Python对象初见 工作整两年了,用python最多,然而对于python内部机制不一定都清楚,每天沉醉于增删改查的简单逻辑编写,实在耗神.很多东西不用就忘记了,比如C语言,正好,python源码用C写的,分析python源码的同时又能温故C语言基础,实在是件很好的事情.另外,还有陈儒大神的<python源码剖析>做指引,分析也不至于没头没脑.期望在一个月的业余时间,能有所小成,以此为记. 1 python中的对象 python中,一切东西都是对象,在c语言实现