新聞中心
垃圾回收

清水網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站設(shè)計(jì)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)2013年至今到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。
1.1 refchain
在python的C源碼中有一個(gè)refchain的環(huán)狀雙向鏈表,Python程序當(dāng)中一旦創(chuàng)建對象都會(huì)把這個(gè)對象添加到refchain這個(gè)鏈表當(dāng)中,保存著所有的對象。
name = "皮卡丘" width = 5
1.2引用計(jì)數(shù)器
在refchain中所有對象內(nèi)部都有一個(gè)ob_refcnt用來保存當(dāng)前對象的引用計(jì)數(shù)器
name = "皮卡丘" width = 5 nickname = name
上述代碼表示內(nèi)存中有5和”皮卡丘“兩個(gè)值,他們的引用計(jì)數(shù)器分別為1、2
當(dāng)值被多次引用時(shí)候,不會(huì)再內(nèi)存中重復(fù)創(chuàng)建數(shù)據(jù),而是引用計(jì)數(shù)器+1。當(dāng)對象被銷毀時(shí)候同時(shí)會(huì)讓引用計(jì)數(shù)器-1,如果引用計(jì)數(shù)器為0,則將對象從refchain鏈表中刪除,同時(shí)在內(nèi)存中進(jìn)行銷毀(暫時(shí)不考慮緩存等特殊情況)。
name = "皮卡丘"nickname = name # 對象”皮卡丘“的引用計(jì)數(shù)器+1del name 對象"皮卡丘"的引用計(jì)數(shù)器-1def run(arg): print(arg) run(nickname) # 剛開始執(zhí)行函數(shù)時(shí),對象”皮卡丘“引用計(jì)數(shù)器+1,當(dāng)函數(shù)執(zhí)行完畢之后,對象引用計(jì)數(shù)器-1name_list = ["張三","法外狂徒",name] # 對象”皮卡丘“的引用計(jì)數(shù)器+1
但是這樣還是存在一個(gè)BUG,當(dāng)出現(xiàn)循環(huán)引用的時(shí)候,就會(huì)無法正常的回收一些數(shù)據(jù),例如
v1 = [11,22,33] # refchain中創(chuàng)建一個(gè)列表對象,由于v1=對象,所以列表引對象用計(jì)數(shù)器為1. v2 = [44,55,66] # refchain中再創(chuàng)建一個(gè)列表對象,因v2=對象,所以列表對象引用計(jì)數(shù)器為1. v1.append(v2) # 把v2追加到v1中,則v2對應(yīng)的[44,55,66]對象的引用計(jì)數(shù)器加1,最終為2. v2.append(v1) # 把v1追加到v1中,則v1對應(yīng)的[11,22,33]對象的引用計(jì)數(shù)器加1,最終為2. del v1 # 引用計(jì)數(shù)器-1del v2 # 引用計(jì)數(shù)器-1
對于上面的代碼,執(zhí)行del操作之后,沒有變量再會(huì)去使用那兩個(gè)列表對象,但由于循環(huán)引用的問題,他們的引用計(jì)數(shù)器不為0,所以他們的狀態(tài):永遠(yuǎn)不會(huì)被使用、也不會(huì)被銷毀。項(xiàng)目中如果這種代碼太多,就會(huì)導(dǎo)致內(nèi)存一直被消耗,直到內(nèi)存被耗盡,程序崩潰。
1.3標(biāo)記清除&分代回收
標(biāo)記清除:創(chuàng)建特殊鏈表專門用于保存 列表、元組、字典、集合、自定義類等對象,之后再去檢查這個(gè)鏈表中的對象是否存在循環(huán)引用,如果存在則讓雙方的引用計(jì)數(shù)器均 - 1 。
分代回收:對標(biāo)記清楚中的鏈表進(jìn)行優(yōu)化,將那些可能存在循環(huán)引用的對象拆分到3個(gè)鏈表,鏈表成為0/1/2三代,每代都可以存儲(chǔ)對象和閾值,當(dāng)達(dá)到閾值時(shí),就會(huì)對相應(yīng)的鏈表中的每個(gè)對象做一次掃描,除循環(huán)引用各自減1并且銷毀引用計(jì)數(shù)器為0的對象。`
// 分代的C源碼#define NUM_GENERATIONS 3struct gc_generation generations[NUM_GENERATIONS] = {
/* PyGC_Head, threshold, count */
{{(uintptr_t)_GEN_HEAD(0), (uintptr_t)_GEN_HEAD(0)}, 700, 0}, // 0代
{{(uintptr_t)_GEN_HEAD(1), (uintptr_t)_GEN_HEAD(1)}, 10, 0}, // 1代
{{(uintptr_t)_GEN_HEAD(2), (uintptr_t)_GEN_HEAD(2)}, 10, 0}, // 2代};
0代,count表示0代鏈表中對象的數(shù)量,threshold表示0代鏈表對象個(gè)數(shù)閾值,超過則執(zhí)行一次0代掃描檢查。
1代,count表示0代鏈表掃描的次數(shù),threshold表示0代鏈表掃描的次數(shù)閾值,超過則執(zhí)行一次1代掃描檢查。
2代,count表示1代鏈表掃描的次數(shù),threshold表示1代鏈表掃描的次數(shù)閾值,超過則執(zhí)行一2代掃描檢查。
1.4緩存機(jī)制
實(shí)際上他不是這么的簡單粗暴,因?yàn)榉磸?fù)的創(chuàng)建和銷毀會(huì)使程序的執(zhí)行效率變低。Python中引入了“緩存機(jī)制”機(jī)制。
例如:引用計(jì)數(shù)器為0時(shí),不會(huì)真正銷毀對象,而是將他放到一個(gè)名為free_list的鏈表中,之后會(huì)再創(chuàng)建對象時(shí)不會(huì)在重新開辟內(nèi)存,而是在free_list中將之前的對象來并重置內(nèi)部的值來使用。
float類型,維護(hù)的free_list鏈表最多可緩存100個(gè)float對象。
1. `v1 = 3.14 # 開辟內(nèi)存來存儲(chǔ)float對象,并將對象添加到refchain鏈表。` 2. `print( id(v1) ) # 內(nèi)存地址:4436033488` 3. `del v1 # 引用計(jì)數(shù)器-1,如果為0則在rechain鏈表中移除,不銷毀對象,而是將對象添加到float的free_list.` 4. `v2 = 9.999 # 優(yōu)先去free_list中獲取對象,并重置為9.999,如果free_list為空才重新開辟內(nèi)存。` 5. `print( id(v2) ) # 內(nèi)存地址:4436033488` 7. `# 注意:引用計(jì)數(shù)器為0時(shí),會(huì)先判斷free_list中緩存?zhèn)€數(shù)是否滿了,未滿則將對象緩存,已滿則直接將對象銷毀。`
int類型,不是基于free_list,而是維護(hù)一個(gè)small_ints鏈表保存常見數(shù)據(jù)(小數(shù)據(jù)池),小數(shù)據(jù)池范圍:-5 <= value < 257。即:重復(fù)使用這個(gè)范圍的整數(shù)時(shí),不會(huì)重新開辟內(nèi)存。
v1 = 38 # 去小數(shù)據(jù)池small_ints中獲取38整數(shù)對象,將對象添加到refchain并讓引用計(jì)數(shù)器+1。 print( id(v1)) #內(nèi)存地址:4514343712 v2 = 38 # 去小數(shù)據(jù)池small_ints中獲取38整數(shù)對象,將refchain中的對象的引用計(jì)數(shù)器+1。 print( id(v2) ) #內(nèi)存地址:4514343712 # 注意:在解釋器啟動(dòng)時(shí)候-5~256就已經(jīng)被加入到small_ints鏈表中且引用計(jì)數(shù)器初始化為1,代碼中使用的值時(shí)直接去small_ints中拿來用并將引用計(jì)數(shù)器+1即可。另外,small_ints中的數(shù)據(jù)引用計(jì)數(shù)器永遠(yuǎn)不會(huì)為0(初始化時(shí)就設(shè)置為1了),所以也不會(huì)被銷毀。
str類型,維護(hù)unicode_latin1[256]鏈表,內(nèi)部將所有的ascii字符緩存起來,以后使用時(shí)就不再反復(fù)創(chuàng)建。
v1 = "A" print( id(v1) ) # 輸出:4517720496 del v1 v2 = "A" print( id(v1) ) # 輸出:4517720496 # 除此之外,Python內(nèi)部還對字符串做了駐留機(jī)制,針對那么只含有字母、數(shù)字、下劃線的字符串(見源碼Objects/codeobject.c),如果內(nèi)存中已存在則不會(huì)重新在創(chuàng)建而是使用原來的地址里(不會(huì)像free_list那樣一直在內(nèi)存存活,只有內(nèi)存中有才能被重復(fù)利用)。 v1 = "wupeiqi" v2 = "wupeiqi" print(id(v1) == id(v2)) # 輸出:True
list類型,維護(hù)的free_list數(shù)組最多可緩存80個(gè)list對象。
v1 = [11,22,33] print( id(v1) ) # 輸出:4517628816 del v1 v2 = ["小豬","佩奇"] print( id(v2) ) # 輸出:4517628816
tuple類型,維護(hù)一個(gè)free_list數(shù)組且數(shù)組容量20,數(shù)組中元素可以是鏈表且每個(gè)鏈表最多可以容納2000個(gè)元組對象。元組的free_list數(shù)組在存儲(chǔ)數(shù)據(jù)時(shí),是按照元組可以容納的個(gè)數(shù)為索引找到free_list數(shù)組中對應(yīng)的鏈表,并添加到鏈表中。
v1 = (1,2)
print( id(v1) ) del v1 # 因元組的數(shù)量為2,所以會(huì)把這個(gè)對象緩存到free_list[2]的鏈表中。
v2 = ("小豬","佩奇") # 不會(huì)重新開辟內(nèi)存,而是去free_list[2]對應(yīng)的鏈表中拿到一個(gè)對象來使用。
print( id(v2) )
dict類型,維護(hù)的free_list數(shù)組最多可緩存80個(gè)dict對象。
v1 = {"k1":123}
print( id(v1) ) # 輸出:4515998128
del v1
v2 = {"name":"武沛齊","age":18,"gender":"男"}
print( id(v1) ) # 輸出:4515998128
2 C語言源碼分析
2.1兩個(gè)重要的結(jié)構(gòu)體
#define PyObject_HEAD PyObject ob_base;#define PyObject_VAR_HEAD PyVarObject ob_base;// 宏定義,包含 上一個(gè)、下一個(gè),用于構(gòu)造雙向鏈表用。(放到refchain鏈表中時(shí),要用到)#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;typedef struct _object {
_PyObject_HEAD_EXTRA // 用于構(gòu)造雙向鏈表
Py_ssize_t ob_refcnt; // 引用計(jì)數(shù)器
struct _typeobject *ob_type; // 數(shù)據(jù)類型} PyObject;typedef struct {
PyObject ob_base; // PyObject對象
Py_ssize_t ob_size; /* Number of items in variable part,即:元素個(gè)數(shù) */} PyVarObject;
這兩個(gè)結(jié)構(gòu)體PyObject和PyVarObject是基石,他們保存這其他數(shù)據(jù)類型公共部分,例如:每個(gè)類型的對象在創(chuàng)建時(shí)都有PyObject中的那4部分?jǐn)?shù)據(jù);list/set/tuple等由多個(gè)元素組成對象創(chuàng)建時(shí)都有PyVarObject中的那5部分?jǐn)?shù)據(jù)。
2.2常見類型結(jié)構(gòu)體
平時(shí)我們在創(chuàng)建一個(gè)對象時(shí),本質(zhì)上就是實(shí)例化一個(gè)相關(guān)類型的結(jié)構(gòu)體,在內(nèi)部保存值和引用計(jì)數(shù)器等。
float類型
typedef struct {
PyObject_HEAD double ob_fval;
} PyFloatObject;
int類型
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
}; /* Long (arbitrary precision) integer object interface */
typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */
str類型
typedef struct {
PyObject_HEAD
Py_ssize_t length; /* Number of code points in the string */
Py_hash_t hash; /* Hash value; -1 if not set */
struct {
unsigned int interned:2; /* Character size:
- PyUnicode_WCHAR_KIND (0):
* character type = wchar_t (16 or 32 bits, depending on the
platform)
- PyUnicode_1BYTE_KIND (1):
* character type = Py_UCS1 (8 bits, unsigned)
* all characters are in the range U+0000-U+00FF (latin1)
* if ascii is set, all characters are in the range U+0000-U+007F
(ASCII), otherwise at least one character is in the range
U+0080-U+00FF
- PyUnicode_2BYTE_KIND (2):
* character type = Py_UCS2 (16 bits, unsigned)
* all characters are in the range U+0000-U+FFFF (BMP)
* at least one character is in the range U+0100-U+FFFF
- PyUnicode_4BYTE_KIND (4):
* character type = Py_UCS4 (32 bits, unsigned)
* all characters are in the range U+0000-U+10FFFF
* at least one character is in the range U+10000-U+10FFFF
*/
unsigned int kind:3; unsigned int compact:1; unsigned int ascii:1; unsigned int ready:1; unsigned int :24;
} state; wchar_t *wstr; /* wchar_t representation (null-terminated) */
} PyASCIIObject; typedef struct {
PyASCIIObject _base;
Py_ssize_t utf8_length; /* Number of bytes in utf8, excluding the
* terminating \0. */
char *utf8; /* UTF-8 representation (null-terminated) */
Py_ssize_t wstr_length; /* Number of code points in wstr, possible
* surrogates count as two code points. */
} PyCompactUnicodeObject; typedef struct {
PyCompactUnicodeObject _base; union { void *any;
Py_UCS1 *latin1;
Py_UCS2 *ucs2;
Py_UCS4 *ucs4;
} data; /* Canonical, smallest-form Unicode buffer */
} PyUnicodeObject;
list類型
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;
tuple類型
typedef struct {
PyObject_VAR_HEAD
PyObject *ob_item[1];
} PyTupleObject;
dict類型
typedef struct {
PyObject_HEAD
Py_ssize_t ma_used;
PyDictKeysObject *ma_keys;
PyObject **ma_values;
} PyDictObject;
通過常見結(jié)構(gòu)體可以基本了解到本質(zhì)上每個(gè)對象內(nèi)部會(huì)存儲(chǔ)的數(shù)據(jù)。
擴(kuò)展:在結(jié)構(gòu)體部分你應(yīng)該發(fā)現(xiàn)了str類型比較繁瑣,那是因?yàn)閜ython字符串在處理時(shí)需要考慮到編碼的問題,在內(nèi)部規(guī)定(見源碼結(jié)構(gòu)體):
字符串只包含ascii,則每個(gè)字符用1個(gè)字節(jié)表示,即:latin1字符串包含中文等,則每個(gè)字符用2個(gè)字節(jié)表示,即:ucs2字符串包含emoji等,則每個(gè)字符用4個(gè)字節(jié)表示,即:ucs4
分享文章:創(chuàng)新互聯(lián)Python教程:大話Python的垃圾回收機(jī)制
轉(zhuǎn)載源于:http://m.jiaoqi3.com/article/djoiphc.html


咨詢
建站咨詢
