垃圾回收
當我們在定義變數的時候,變數會在記憶體中申請空間用來存盤記憶體的值,當變數不在被參考的時候 記憶體地址需要釋放,記憶體的釋放機制,可以理解為垃圾回識訓制,
如果記憶體的值一直不被釋放,會存在記憶體泄漏的風險
垃圾回識訓制(簡稱GC)是Python解釋器自帶一種機制,python的Cpython解釋器會幫我們實作垃圾回收,
基礎知識
在定義變數的時候,變數名 于變數的值是分開存盤的,分別對應記憶體中的兩塊區域:堆區與堆疊區
堆疊區:存放的是變數名與值記憶體地址的關聯關系 存的是對應關系
堆區:變數值存放于堆區,記憶體管理回收的則是堆區的內容
x=10 x與10的對應關系存在堆疊區1-1 10這個值 存在堆區1
y=20 y 與 20的對應關系存在與站區 2-2 20這個值存在與堆區2

當我們執行 x=y的時候
x堆疊區的對應關系變成了 2-2 其它的地方不變,


直接參考于間接的參考
直接參考:指的是從堆疊區出發直接參考到的記憶體地址, 也就是通過堆疊區的變數名可以直接找到堆區值的參考
間接參考: 指的是從堆疊區出發參考到堆區后,再通過進一步參考才能到達的記憶體地址, 無法通過堆疊區的名字一步實作找到堆區的值
直接參考舉例
x=10
y=x
y就是直接參考的x的值
間接參考舉例
list1 = ['24', 'b', 'luck']
list2=['ab','ac',list1]
串列list2對list1是直接參考 但list2對list2中的值是間接參考
list2要取出list1 中24這個值 需要list2的堆疊區需要先找到對應的list1的站區,然后在通過list1的堆疊區找到對應的list[0]的值
list1 = ['24', 'b', 'luck']
print(id(list1))
print(id(list1[0]))
list2 = ['ab', 'ac', list1]
print(id(list2[2]))
print(list2[2][0])
print(id(list2[2][0]))
2365435328832 list1的id
2365405395696 24值的id
2365435328832 list 2中list1的id
24
2365405395696 list2中list1中24的id
課件list2中存的list1 是存了list1的堆疊區的記憶體地址
Python的GC模塊 記憶體回識訓制主要運用下面三個計數
參考計數(reference counting)來跟蹤和回收垃圾,
在參考計數的基礎上,通過“標記-清除”(mark and sweep)解決容器物件可能產生的回圈參考的問題,
分代回收(generation collection)以空間換取時間的方式來進一步提高垃圾回收的效率
下面分別講一下三種計數的實作
1. 參考計數
參考計數其實就是指的 變數的值被關聯的次數,也就是有多少個直接或者間接指向改值的記憶體地址,計算個數 如果這個數為0 則回收改值的記憶體空間
舉例
a=10 10 這個值的記憶體空間 有一個計數了
x=a 在記憶體中x這個堆疊區指向的堆區也是 10的記憶體空間 計數+1 變成2 了
del a
del x 當兩個變數名都洗掉后 x a 的堆疊區沒有了 10的記憶體空間的參考計數變為0 10的記憶體空間被回收,
2. 標記-清除
標記清除是為了解決參考計數的漏洞
下面例子
x= ['xxx']
y=['yyy']
x.append(y) 串列y的參考計數變為2
y.append(x) 串列x的參考計數變為2
# x與y之間有相互參考
# x = ['xxx'的記憶體地址,串列2的記憶體地址]
# y = ['yyy'的記憶體地址,串列1的記憶體地址]
del x 串列x的參考計數變為1 來自y的參考
del y 串列的y參考計數變為1 串列x的參考
現在沒有變數名指向這兩個記憶體地址的值 但是這兩個記憶體地址因為彼此的相互參考,參考計數不為0 通過參考計數的方式是無法被清除的.
回圈參考會導致 記憶體變數無法被訪問到,同時通過標記清除的方式無法清除,造成大量無用的變數占用記憶體,
標記清除實作的演算法:當應用程式記憶體耗盡的時候,停止整個程式,進行兩項操作,一個是標記,一個操作是清除,
標記: 我們知道當一個變數存在記憶體中的時候,會產生堆疊區和堆區 堆疊區存的是記憶體地址和記憶體值的對應關系
而堆區存的是真正的變數的值,當我訪問堆區內容的時候,只能通過堆疊區直接或者間接的訪問到堆區的內容
標記的做法是 凡事所有從堆疊區出發,可以訪問到的堆區內容的值標記 不論是堆疊區-->堆區 還是 堆疊區---->**---->堆區,只要是堆疊區出發,最終可以訪問到的堆區的值都標記,堆疊區出發 無法訪問到的值則標記為需要清除的值,
因為我們無法繞過堆疊區 直接訪問到堆區的值,
清除:遍歷堆區中的所有物件值,將沒有標記的物件全部清除
這樣就解決了變數之間回圈參考造成的記憶體泄漏問題
問題: 我們知道標記-清除是要遍歷整個程式記憶體中所有變數的值,整個遍歷的程序會非常消耗資源的,同時所有的值都變數一次,效率上也不高,所以下面參考了分代回識訓制 “ 空間換時間” 犧牲一部分記憶體空間換來更高的執行效率、分代回識訓制
3.分代回收
存活時間:遍歷一邊記憶體的值 存活沒有被清除的記為存活時間,多次掃描 仍然存活的 我們稱為老年代(變數創建的時間早,但是多次掃描仍然在使用)
那有老年代 就是 青年代 青春代 新生代 以此創建時間越來越短,不同的”代“ 掃描的權重值不同,年輕的權重值高
把不同的變數分代以后,年輕的變數掃描次數多 年老的掃描次數少,例如 新生代 半小時一次,青春帶 2小時一次
到了 老年代 可以一天掃描一次,
通過分代回識訓制,減少 遍歷的次數和個數,提高 標記-清除的效率,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/142965.html
標籤:其他
上一篇:沒物件的快自己寫一個吧!帶你了解一下python物件!
下一篇:4-python基本資料型別
