【背景】

原文链接: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)的内存池的更多相关文章

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

    1. 小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间. Python 对小整数的定义是 [-5, 256] 这些整数对象是提 ...

  2. python中小整数对象池及intern机制

    小整数对象池: Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁 Python 对小整数的定义是 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收,所有位于这个范围 ...

  3. linux内存源码分析 - 内存池

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 内存池是用于预先申请一些内存用于备用,当系统内存不足无法从伙伴系统和slab中获取内存时,会从内存池中获取预留的 ...

  4. Boost内存池使用与测试

    目录 Boost内存池使用与测试 什么是内存池 内存池的应用场景 安装 内存池的特征 无内存泄露 申请的内存数组没有被填充 任何数组内存块的位置都和使用operator new[]分配的内存块位置一致 ...

  5. Linux设备驱动程序 之 内存池

    内核中有些地方的内存分配是不允许失败的,为了确保这种情况下的成功分配,内核开发者建立了一种称为内存池的抽象:内存池其实就是某种形式的后备高速缓存,它试图始终保存空闲的内存,以便在紧急状态下使用: me ...

  6. python tips:小整数对象池与字符串intern

    本文为is同一性运算符的详细解释.is用于判断两个对象是否为同一个对象,具体来说是两个对象在内存中的位置是否相同. python为了提高效率,节省内存,在实现上大量使用了缓冲池技术和字符串intern ...

  7. Python 源码剖析(二)【整数对象】

    二.整数对象 1.PyIntObject 2.PyIntObject 对象的创建和维护 3.Hack PyIntObject 1.PyIntObject PyIntObject的定义: [intobj ...

  8. python 小整数池 和intern 【整理】

    小整数对象池 (在python内置了) 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间. Python对小整数的定义是[-5,257]这些整 ...

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

    刚开始学python时候,发现一个很迷惑的现象,一直到看了源码后才知道了: >>> a=6 >>> b=6 >>> a is b True 想用同 ...

随机推荐

  1. [转]automaticallyAdjustsScrollViewInsets(个人认为iOS7中略坑爹的属性)

    @当我们在一个UIViewController中同时创建2个tableView的时候,如果把它们的frame中的Y坐标设置为一样,你可能会发现它们的位置并没有达到你想要的结果.比如第一tableVie ...

  2. 与LCD_BPP相关的函数

    board/freescale/mx6q_sabresd/mx6q_sabresd.c:    panel_info.vl_bpix = LCD_BPP; common/lcd.c:   off  = ...

  3. 菜鸟的《Linux程序设计》学习——MySQL数据库安装、配置及基本操作

    1. MySQL数据库: 在涉及到一些大型的Web系统或者嵌入式软件的开发时,都少不了用数据库来管理数据.在Windows操作系统下,使用过各种各样的数据库,如:sqlServer.Oracle.My ...

  4. BFS:UVa220 ACM/ICPC 1992-Othello(黑白棋)

    Othello Othello is a game played by two people on an 8 x 8 board, using disks that are white on one ...

  5. LDAP学习小结【仅原理和基础篇】

    此篇文章花费了好几个晚上,大部分是软件翻译的英文文档,加上自己的理解所写,希望学习者能尊重每个人的努力. 我有句话想送给每个看我文章的人: 慢就是快,快就是慢!!! 另外更希望更多人能从认真从原理学习 ...

  6. 关于Relay Log无法自动删除的问题

    本文介绍了一次运维实践中relay-log长期无法自动删除的原因和解决过程 背景: 今天在运维一个mysql实例时,发现其数据目录下的relay-log 长期没有删除,已经堆积了几十个relay-lo ...

  7. [adb 连接不上的原因] 汇总

    http://www.cnblogs.com/dazhao/p/6534128.html 查看android studio 的sdk路径( 点击工具栏 ?号旁边的类似下载按钮) SDK_Manager ...

  8. springboot的使用体验和思考

    首先,写这篇博客的背景: 1,通过maven使用springboot创建项目,进行了简单的页面跳转,并未编写service和DAL层,也就是说,并未整合持久化框架 2,阅读了maven的官方文档.sp ...

  9. 九度oj 题目1088:剩下的树

    题目描述: 有一个长度为整数L(1<=L<=10000)的马路,可以想象成数轴上长度为L的一个线段,起点是坐标原点,在每个整数坐标点有一棵树,即在0,1,2,...,L共L+1个位置上有L ...

  10. 【Luogu】P2894酒店Hotel(线段树)

    题目链接 我好蒻啊   题题看题解 线段树维护从左端点开始的最长连续空房.右端点结束的最长连续空房.整段区间的最长连续空房.区间非空房的个数. http://blog.csdn.net/qq_3955 ...