Python引用計數(shù)與相關(guān)析構(gòu)函數(shù)的實際操作步驟
在C或是在C++中,相關(guān)人員在實際的操作中是可以自由的運用,但是在某些方面可以說是存在一些罪惡,就這一缺陷,我們可以用Python引用計數(shù)在其方面有一個解決的方案,以下是文章的具體介紹。
在權(quán)利的面,程序員必須負責(zé)將申請的內(nèi)存釋放,并釋放無效指針。可以說,這一點正是萬惡之源,大量內(nèi)存泄露和懸空指針的bug由此而生,如黃河泛濫一發(fā)不可收拾。
現(xiàn)代的開發(fā)語言中一般都選擇由語言本身負責(zé)內(nèi)存的管理和維護,即采用了垃圾收集機制,比如Java和C#。垃圾收集機制使開發(fā)人員從維護內(nèi)存分配和清理的繁重工作中解放出來,但同時也剝奪了程序員與內(nèi)存親密接觸的機會,并付出了一定的運行效率作為代價。#t#
現(xiàn)在看來,隨著垃圾收集機制的完善,對時間要求不是非常高的程序完全可以通過使用垃圾收集機制的語言來完成,這部分程序占了現(xiàn)存的大多數(shù)的程序。這樣做的好處是提高了開發(fā)效率,并降低了bug發(fā)生的幾率。Python同樣也內(nèi)建了垃圾收集機制,代替程序員進行繁重的內(nèi)存管理工作,而引用計數(shù)正是Python垃圾收集機制的一部分。
Python通過對一個對象的引用計數(shù)的管理來維護對象在內(nèi)存中的存在與否。我們知道在Python中每一個東西都是一個對象,都有一個ob_refcnt變量。這個變量維護著該對象的引用計數(shù),從而也最終決定著該對象的創(chuàng)建與消亡。
在Python中,主要是通過Py_INCREF(op)和Py_DECREF(op)兩個宏來增加和減少一個對象的Python引用計數(shù)。當(dāng)一個對象的引用計數(shù)減少到0之后,Py_DECREF將調(diào)用該對象的析構(gòu)函數(shù)來釋放該對象所占有的內(nèi)存和系統(tǒng)資源。注意這里的“析構(gòu)函數(shù)”借用了C++的詞匯,實際上這個析構(gòu)動作是通過在對象對應(yīng)的類型對象中定義的一個函數(shù)指針來指定的,就是那個tp_dealloc。
如果熟悉設(shè)計模式中的Observer模式,就可以看到,這里隱隱約約透著Observer模式的影子。在ob_refcnt減為0之后,將觸發(fā)對象銷毀的事件。從Python的對象體系來看,各個對象提供了不同的事件處理函數(shù),而事件的注冊動作正是在各個對象對應(yīng)的類型對象中靜態(tài)完成的。
PyObject中的ob_refcnt是一個32位的整形變量,這實際蘊含著Python所做的一個假設(shè),即對一個對象的引用不會超過一個整形變量的最大值。一般情況下,如果不是惡意代碼,這個假設(shè)顯然是成立的。
需要注意的是,在Python的各種對象中,類型對象是超越引用計數(shù)規(guī)則的。類型對象“跳出三界外,不再五行中”,永遠不會被析構(gòu)。每一個對象中指向類型對象的指針不被視為對類型對象的引用。在每一個對象創(chuàng)建的時候,Python提供了一個_Py_NewReference(op)宏來將對象的Python引用計數(shù)初始化為1。
在Python的源代碼中可以看到,在不同的編譯選項下(Py_REF_DEBUG, Py_TRACE_ REFS),引用計數(shù)的宏還要做許多額外的工作。下面展示的代碼是Python在最終發(fā)行時這些宏所對應(yīng)的實際的代碼:
- [object.h]
- #define _Py_NewReference(op) ((op)->ob_refcnt = 1)
- #define _Py_Dealloc(op) ((*(op)->ob_type->tp_dealloc)((PyObject *)(op)))
- #define Py_INCREF(op) ((op)->ob_refcnt++)
- #define Py_DECREF(op) \
- if (--(op)->ob_refcnt != 0) \
- ; \
- else \
- _Py_Dealloc((PyObject *)(op))
- /* Macros to use in case the object pointer may be NULL: */
- #define Py_XINCREF(op) if ((op) == NULL) ; else Py_INCREF(op)
- #define Py_XDECREF(op) if ((op) == NULL) ; else Py_DECREF(op)
在一個對象的Python引用計數(shù)減為0時,與該對象對應(yīng)的析構(gòu)函數(shù)就會被調(diào)用,但是要特別注意的是,調(diào)用析構(gòu)函數(shù)并不意味著最終一定會調(diào)用free釋放內(nèi)存空間,如果真是這樣的話,那頻繁地申請、釋放內(nèi)存空間會使Python的執(zhí)行效率大打折扣(更何況Python已經(jīng)多年背負了人們對其執(zhí)行效率的不滿)。
一般來說,Python中大量采用了內(nèi)存對象池的技術(shù),使用這種技術(shù)可以避免頻繁地申請和釋放內(nèi)存空間。因此在析構(gòu)時,通常都是將對象占用的空間歸還到內(nèi)存池中。這一點在接下來對Python內(nèi)建對象的實現(xiàn)中可以看得一清二楚。

















