PyListObject对象

1.PyListObject对象的定义
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item; //指向存储列表对象指针数组的首地址
int allocated;  //列表可以容纳的元素数目,注意这个和ob_size不同。ob_size表示已有元素数目,allocated表示能容纳的最多元素数目
} PyListObject;

这里写图片描述

0 <= ob_size <= allocated
len(list) == ob_size
ob_item == NULL implies ob_size == allocated == 0

这里ob_size和allocated的关系就像C++的vector中size和capacity的关系一样。

//Python中只提供了唯一一种创建PyListObject对象的方法—PyList_New:

[listobject.c]


PyObject* PyList_New(int size)
{
    PyListObject *op;
    size_t nbytes;


    nbytes = size * sizeof(PyObject *);
    /* Check for overflow */
    if (nbytes / sizeof(PyObject *) != (size_t)size)
        return PyErr_NoMemory();


    //为PyListObject申请空间
    if (num_free_lists) {
        //使用缓冲池
        num_free_lists--;
        op = free_lists[num_free_lists];
        _Py_NewReference((PyObject *)op);
} else {
        //缓冲池中没有可用的对象,创建对象
        op = PyObject_GC_New(PyListObject, &PyList_Type);
}
//为PyListObject对象中维护的元素列表申请空间
    if (size <= 0)
        op->ob_item = NULL;
    else {
        op->ob_item = (PyObject **) PyMem_MALLOC(nbytes);
        memset(op->ob_item, 0, nbytes);
    }
    op->ob_size = size;
    op->allocated = size;
    _PyObject_GC_TRACK(op);
    return (PyObject *) op;
}
创建过程大致是:

1.检查size参数是否有效,如果小于0,直接返回NULL,创建失败
2. 检查size参数是否超出Python所能接受的大小,如果大于PY_SIZE_MAX(64位机器为8字节,在32位机器为4字节),内存溢出。
3. 检查缓冲池free_list是否有可用的对象,有则直接从缓冲池中使用,没有则创建新的PyListObject,分配内存。
4. 初始化ob_item中的元素的值为Null
5. 设置PyListObject的allocated和ob_size。

说明:PyListObject和PyStringObject一样,都是变长对象,因此都有头PyObject_VAR_HEAD, 不同点是,PyStringObject是不可变对象,一旦创建,字符串内容不可改变;PyListObject则不同,可以再运行过程中动态删除、修改或新增元素。注意ob_size和allocated的关系:0 <= ob_size <= allocated

2.内存管理策略

每次申请内存是,PyListObject对象会申请一大块内存,而不是有多少元素申请多少元素,申请的总大小存储在allocated中。这样做是为了提高效率

3.修改、插入及删除操作

修改操作最简单,直接定位到相应位置替换掉对应位置的对象指针即可。如a = 100, ls[2] = a, 实际操作:(ls->ob_item + 2) = &a
插入操作:
对应两种操作,ls.insert(3, ‘new value’); ls.append(‘new value’),这两个函数操作流程几乎相同
step1: 检查ls->ob_size和ls->allocated的关系,判断是否需要重新分配内存,如果ob_size < allocated,则无需重新分配内存,转step3,否则转step2重新分配内存(这一步并不完全,实际上还会检查如果allocated> 2*ob_size,也会重新分配内存,目的是释放过多的空闲内存)
step2:重新分配内存, 注意这步需要重新申请内存空间,然后将元ob_item的数据拷贝到新申请的空间
step3:在相应位置插入元素,其后所有元素需要往后移动.(这和C++中的Vector类似,插入操作效率较低)
删除操作:
删除操作将被删除对象对应的指针删除之后,其后的所有指针都要向前移动,和C++中的Vector类似,删除效率较低
注意:
删除操作可以删除单个元素,也可以删除片段,如del ls[1]; del ls[1 : 3]
替换也可以替换一个片段, 如ls[2:4] = [1,3,4,5],这个操作实际上是通过内存拷贝来实现

4.PyListObject缓冲池

Python系统会为List对象维护一个默认大小为80的对象缓冲池,每次创建新的List对象时,系统会坚持缓冲池中有没有空余空间,若有,则直接使用,重新围棋分配ob_item的空间,否则重新创建List对象。

/* Empty list reuse scheme to save calls to malloc and free */
#define MAXFREELISTS 80
static PyListObject *free_lists[MAXFREELISTS];
static int num_free_lists = 0;

当PyListObject对象被销毁的时候,首先将列表中所有元素的引用计数减一,然后释放ob_item占用的内存,只要缓冲池空间还没满,那么就把该PyListObject加入到缓冲池中(此时PyListObject占用的内存并不会正真正回收给系统,下次创建PyListObject优先从缓冲池中获取PyListObject),否则释放PyListObject对象的内存空间。

该缓冲池初大小为0,在销毁List对象时会被分配空间。具体如下:
当销毁某List对象时,实际上分两步。第一步先销毁List维护的元素空间,即ob_item指向的内存空间,然后销毁List本身。当销毁完ob_item对应的空间之后,系统会检查缓冲池大小是否小于80,若小于80,系统不会真正销毁List对象本身,而是将其挂载到List缓冲区等待下次使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值