Python的垃圾回收机制(二)之内存模型

0. 背景

之前介绍了Python垃圾回收的简介,它简要介绍了三种垃圾回收器:引用计数回收器,标记清除回收器和世代回收器,这里会给出Python中基础类型在C语言中的内存模型,以帮助我们理解下一节三种垃圾回收器结合使用流程。

1. Python对象内存模型

首先介绍一下Python对象的内存模型,如下图1所示:

Python的垃圾回收机制(二)之内存模型_第1张图片
图1. PyObject对象内存模型

上图可以看到,一个PyObject必须包含ob_refcntob_typeob_refcnt是这个对象的引用计数,而ob_type则是指向_typeobject结构体的指针,它是Python内部的一种特殊对象,它是用来制定一个对象类型的类型对象,所以上图中它指向了一个PyTypeObject

在PyTypeObject中定义了大量的函数指针,这些函数指针最终都会指向某个函数,或者指向NULL。这些函数指针可以视为类型对象中所定义的操作,而这些操作直接决定着一个对象在运行时所表现出的行为。

在Python中,对象机制的核心其实非常简单,一个是引用计数,一个是类型信息。

a). 定长对象(Fixed-sized)

我们再来看定长对象None, Int,List,其结构如下图2所示:

Python的垃圾回收机制(二)之内存模型_第2张图片

图2. None, Int, List对象内存模型

上图可以看到,None对象的内存模型是与PyObject一致的;而Int对象则是多了一个ob_ival,这个字段其实就是存储Int真实的value。

问题一:令人奇怪的是,在Python中,List其实是一个可变长的,类似于C++中的vector的数据结构,为什么它的内存模型是定长的呢?

其实它的内存模型的字段都是元数据(meta-data),而真正存放数组数据的是ob_item指向的一个数组。

问题二:在PyListObject对象中,有一个ob_size,而在最后为什么又有一个allocated,那么这两个变量之间的关系是什么呢?

其实,ob_sizeallocated都和PyListObject对象的内存管理有关,PyListObject所采用的内存管理策略和C++中的vector采取内存管理策略是一样的。在每一次需要申请内存的时候, PyListObject总会申请一大块内存,这是申请的总内存的大小记录在allocated中,而实际被使用了的内存的数量则记录在了ob_size中。

b). 变长对象(Variable-sized)

我们再来看看变长对象的内存模型,例如tuple,它的结构如下图3所示:

Python的垃圾回收机制(二)之内存模型_第3张图片
图3. tuple对象内存模型

上图就可以解释,为什么tuple在python中使用是一个定长的list,为什么在PyObject中却是变长对象了。

而string的内存模型如下图所示:

Python的垃圾回收机制(二)之内存模型_第4张图片
图4. string对象内存模型

在创建PyStringObject对象时,除了为PyString_Object申请内存,还有为字符数组内的元素申请额外的内存(绿色填充的字符数组内存)。然后将hash缓存值设为-1,将参数str指向的字符数组内的字符拷贝到PyStringObject所维护的空间中,在拷贝过程中,将字符数组最后的'\0'字符也拷贝了。

c). All is Object

上面讲述了很多python的基础数据类型,而这些基础类型(包括PyTypeObject)都是对象(All is Object)。每个对象至少包含了ob_refcntob_type两个字段,如果是32bit的操作系统,那么他们共是8个字节。

2. 总结

本节主要罗列在Python基础类型在c语言中的内存模型,也是深入理解Python 垃圾回收机制的预备知识。欢迎关注csdz的github

你可能感兴趣的:(Python)