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