python源码研究之整型对象探索

字数 2250阅读 52


1.python的整型对象是PyIntObject对象,这个对象是一个不可变对象,即没有ob_size这个变量,这个对象在c层面实现,只是在基本的pyobject中添加了long ob_ival对象,结构体大概是这样的:

typedef struct{

PyObject_HEAD;

long ib_ival

} PyIntObject;

而这个对象对应的类型变量就比较复杂了,很长:

这里面就是int这个类型的一下名称啊,内存大小,以及操作的信息,操作信息是最多的包括:析构函数,比较操作,获取HASH值,打印,都在这里了。

2.说到python里面的整型是定长对象,那么是不是很好奇定长是多少呢,经过研究发现是24个字节,那么为什么是24个字节呢,看上面的结构体,int型4个字节,long型8个字节,指针4个字节,加起来4+4+8=16个字节,这里我开始也是不明白,后来突然想到噢,c语言的结构体有内存对其,所以内存直接就是3*8=24个字节,不行可以试试,申请的整型都是24个字节。

3.下面说说整型的创建,Python是调用c api创建的整型,Python的整型分为小整数和大整数,那么这个小大是怎么区分的,是根据编译源码python时NSMALLPOSINTS区分的,正常的默认编译是[-5,256]是小整数,其他的为大整数,之所以这么分是因为小整数频繁被使用,如果每一个都调用c语言的malloc分配内存,使用完再释放效率很低,所以python使用的内存缓冲池,初始化的时候,python就已经把小整数的内存申请好了,所以小整数的内存地址是不变的。而大整数不是,是动态的,使用的时候申请,所以就有了一个奇怪的东西(这个东西探寻良久,现在醍醐灌顶):

你初始化一个变量在[-5,257]范围内时id(这个变量),内存地址都一样,而初始化[-5,257]以外的数值时,每一次都是变化的,见下图:

(不会缩小图~~)上面就是这个意思了 两个变量都为2 地址一样,两个变量都是-10地址不一样

4.小整数对象就是初始化的都准备好了,在内存申请好了,就像一块死的东西,你需要的时候直接一个引用指过去就行了,引用加1,地址都是一样的,不会新申请,所以很快。

5.大整数不一样了,大整数是存储在一个PyIntBlock中的,PyIntBlock是一个结构体,如下图

这个结构体的大小是1000字节,一个整数是12个字(32位的机器)节所以 1000/12 = 82个整型,也就是一个PyIntBlock可以有82个整型。

继续说,PyIntBlock中的block_list是已经使用了多少内存,已经分配了的内存指针,free_list是没有分配的剩余的内存指针,开始时这俩都是NULL,因为是一个新的PyIntBlock,注意这里block是每一个PyIntBlock的使用内存的情况,而free_list是所有的剩余内存情况,补一句,这些存储单元都是单项链表。

6.所以综上就可以知道,当free_list为NULL的时候,就需要新建PyIntBlock,此时调用fill_free_list方法,把空闲的快用单向链表链接起来给free_list,新建一个int对象时,在free_list拿一块初始化数值,加入Block_list就行了。

7.这里有个坑,当A,B两个PyIntBlock,A已经全部使用,B使用一半,A某一个元素释放了的时候,实际是没有释放,二十加入了Free_List,这里导致的就是新申请的int类型永远不会释放掉,而是给free_list了,(这里我也不明白。。这不就是内存泄露了,。。。思考中)

8.ok就写介个多吧,下次继续。​

1.python的整型对象是PyIntObject对象,这个对象是一个不可变对象,即没有ob_size这个变量,这个对象在c层面实现,只是在基本的pyobject中添加了long ob_ival对象,结构体大概是这样的:

typedef struct{

PyObject_HEAD;

long ib_ival

} PyIntObject;

而这个对象对应的类型变量就比较复杂了,很长:

这里面就是int这个类型的一下名称啊,内存大小,以及操作的信息,操作信息是最多的包括:析构函数,比较操作,获取HASH值,打印,都在这里了。

2.说到python里面的整型是定长对象,那么是不是很好奇定长是多少呢,经过研究发现是24个字节,那么为什么是24个字节呢,看上面的结构体,int型4个字节,long型8个字节,指针4个字节,加起来4+4+8=16个字节,这里我开始也是不明白,后来突然想到噢,c语言的结构体有内存对其,所以内存直接就是3*8=24个字节,不行可以试试,申请的整型都是24个字节。

3.下面说说整型的创建,Python是调用c api创建的整型,Python的整型分为小整数和大整数,那么这个小大是怎么区分的,是根据编译源码python时NSMALLPOSINTS区分的,正常的默认编译是[-5,256]是小整数,其他的为大整数,之所以这么分是因为小整数频繁被使用,如果每一个都调用c语言的malloc分配内存,使用完再释放效率很低,所以python使用的内存缓冲池,初始化的时候,python就已经把小整数的内存申请好了,所以小整数的内存地址是不变的。而大整数不是,是动态的,使用的时候申请,所以就有了一个奇怪的东西(这个东西探寻良久,现在醍醐灌顶):

你初始化一个变量在[-5,257]范围内时id(这个变量),内存地址都一样,而初始化[-5,257]以外的数值时,每一次都是变化的,见下图:

(不会缩小图~~)上面就是这个意思了 两个变量都为2 地址一样,两个变量都是-10地址不一样

4.小整数对象就是初始化的都准备好了,在内存申请好了,就像一块死的东西,你需要的时候直接一个引用指过去就行了,引用加1,地址都是一样的,不会新申请,所以很快。

5.大整数不一样了,大整数是存储在一个PyIntBlock中的,PyIntBlock是一个结构体,如下图

这个结构体的大小是1000字节,一个整数是12个字(32位的机器)节所以 1000/12 = 82个整型,也就是一个PyIntBlock可以有82个整型。

继续说,PyIntBlock中的block_list是已经使用了多少内存,已经分配了的内存指针,free_list是没有分配的剩余的内存指针,开始时这俩都是NULL,因为是一个新的PyIntBlock,注意这里block是每一个PyIntBlock的使用内存的情况,而free_list是所有的剩余内存情况,补一句,这些存储单元都是单项链表。

6.所以综上就可以知道,当free_list为NULL的时候,就需要新建PyIntBlock,此时调用fill_free_list方法,把空闲的快用单向链表链接起来给free_list,新建一个int对象时,在free_list拿一块初始化数值,加入Block_list就行了。

7.这里有个坑,当A,B两个PyIntBlock,A已经全部使用,B使用一半,A某一个元素释放了的时候,实际是没有释放,二十加入了Free_List,这里导致的就是新申请的int类型永远不会释放掉,而是给free_list了,(这里我也不明白。。这不就是内存泄露了,。。。思考中)

8.ok就写介个多吧,下次继续。

推荐阅读更多精彩内容