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
时间: 2024-10-05 11:50:19

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

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 {

zg手册 之 python2.7.7源码分析(2)-- python 的整数对象和字符串对象

python 中的内置对象 python 中常用的内置对象有:整数对象,字符串对象,列表对象,字典对象.这些对象在python中使用最多,所以在实现上提供缓存机制,以提高运行效率. 整数对象 (PyIntObject) python 中的整数对象是不可变对象(immutable),即创建了一个 python 整数对象之后,不能再改变该对象的值. python 为创建整数对象提供了下面三种方法,其中 PyInt_FromString 和 PyInt_FromUnicode 内部也是调用 PyInt

python 整数对象PyIntObject的创建和维护

整数对象的创建有以下几种,并非书中所述3种 从源码中看到最终都是调用PyInt_FromLong,书中写的PyInt_FromFloat,需要注意这点. 所以重点阅读函数PyInt_FromLong: 方便用户直接取用,小整数对象池是python运行是就必须存在的.按照这个思路,那么小整数对象池的初始化就应该在PyIntObject的_init中,在源码中也证实了这个思路: small_ints数组管理着小整数对象的指针.在PyInt_FromLong函数中,小数值对象就是在这个数组中取出来的.

Python中小整数对象池和大整数对象池

1. 小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间. Python 对小整数的定义是 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收.在一个 Python 的程序中,所有位于这个范围内的整数使用的都是同一个对象. 同理,单个字母也是这样的.

python:深拷贝,浅拷贝,内存管理

深拷贝和浅拷贝都是对象的拷贝,本质的区别是拷贝出来的对象的地址是否和原对象一样,也就是地址的复制还是值的复制的区别. 可变对象:直接在对象所指的地址上把值改了,这个对象依然指向这个地址. 不可变对象:一个对象所指向的地址上的值是不能修改的,如果修改了这个对象的值,它所指向的地址就改变了. 深拷贝就是完全跟以前就没有任何关系了,原来的对象怎么改都不会影响当前对象 浅拷贝,原对象的list元素改变的话会改变当前对象,如果当前对象中list元素改变了,也同样会影响原对象. 内存管理机制 python的

boost内存池的使用介绍

Boost库的pool提供了一个内存池分配器,用于管理在一个独立的.大的分配空间里的动态内存分配. Boost库的pool主要适用于快速分配同样大小的内存块,尤其是反复分配和释放同样大小的内存块的情况.使用pool内存池主要有以下两个优点: 1. 能够有效地管理许多小型对象的分配和释放工作,避免了自己去管理内存而产生的内存碎片和效率低下问题. 2.  告别程序内存泄漏的烦恼,pool库会在内部对内存自动进行管理,避免了程序员一不小心而造成的内存泄漏问题. pool库主要提供了四种内存池接口,分别

【源码剖析】MemoryPool —— 简单高效的内存池 allocator 实现

什么是内存池?什么是 C++ 的 allocator? 内存池简单说,是为了减少频繁使用 malloc/free new/delete 等系统调用而造成的性能损耗而设计的.当我们的程序需要频繁地申请和释放内存时,频繁地使用内存管理的系统调用可能会造成性能的瓶颈,嗯,是可能,毕竟操作系统的设计也不是盖的(麻麻说把话说太满会被打脸的(⊙v⊙)).内存池的思想是申请较大的一块内存(不够时继续申请),之后把内存管理放在应用层执行,减少系统调用的开销. 那么,allocator 呢?它默默的工作在 C++

转 内存池技术的原理与实现

内存池技术的原理与实现 序言 最近在网上看到了几篇篇讲述内存池技术的文章,有一篇是有IBM中国研发中心的人写的,写的不错~~文章地址在本篇blog最后.原文的讲述比我的要清晰很多,我在这只是把我的一些理解和遇到的一些问题和大家分享一下~~ 一.为什么要使用内存池技术呢 主要有两个原因:1.减少new.delete次数,减少运行时间:2.避免内存碎片. 1.效率 c语言中使用malloc/free来分配内存,c++中使用new/delete来分配内存,他们的内存申请与释放都是与操作系统进行交互的.

整数对象池

Python 的内建对象存放在源代码的Objects目录下.intobject.c用于整数对象 在 Python 中,整数分为小整数对象和大整数对象 小整数对象 由于数值较小的整数对象在内存中会很频繁地使用,如果每次都向内存申请空间.请求释放,会严重影响 Python 的性能.好在 整数对象 属于不可变对象,可以被共享而不会被修改导致问题,所以为 小整数对象 划定一个范围,即小整数对象池,在Python运行时初始化并创建范围内的所有整数,这个范围内的 整数对象是被共享的,即一次创建,多次共享引用