python垃圾回收機制

Python內存

None 和小整數池類似,python解釋器啓動就初始化好的python

小整數池

當python解釋器啓動,就會在內存初始化一塊保存-5到256的區域算法

並且這部分不會被垃圾回收,直到python解釋器關閉函數

交互式裏面運行:.net

a=1
b=1
print(a is b)

True指針

a=257
b=257
print(a is b)

Falsecode

垃圾回收

以引用計數爲主,標記-清除和分代回收爲輔htm

引用計數對象

當一個對象的引用計數爲0,python解釋器就會回收這個對象的內存blog

1.致使引用計數+1的狀況生命週期

對象被建立,例如zx=125

對象被引用,例如zy=zx

對象被做爲參數,傳入到一個函數中,例如func(zx)

對象做爲一個元素,存儲在容器中,例如lis=[zx,func]

2.致使引用計數-1的狀況

對象的別名被顯示銷燬,例如del zx

對象的別名被賦予了新的對象,例如zx="wl"

一個對象離開它的做用域,例如zx函數執行完畢,zx函數中的局部變量(全局變量不會)

對象所在的容器被銷燬,或從容器中刪除對象

sys.getrefcount()能夠查看某個對象的引用計數,可是因爲它自己就是一個函數,若是打印的話,引用計數會比正常大1

實驗

1.打印小整數池會發生的奇怪現象(小整數池)

import sys
print(sys.getrefcount(256))

19

import sys
print(sys.getrefcount(256))
print(sys.getrefcount(256))
print(sys.getrefcount(256))
print(sys.getrefcount(256))

22
22
22
22

總結:只有小整數池的會發生這種奇怪的打印(引用計數會增長),257就不會,以下

import sys
print(sys.getrefcount(257))

3

(看最後總結)

import sys
print(sys.getrefcount(257))
print(sys.getrefcount(257))
print(sys.getrefcount(257))
print(sys.getrefcount(257))

3
3
3
3

2.對象被引用

import sys
print(sys.getrefcount(257))
zx=257
print(sys.getrefcount(257))
c=zx
print(sys.getrefcount(257))

3
4
5

3.對象被做爲參數,傳入到一個函數中(看最後總結)

import sys
print(sys.getrefcount(257))
print(sys.getrefcount(257))
print(sys.getrefcount(257))

3
3
3

總結:其實257的真正引用計數爲2,打印爲3是由於函數調用的時候引用計數會+1,可是後面爲啥不繼續加上去,是由於,函數調用完畢引用計數就會-1

。。。。。。其餘相似

標記清除

標記清除算法(一種基於追蹤回收技術實現的垃圾回收算法)

它主要分爲兩個階段:第一個階段,GC會把全部活動的對象打上標記,第二階段,會把那些沒有標記的對象進行回收。

循環引用

a =[]#對象A的引用計數爲 1
b =[]#對象B的引用計數爲 1
a[1] = b  #B的引用計數增1
b[1] = a  #A的引用計數增1
del a #A的引用減 1,最後A對象的引用爲 1
del b #B的引用減 1, 最後B對象的引用爲 1

到最後,a,b的引用計數都爲1,沒法被垃圾回收(針對引用計數方法),這個時候標記清除出現了

相關概念:

1.變量名存的內存是棧

2.變量值存的內存是堆

存在於棧區的內存是GC Roots對象,首先會掃描棧區,與變量名有直接或者間接關係的堆對象標記爲活動對象,其他的都是非活動對象

對象之間經過引用(指針)連在一塊兒,構成一個有向圖,對象構成這個有向圖的節點,而引用關係構成這個有向圖的邊。從根對象(root object)出發,沿着有向邊遍歷對象,可達的(reachable)對象標記爲活動對象,不可達的對象就是要被清除的非活動對象。根對象就是全局變量、調用棧、寄存器。 mark-sweepg 在上圖中,咱們把小黑圈視爲全局變量,也就是把它做爲root object,從小黑圈出發,對象1可直達,那麼它將被標記,對象二、3可間接到達也會被標記,而4和5不可達,那麼一、二、3就是活動對象,4和5是非活動對象會被GC回收。

標記清除算法做爲Python的輔助垃圾收集技術主要處理的是一些容器對象,好比list、dict、tuple,instance等,由於對於字符串、數值對象是不可能形成循環引用問題。Python使用一個雙向鏈表將這些容器對象組織起來。不過,這種簡單粗暴的標記清除算法也有明顯的缺點:清除非活動的對象前它必須順序掃描整個堆內存,哪怕只剩下小部分活動對象也要掃描全部對象。

分代回收

-> 分配內存
-> 發現超過閾值了
-> 觸發垃圾回收
-> 將全部可收集對象鏈表放到一塊兒
-> 遍歷, 計算有效引用計數
-> 分紅 有效引用計數=0 和 有效引用計數 > 0 兩個集合
-> 大於0的, 放入到更老一代
-> =0的, 執行回收
-> 回收遍歷容器內的各個元素, 減掉對應元素引用計數(破掉循環引用)
-> 執行-1的邏輯, 若發現對象引用計數=0, 觸發內存回收
-> python底層內存管理機制回收內存

1.分代回收是一種以空間換時間的操做方式,這種思想簡單點說就是:對象存在時間越長,越可能不是垃圾,應該越少去收集。

2.Python將內存根據對象的存活時間劃分爲不一樣的集合,每一個集合稱爲一個代,Python將內存分爲了3「代」,分別爲年輕代(第0代)、中年代(第1代)、老年代(第2代),他們對應的是3個鏈表,它們的垃圾收集頻率與對象的存活時間的增大而減少。

3.新建立的對象都會分配在年輕代,年輕代鏈表的總數達到上限時,Python垃圾收集機制就會被觸發,把那些能夠被回收的對象回收掉,而那些不會回收的對象就會被移到中年代去,依此類推,老年代中的對象是存活時間最久的對象,甚至是存活於整個系統的生命週期內。

4.(700,10,10)每一個代的threshold值表示該代最多容納對象的個數。默認狀況下,當0代超過700,或1,2代超過10,垃圾回收機制將觸發。

5.同時,分代回收是創建在標記清除技術基礎之上。分代回收一樣做爲Python的輔助垃圾收集技術處理那些容器對象.

參考博客:

https://www.jb51.net/article/79306.htm

https://testerhome.com/topics/16556

https://blog.51cto.com/13764714/2373842?source=dra

https://blog.csdn.net/xiongchengluo1129/article/details/80462651

相關文章
相關標籤/搜索