溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python垃圾回收機制的原理

發(fā)布時間:2021-08-17 20:39:09 來源:億速云 閱讀:125 作者:chen 欄目:編程語言

本篇內(nèi)容介紹了“Python垃圾回收機制的原理”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

  引用計數(shù)器為主

  標記清除和分代回收為輔

  + 緩存機制

  1. 引用計數(shù)器

  1.1 環(huán)狀雙向鏈表 refchain

  在python程序中創(chuàng)建的任何對象都會放在refchain鏈表中。

  #define PyObject_HEAD PyObject ob_base;

  #define PyObject_VAR_HEAD PyVarObject ob_base;

  // 宏定義,包含 上一個、下一個,用于構(gòu)造雙向鏈表用。(放到refchain鏈表中時要用到)

  #define _PyObject_HEAD_EXTRA \

  struct _object *_ob_next; \

  struct _object *_ob_prev;

  name = "阿瑋"

  age = 18

  hobby = ["健身", "美女"]

  內(nèi)部會創(chuàng)建一些數(shù)據(jù) [ 上一個對象、下一個對象、類型、引用個數(shù) ]

  name = "阿瑋"

  new = name # 引用個數(shù)變成2

  內(nèi)部會創(chuàng)建一些數(shù)據(jù) [ 上一個對象、下一個對象、類型、引用個數(shù)、val=18 ]

  age = 18

  內(nèi)部會創(chuàng)建一些數(shù)據(jù) [ 上一個對象、下一個對象、類型、引用個數(shù)、items=元素、元素個數(shù) ]

  hobby = ["健身", "美女"]

  #define PyObject_HEAD PyObject ob_base;

  #define PyObject_VAR_HEAD PyVarObject ob_base;

  // 宏定義,包含 上一個、下一個,用于構(gòu)造雙向鏈表用。(放到refchain鏈表中時要用到)

  #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; // 引用計數(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,即:元素個數(shù)

  } PyVarObject;

  在C源碼中如何體現(xiàn)每個對象中都有的相同的值:PyObject結(jié)構(gòu)體(4個值)。

  有多個元素組成的對象:PyObject結(jié)構(gòu)體(4個值)+ ob_size = PyVarObject。

  1.2 類型封裝結(jié)構(gòu)體

  float類型

  typedef struct {

  PyObject_HEAD;

  double ob_fval;

  };

  data = 3.14;

  內(nèi)部會創(chuàng)建:

  _ob_next = refchain中的下一個對象

  _ob_prev = refchain中的上一個對象

  ob_refcnt = 1

  ob_type = float

  ob_fval = 3.14

  int類型

  struct _longobect {

  PyObject_VAR_HEAD;

  digit ob_dit[1];

  };

  /* Long (arbitrary precision) integer object interface */

  typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */

  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;

  PyDictKeyObject *ma_keys;

  PyObject **ma_values;

  } PyDictObject;

  1.3 引用計數(shù)器

  v1 = 3.14

  v2 = 999

  v3 = (1,2,3)

  當python程序運行時,會根據(jù)數(shù)據(jù)類型的不同找到其結(jié)構(gòu)體,根據(jù)結(jié)構(gòu)體中的字段來進行創(chuàng)建相關的數(shù)據(jù),然后將對象添加到refchain雙向鏈表中。

  在C源碼中有兩個關鍵的結(jié)構(gòu)體:PyObject、PyVarObject。

  每個對象中有 ob_refcnt 就是引用計數(shù)器,值默認為1,當有其他變量引用這個對象時,引用計數(shù)器就會發(fā)生變化。

  引用

  a = 99999

  b = a

  # 此時 99999 這個對象引用計數(shù)器的值為2

  '''

  下面情況會導致引用計數(shù)器+1:

  1.對象被創(chuàng)建,如 a = 2

  2.對象被引用,如 b = a

  3.對象被作為參數(shù),傳入到一個函數(shù)中

  4.對象作為一個元素,存儲在容器中

  可以通過sys包中的getrefcount()來獲取一個名稱所引用的對象當前的引用計數(shù)器的值(注意這里getrefcount()本身會使得引用計數(shù)器+1)

  '''

  刪除引用

  a = 99999

  b = a

  # b變量刪除,b對應對象的引用計數(shù)器-1

  def b

  # a變量刪除,a對應對象的引用計數(shù)器-1

  '''

  下面情況會導致引用計數(shù)器-1:

  1.變量被顯示銷毀 del

  2.變量被賦予新的對象

  3.一個對象離開它的作用域

  4.對象所在的容器被銷毀或從容器中刪除對象

  '''

  # 當一個對象的引用計數(shù)器為0時,意味著沒有人再使用這個對象了,這個對象就是垃圾,垃圾回收。

  # 回收:1.對象從rechain鏈表移出。2.將對象銷毀,內(nèi)存歸還。

  1.4 循環(huán)引用問題

  由于 v1 指向的對象引用了 v2,v2 指向的對象也引用了 v1,當將 v1、v2 兩個變量刪除時,雖然引用計數(shù)器會減1,但是兩個對象間還存在循環(huán)引用,而此時已經(jīng)沒有變量能去指向它們,這兩個對象就會在內(nèi)存中常駐無法處理。

  2. 標記清除

  目的:為了解決引用計數(shù)器循環(huán)引用的問題。

  實現(xiàn):在python的底層再維護一個鏈表,鏈表中專門放哪些可能存在循環(huán)應用的對象(容器類對象:list、tuple、dict、set)。

  在Python內(nèi)部某種情況下觸發(fā),會去掃描可能存在循環(huán)引用的鏈表中的每個元素,檢查是否有循環(huán)引用,如果有則讓雙方的引用計數(shù)器-1;如果是0則垃圾回收。

  2.1 標記階段

  遍歷所有對象,如果是可達的(reachable),也就是還有對象引用它,那么就將該對象標記為可達

  該階段從某個對象開始掃描(而不是從變量),如果變量A引用了變量B,則將變量B的引用計數(shù)器-1(指的是gc_ref),然后掃描變量B鄭州人流手術醫(yī)院 http://wap.zyfuke.com/

  如圖所示,link1、link2、link3形成了一個引用環(huán),link4自引用。從link1開始掃描,link1引用了link2,則link2的gc_ref-1,接著掃描link2…

  像這也將鏈表中所有對象考察一遍后,兩個鏈表中的對象ref_count和gc_ref圖如所示,這一步操作就相當于解除了循環(huán)引用對引用計數(shù)器的影響

  如果gc_ref為0,則將對象標記為 GC_TENTATIVELY_UNREACHABLE,并且被移至”Unreachable“鏈表中,如下圖link3、link4(我覺得link2應該也是)

  如果gc_ref不為0,那么這個對象會被標記為可達的GC_REACHABLE,同時當gc發(fā)現(xiàn)有一個節(jié)點是可達的,那么它會遞歸式的從該節(jié)點觸發(fā)將所有可達的節(jié)點標記為GC_REACHABLE,這樣把link2、link3救回來

  2.2 清除階段

  將被標記成 GC_UNREACHABLE 的對象銷毀,內(nèi)存歸還(也就是Unreachable鏈表中的對象)

  2.3 標記清除的問題

  在標記清除算法開始后,會暫停整個應用程序,等待標記清除結(jié)束后才會恢復應用的運行,且對循環(huán)引用的掃描代價大,每次掃描耗時可能很久

  3. 分代回收

  將可能存在循環(huán)引用的對象維護成3個鏈表:

  0代:0代中對象個數(shù)達到700個掃描一次

  1代:0代掃描10次,則1代掃描一次

  2代:1代掃描10次,則2代掃描一次

  4. 小結(jié)

  在python中維護了一個refchain的雙向環(huán)狀鏈表,這個鏈表中存儲程序創(chuàng)建的所有對象,每種類型的對象都有一個ob_refcnt引用計數(shù)器的值,當引用計數(shù)器變?yōu)?時會進行垃圾回收(對象銷毀、refchain中移出)。

  但是,在python中對于那些可以有多個元素組成的對象可能會存在循環(huán)引用的問題,為了解決這個問題,python又引入了標記清除和分代回收,在其內(nèi)部維護了4個鏈表,分別為:

  refchain

  2代

  1代

  0代

  在源碼內(nèi)部,當達到各自的閾值時,就會觸發(fā)掃描鏈表進行標記清除的動作(有循環(huán)引用則各自-1)。

  But,源碼內(nèi)部在上述流程中提出了優(yōu)化機制。

“Python垃圾回收機制的原理”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI