姆咪 這個好麻煩 不過比ml文章好寫 作為一個高階語言,python擁有自動GC的機制 在本文中,我們將討論python最常用的編譯器:Cpython的GC 引用記數機制是Cpython GC的重要環節 首先 我們將聚焦於引用計數機制 引用計數機制 typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; PyTypeObject *ob_type; } PyObject; Cpython定義的物件長成上面那樣子 ob_refcnt就是引用計數 引用計數的概念並不複雜 當我們創建一個物件,該物件的引用計數就加一 反之,刪除一個物件,引用計數減一 import sys a = 1 print(sys.getrefcount(1)) del a print(sys.getrefcount(1)) 這時候output會是一串很長的數字(1000000052) 直觀上,我們會預期out會是1跟0 最多基於某些隱式引用,output可能會多一點 但不該是一串很長的數字 之所以會跑出一串很長的數字 這是因為某些小整數經常被使用,為了避免不斷為這些小整數分配與釋出記憶體 程式開始執行時,Cpython預先為這些整數分配記憶體並創建物件 這些小整數已經被多次引用,所以引用計數才會特別高 小整數的範圍是-5到256(這是Cpython設定的範圍,隨著編譯器不同而改變。) import sys a = 999 print(sys.getrefcount(999)) del a print(sys.getrefcount(999)) 改成999 ,這時候output就4與3,之所以不是1與0,這是因為隱式引用 諸如sys.getrefcount()本身就有一次臨時引用 Cpyhton為了最佳化,編譯時會把變數存入co_consts,這時引用數又加一。 假如引用記數歸零,Cpython會銷毀該物件,並釋放記憶體。 ... #ifndef PyFloat_MAXFREELIST # define PyFloat_MAXFREELIST 100 #endif ... PyTypeObject PyFloat_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "float", sizeof(PyFloatObject), 0, (destructor)float_dealloc, /* tp_dealloc */ ... }; ... static void float_dealloc(PyFloatObject *op) { #if PyFloat_MAXFREELIST > 0 if (PyFloat_CheckExact(op)) { struct _Py_float_state *state = get_float_state(); #ifdef Py_DEBUG // float_dealloc() must not be called after _PyFloat_Fini() assert(state->numfree != -1); #endif if (state->numfree >= PyFloat_MAXFREELIST) { PyObject_Free(op); return; } state->numfree++; Py_SET_TYPE(op, (PyTypeObject *)state->free_list); state->free_list = op; } else #endif { Py_TYPE(op)->tp_free((PyObject *)op); } } 前面主要聚焦於整數的引用計數與回收,現在讓我們研究浮點數的回收機制 當浮點數的引用計數歸零,這時會有兩種可能 假若浮點數的freelist <100,浮點數放入freelist 假設freelist >= 100,則銷毀浮點數並釋放空間 Cpython設置free list的用意也是為了避免頻繁地記憶體分配與釋放 引用計數的問題 import gc a = [] a.append(a) del a print(gc.collect()) 這邊發生循環引用,首先創建a,引用計數+=1 在 a.append(a) 之後,a 這個列表對象內部有一個元素是自己,這樣 a 的引用計數變 成 2。 後面del a,引用計數-=1,我們預期a會被引用記數回收,但此時a的引用計數是1,因為a 包含對自己的引用,所以a不會被回收。 gc.collect()是手動觸發其他回收機制,因為引用計數無法清理a,其他機制能解決循環 引用的問題,所以a被其他機制清除。gc.collect()回傳1,代表一個物件被清除。 所以,單單依靠引用計數會出現上述的麻煩,這時候就有了其他配套機制, 參考文章: https://hackmd.io/@8thEdition/cpython_garbage_collection_reference_counting -- ※ 發信站: 批踢踢實業坊(ptt-site.org.tw), 來自: 42.73.85.3 (臺灣) ※ 文章網址: https://ptt-site.org.tw/Marginalman/M.1731400907.A.611