源碼解析python的內(nèi)存回收機(jī)制
一:建立對(duì)象引用計(jì)數(shù)
1. 相關(guān)代碼
void _Py_NewReference(PyObject *op) { if (_Py_tracemalloc_config.tracing) { _PyTraceMalloc_NewReference(op); } #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif Py_SET_REFCNT(op, 1); #ifdef Py_TRACE_REFS _Py_AddToAllObjects(op, 1); #endif }
2. 代碼解釋
_Py_NewReference
這個(gè)函數(shù)的主要目的是為新創(chuàng)建的Python對(duì)象建立引用計(jì)數(shù)。在CPython中,引用計(jì)數(shù)是用來管理內(nèi)存的一種方法,當(dāng)一個(gè)Python對(duì)象的引用計(jì)數(shù)變?yōu)榱銜r(shí),表示沒有其他對(duì)象引用該對(duì)象,因此可以安全地將其內(nèi)存回收。
下面是_Py_NewReference
函數(shù)的各個(gè)部分的簡(jiǎn)要說明:
- _Py_tracemalloc_config.tracing:當(dāng)內(nèi)存追蹤功能啟用時(shí)(即_Py_tracemalloc_config.tracing為真),調(diào)用_PyTraceMalloc_NewReference(op)以記錄新引用的內(nèi)存分配。
- #ifdef Py_REF_DEBUG:如果啟用了引用計(jì)數(shù)調(diào)試(即編譯時(shí)定義了Py_REF_DEBUG),則增加全局引用計(jì)數(shù)_Py_RefTotal。
- Py_SET_REFCNT(op, 1):將新對(duì)象op的引用計(jì)數(shù)設(shè)置為1。
- #ifdef Py_TRACE_REFS:如果啟用了引用跟蹤功能(即編譯時(shí)定義了Py_TRACE_REFS),則調(diào)用_Py_AddToAllObjects(op, 1)將新對(duì)象op添加到所有對(duì)象列表中以進(jìn)行跟蹤。
這個(gè)函數(shù)通常在創(chuàng)建新的Python對(duì)象時(shí)調(diào)用,以便正確初始化引用計(jì)數(shù)。需要注意的是,這個(gè)函數(shù)是CPython內(nèi)部使用的,不應(yīng)該在普通Python代碼或擴(kuò)展模塊中直接使用。
#ifdef Py_REF_DEBUG
是一個(gè)C預(yù)處理器指令,它會(huì)檢查是否在編譯時(shí)定義了Py_REF_DEBUG
宏。如果定義了這個(gè)宏,那么在#ifdef
和#endif
之間的代碼塊將被編譯并包含在最終的程序中。否則,這部分代碼將被忽略。
Py_REF_DEBUG
宏用于啟用引用計(jì)數(shù)調(diào)試功能。這個(gè)功能允許CPython開發(fā)者和擴(kuò)展模塊開發(fā)者在開發(fā)過程中更輕松地追蹤和診斷潛在的引用計(jì)數(shù)錯(cuò)誤。這對(duì)于調(diào)試內(nèi)存泄漏或提前釋放對(duì)象等問題非常有用。
當(dāng)啟用Py_REF_DEBUG
時(shí),_Py_RefTotal
變量被用來跟蹤當(dāng)前分配給Python對(duì)象的總引用計(jì)數(shù)。這個(gè)全局計(jì)數(shù)器在每次創(chuàng)建新引用(如在_Py_NewReference
函數(shù)中)時(shí)遞增,在釋放引用時(shí)遞減。通過檢查_Py_RefTotal
的值,開發(fā)者可以在某些情況下發(fā)現(xiàn)可能的內(nèi)存泄漏或錯(cuò)誤的引用計(jì)數(shù)操作。
需要注意的是,Py_REF_DEBUG
功能會(huì)帶來一定的性能開銷,因此在生產(chǎn)環(huán)境中通常不啟用。在發(fā)布構(gòu)建中,默認(rèn)情況下不會(huì)定義Py_REF_DEBUG
宏。在開發(fā)和調(diào)試階段,可以通過配置構(gòu)建選項(xiàng)來啟用這個(gè)功能。
二: 引用計(jì)數(shù)增加
1. 相關(guān)源碼
// 引用計(jì)數(shù)增加 void Py_IncRef(PyObject *o) { Py_XINCREF(o); } // 宏定義 #define Py_XINCREF(op) _Py_XINCREF(_PyObject_CAST(op)) // 內(nèi)聯(lián)函數(shù) static inline void _Py_XINCREF(PyObject *op) { if (op != NULL) { Py_INCREF(op); } } // 宏定義 #define Py_INCREF(op) _Py_INCREF(_PyObject_CAST(op)) static inline void _Py_INCREF(PyObject *op) { #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif op->ob_refcnt++; // 對(duì)象的引用計(jì)數(shù)加1 }
2. 源碼解釋
_Py_INCREF
函數(shù)是一個(gè)靜態(tài)內(nèi)聯(lián)函數(shù),用于增加給定Python對(duì)象(op
)的引用計(jì)數(shù)。內(nèi)聯(lián)函數(shù)允許編譯器在調(diào)用處內(nèi)聯(lián)展開函數(shù)體,以減少函數(shù)調(diào)用的開銷。以下是_Py_INCREF
函數(shù)的各個(gè)部分的簡(jiǎn)要說明:
#ifdef Py_REF_DEBUG
:如果啟用了引用計(jì)數(shù)調(diào)試(即編譯時(shí)定義了Py_REF_DEBUG
),則增加全局引用計(jì)數(shù)_Py_RefTotal
。op->ob_refcnt++
:增加給定Python對(duì)象op
的引用計(jì)數(shù)(ob_refcnt
字段)。
Py_INCREF
是一個(gè)宏,用于調(diào)用_Py_INCREF
函數(shù)。在調(diào)用_Py_INCREF
之前,它首先使用_PyObject_CAST
宏將給定的對(duì)象(op
)轉(zhuǎn)換為PyObject
指針。這是為了確保_Py_INCREF
函數(shù)接收到的參數(shù)具有正確的類型。在編寫Python C擴(kuò)展時(shí),通常會(huì)使用Py_INCREF
宏來增加Python對(duì)象的引用計(jì)數(shù)。
三:引用計(jì)數(shù)減少
1. 相關(guān)源碼
void Py_DecRef(PyObject *o) { Py_XDECREF(o); } #define Py_XDECREF(op) _Py_XDECREF(_PyObject_CAST(op)) static inline void _Py_XDECREF(PyObject *op) { if (op != NULL) { Py_DECREF(op); } } define Py_DECREF(op) _Py_DECREF(_PyObject_CAST(op)) static inline void _Py_DECREF( #ifdef Py_REF_DEBUG const char *filename, int lineno, #endif PyObject *op) { #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif if (--op->ob_refcnt != 0) { #ifdef Py_REF_DEBUG if (op->ob_refcnt < 0) { _Py_NegativeRefcount(filename, lineno, op); } #endif } else { _Py_Dealloc(op); } } // 引用計(jì)數(shù)等于0了,就調(diào)用dealloc函數(shù)進(jìn)行對(duì)象刪除 void _Py_Dealloc(PyObject *op) { destructor dealloc = Py_TYPE(op)->tp_dealloc; #ifdef Py_TRACE_REFS _Py_ForgetReference(op); #endif (*dealloc)(op); }
2. 源碼解釋
在這段代碼中,我們可以看到Py_DecRef
、Py_XDECREF
、_Py_XDECREF
、Py_DECREF
和_Py_DECREF
這幾個(gè)用于處理Python對(duì)象引用計(jì)數(shù)的函數(shù)和宏。
Py_DecRef
:這是一個(gè)簡(jiǎn)單的封裝函數(shù),接受一個(gè)指向PyObject
的指針o
作為參數(shù),然后調(diào)用Py_XDECREF(o)
宏。Py_XDECREF
:這是一個(gè)宏,用于調(diào)用_Py_XDECREF
函數(shù)。在調(diào)用之前,它使用_PyObject_CAST(op)
宏將給定的對(duì)象(op
)轉(zhuǎn)換為PyObject
指針。_Py_XDECREF
:這是一個(gè)靜態(tài)內(nèi)聯(lián)函數(shù),用于在給定對(duì)象不為NULL時(shí)調(diào)用Py_DECREF
宏。這意味著如果對(duì)象指針為空(即op == NULL
),則不會(huì)對(duì)引用計(jì)數(shù)進(jìn)行任何操作。Py_DECREF
:這是一個(gè)宏,用于調(diào)用_Py_DECREF
函數(shù)。在調(diào)用之前,它使用_PyObject_CAST(op)
宏將給定的對(duì)象(op
)轉(zhuǎn)換為PyObject
指針。_Py_DECREF
:這是一個(gè)靜態(tài)內(nèi)聯(lián)函數(shù),用于減少給定Python對(duì)象(op
)的引用計(jì)數(shù)。以下是_Py_DECREF
函數(shù)的各個(gè)部分的簡(jiǎn)要說明:
a. #ifdef Py_REF_DEBUG
:如果啟用了引用計(jì)數(shù)調(diào)試(即編譯時(shí)定義了Py_REF_DEBUG
),則減少全局引用計(jì)數(shù)_Py_RefTotal
。
b. 減少對(duì)象的引用計(jì)數(shù):使用--op->ob_refcnt
來減少給定對(duì)象op
的引用計(jì)數(shù)。
c. 判斷引用計(jì)數(shù)是否為0:如果引用計(jì)數(shù)不為0,表示仍有其他對(duì)象引用該對(duì)象。如果引用計(jì)數(shù)為0,則調(diào)用_Py_Dealloc(op)
來釋放對(duì)象的內(nèi)存。在引用計(jì)數(shù)調(diào)試模式下,還會(huì)檢查引用計(jì)數(shù)是否為負(fù)數(shù),如果是,則調(diào)用_Py_NegativeRefcount(filename, lineno, op)
報(bào)告錯(cuò)誤。
在Python C擴(kuò)展中,通常使用Py_DECREF
和Py_XDECREF
宏來減少Python對(duì)象的引用計(jì)數(shù)。這些宏提供了安全和高效的方式來處理引用計(jì)數(shù),以防止內(nèi)存泄漏和提前釋放對(duì)象。
四:對(duì)象刪除
1. 相關(guān)源碼
void _Py_Dealloc(PyObject *op) { destructor dealloc = Py_TYPE(op)->tp_dealloc; #ifdef Py_TRACE_REFS _Py_ForgetReference(op); #endif (*dealloc)(op); }
2. 源碼解釋
_Py_Dealloc
函數(shù)是CPython中用于釋放Python對(duì)象內(nèi)存的函數(shù)。它在對(duì)象的引用計(jì)數(shù)變?yōu)榱銜r(shí)調(diào)用,表示沒有其他對(duì)象引用該對(duì)象,可以安全地回收其內(nèi)存。以下是_Py_Dealloc
函數(shù)的各個(gè)部分的簡(jiǎn)要說明:
destructor dealloc = Py_TYPE(op)->tp_dealloc;
:從給定Python對(duì)象op
的類型對(duì)象中獲取析構(gòu)函數(shù)(tp_dealloc
),并將其賦值給dealloc
。每個(gè)類型對(duì)象都有一個(gè)與之關(guān)聯(lián)的析構(gòu)函數(shù),該函數(shù)負(fù)責(zé)清理該類型的對(duì)象所占用的內(nèi)存。#ifdef Py_TRACE_REFS
:如果啟用了引用跟蹤功能(即編譯時(shí)定義了Py_TRACE_REFS
),則調(diào)用_Py_ForgetReference(op)
將對(duì)象op
從所有對(duì)象列表中刪除,以便不再跟蹤該對(duì)象。(*dealloc)(op);
:調(diào)用dealloc
指向的析構(gòu)函數(shù),釋放對(duì)象op
所占用的內(nèi)存。這里使用了函數(shù)指針,這意味著dealloc
可以指向任何類型的析構(gòu)函數(shù),從而可以靈活地處理不同類型的Python對(duì)象。
需要注意的是,_Py_Dealloc
函數(shù)是CPython內(nèi)部使用的,不應(yīng)該在普通Python代碼或擴(kuò)展模塊中直接使用。在Python擴(kuò)展模塊中,您應(yīng)該使用相應(yīng)的宏和API函數(shù)來管理引用計(jì)數(shù),例如Py_DECREF
和Py_XDECREF
。
Py_TYPE(op)
是一個(gè)宏,用于獲取給定Python對(duì)象(op
)的類型。它返回一個(gè)指向PyTypeObject
結(jié)構(gòu)的指針,這個(gè)結(jié)構(gòu)包含了對(duì)象類型的相關(guān)信息,如類型名、方法、屬性、析構(gòu)函數(shù)等。
在CPython內(nèi)部實(shí)現(xiàn)中,每個(gè)Python對(duì)象都包含一個(gè)指向其類型對(duì)象的指針。這個(gè)指針位于PyObject
結(jié)構(gòu)的ob_type
字段中。Py_TYPE(op)
宏實(shí)際上就是訪問這個(gè)ob_type
字段,即op->ob_type
。
在Python C擴(kuò)展和嵌入式代碼中,Py_TYPE(op)
宏可以用于檢查對(duì)象的類型、獲取類型特定的函數(shù)或執(zhí)行類型相關(guān)的操作。例如,可以通過比較兩個(gè)對(duì)象的類型對(duì)象來判斷它們是否屬于相同的類型。
到此這篇關(guān)于源碼解析python的內(nèi)存回收機(jī)制的文章就介紹到這了,更多相關(guān)python的內(nèi)存回收機(jī)制解析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python字符串本身作為bytes進(jìn)行解碼的問題
這篇文章主要介紹了解決Python字符串本身作為bytes進(jìn)行解碼的問題,文末給大家補(bǔ)充介紹了,Python字符串如何轉(zhuǎn)為bytes對(duì)象?Python字符串和bytes類型怎么互轉(zhuǎn),需要的朋友可以參考下2022-11-11python數(shù)據(jù)分析Numpy庫的常用操作
numpy 是 Python 的一個(gè)科學(xué)計(jì)算的庫,提供了矩陣運(yùn)算的功能,其一般與 Scipy、matplotlib 一起使用,這篇文章總結(jié)下python數(shù)據(jù)分析Numpy庫的常用操作,感興趣的朋友一起看看吧2022-01-01python?dataframe獲得指定行列實(shí)戰(zhàn)代碼
對(duì)于一個(gè)DataFrame,常常需要篩選出某列為指定值的行,下面這篇文章主要給大家介紹了關(guān)于python?dataframe獲得指定行列的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12python 利用百度API進(jìn)行淘寶評(píng)論關(guān)鍵詞提取
這篇文章主要介紹了python 利用百度API進(jìn)行淘寶評(píng)論關(guān)鍵詞提取,幫助大家更好的理解和學(xué)習(xí)使用python,感興趣的朋友可以了解下2021-03-03Python 異常的捕獲、異常的傳遞與主動(dòng)拋出異常操作示例
這篇文章主要介紹了Python 異常的捕獲、異常的傳遞與主動(dòng)拋出異常操作,結(jié)合實(shí)例形式詳細(xì)分析了Python針對(duì)異常捕獲、傳遞、處理等常見操作技巧,需要的朋友可以參考下2019-09-09