Python 的內建對象存放在源代碼的Objects
目錄下。
intobject.c
用于整數(shù)對象
在 Python 中,整數(shù)分為小整數(shù)對象
和大整數(shù)對象
小整數(shù)對象
由于數(shù)值較小的整數(shù)對象在內存中會很頻繁地使用,如果每次都向內存申請空間、請求釋放,會嚴重影響 Python 的性能。好在 整數(shù)對象 屬于不可變對象,可以被共享而不會被修改導致問題,所以為 小整數(shù)對象 劃定一個范圍,即小整數(shù)對象池,在Python運行時初始化并創(chuàng)建范圍內的所有整數(shù),這個范圍內的 整數(shù)對象是被共享的,即一次創(chuàng)建,多次共享引用。
那么這個范圍是多少呢?從源文件中可以看到,而且,用戶可以自行調整,只是每次都要在源文件中修改,而后進行編譯、安裝。
小整數(shù)池的范圍:
#ifndef NSMALLPOSINTS
#define NSMALLPOSINTS 257
#endif
#ifndef NSMALLNEGINTS
#define NSMALLNEGINTS 5
#endif
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
/* References to small integers are saved in this array so that they
can be shared.
The integers that are saved are those in the range
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
*/
static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
從源代碼可以看出
define NSMALLPOSINTS 257
,范圍的右邊界
define NSMALLNEGINTS 5
,范圍的左邊界
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive)
,[-5, 257)
大整數(shù)對象
但是,整數(shù)對象很多,不一定都是小整數(shù)對象,又不能將所有整數(shù)對象都放入內存。于是,Python 提供了一個可擴展的內存空間,稱為通用整數(shù)對象池
,誰需要用就給誰用,這樣免去了申請空間,又能提高一些效率。
這個空間是一個PyIntBlock結構,是用一個單向列表連接一串內存(block),這個列表由block_list
維護,而每個 block 維護一個 整數(shù)對象數(shù)組(Objects),用于存放被緩存的整數(shù)對象。block_list
的內容是最新創(chuàng)建的 block。
小整數(shù)對象池 也在block_list
上。
Python 使用一個單向鏈表管理全部 block 的 objects 中的所有空閑內存,由free_list
指出下一個可用的空閑內存。如果當前沒有空閑內存,free_list
為NULL
,會創(chuàng)建新的內存。
當整數(shù)對象的引用計數(shù)變?yōu)?,會銷毀對象,但并不會釋放空閑出來的內存,即將內存交還系統(tǒng),而是重新加入free_list
。
hack
使用Xcode
修改打印整數(shù)對象的方法
原始文件
/* ARGSUSED */
static int
int_print(PyIntObject *v, FILE *fp, int flags)
/* flags -- not used but required by interface */
{
long int_val = v->ob_ival;
Py_BEGIN_ALLOW_THREADS
fprintf(fp, "%ld", int_val);
Py_END_ALLOW_THREADS
return 0;
}
修改后,可以打印部分小整數(shù)地址池中,整數(shù)對象的引用次數(shù)、所在內存地址、下一個可用的空閑內存地址
/* ARGSUSED */
static int values[10];
static int refcounts[10];
static int
int_print(PyIntObject *v, FILE *fp, int flags)
/* flags -- not used but required by interface */
{
PyIntObject* intObjectPtr;
PyIntBlock *p = block_list;
PyIntBlock *last = NULL;
int count = 0;
int i;
while(p != NULL)
{
++count;
last = p;
p = p->next;
}
intObjectPtr = last->objects;
intObjectPtr += N_INTOBJECTS - 1;
printf(" address @%p\n", v);
for(i = 0; i < 10; ++i, -- intObjectPtr)
{
values[i] = intObjectPtr -> ob_ival;
refcounts[i] = intObjectPtr -> ob_refcnt;
}
printf(" value : ");
for(i = 0; i < 8; ++i)
{
printf("%d\t", values[i]);
}
printf("\n");
printf(" refcnt : ");
for(i = 0; i < 8; ++i)
{
printf("%d\t", refcounts[i]);
}
printf("\n");
printf(" block_list count : %d\n", count);
printf(" free_list : %p\n", free_list);
return 0;
/* long int_val = v->ob_ival;
Py_BEGIN_ALLOW_THREADS
fprintf(fp, "%ld", int_val);
Py_END_ALLOW_THREADS
return 0;*/
}
保存并編譯、安裝,運行修改后的 Python
Python 2.6.9 (unknown, Nov 1 2015, 20:22:05)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.1.76)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> i = -9999
>>> i
address @0x7fe412f16470 # -9999 所在的內存地址
value : -5 -4 -3 -2 -1 0 1 2 # 能夠顯示的 -5 ~ 2
refcnt : 1 1 1 1 35 105 64 41 # 引用計數(shù)器,可以看到,小整數(shù)已經(jīng)被Python自身使用了多次
block_list count : 8 # block 數(shù)量
free_list : 0x7fe412f16488 # 下一個可用的空閑內存地址
>>>
>>> a = -258
>>> a
address @0x7fe412f16488 # -258 的內存地址,是上面`free_list`指出的空閑內存
value : -5 -4 -3 -2 -1 0 1 2
refcnt : 1 1 1 1 35 105 64 41
block_list count : 8
free_list : 0x7fe412f164a0 # 新的空閑內存地址
>>> b = -258
>>> b
address @0x7fe412f164a0 # 上一個的空閑內存地址,可以看出,對于多次創(chuàng)建的大整數(shù)對象,即使值一樣,也是不同的內存地址
value : -5 -4 -3 -2 -1 0 1 2
refcnt : 1 1 1 1 35 105 64 41
block_list count : 8
free_list : 0x7fe412f164b8
>>> del b # 釋放 b 的內存空間
>>> a
address @0x7fe412f16488
value : -5 -4 -3 -2 -1 0 1 2
refcnt : 1 1 1 1 35 105 64 41
block_list count : 8
free_list : 0x7fe412f164a0 # 刪除 b 后,新的空閑內存重新加入`free_list`,沒有歸還給系統(tǒng)
>>> c1 = -5 # 屬于小整數(shù)對象池
>>> c1
address @0x7fe412f033d8
value : -5 -4 -3 -2 -1 0 1 2
refcnt : 5 1 1 1 35 105 64 41 # -5 引用此時為 5
block_list count : 8
free_list : 0x7fe412f164b8
>>> c2 = -5 # 同上
>>> c2
address @0x7fe412f033d8 # 兩次創(chuàng)建的相同小整數(shù)對象,指向了相同的內存地址
value : -5 -4 -3 -2 -1 0 1 2
refcnt : 6 1 1 1 35 105 64 41 # -5 的引用次數(shù)加一,變?yōu)?6
block_list count : 8
free_list : 0x7fe412f164b8
>>>
整數(shù)對象的說明文件內置在源代碼中:
PyDoc_STRVAR(int_doc,
"int(x[, base]) -> integer\n\
\n\
Convert a string or number to an integer, if possible. A floating point\n\
argument will be truncated towards zero (this does not include a string\n\
representation of a floating point number!) When converting a string, use\n\
the optional base. It is an error to supply a base when converting a\n\
non-string. If base is zero, the proper base is guessed based on the\n\
string content. If the argument is outside the integer range a\n\
long object will be returned instead.");
int_doc
就是整數(shù)對象的__doc__
屬性
參考資料
《Python 源碼剖析》第二章:整數(shù)對象