當咱們在定義變量的時候,變量會在內存中申請空間用來存儲內存的值,當變量不在被引用的時候 內存地址須要釋放,內存的釋放機制,能夠理解爲垃圾回收機制。python
若是內存的值一直不被釋放,會存在內存泄漏的風險算法
垃圾回收機制(簡稱GC)是Python解釋器自帶一種機制,python的Cpython解釋器會幫咱們實現垃圾回收,app
在定義變量的時候,變量名 於變量的值是分開存儲的,分別對應內存中的兩塊區域:堆區與棧區code
棧區:存放的是變量名與值內存地址的關聯關係 存的是對應關係對象
堆區:變量值存放於堆區,內存管理回收的則是堆區的內容blog
x=10 x與10的對應關係存在棧區1-1 10這個值 存在堆區1內存
y=20 y 與 20的對應關係存在與站區 2-2 20這個值存在與堆區2資源
當咱們執行 x=y的時候內存管理
x棧區的對應關係變成了 2-2 其它的地方不變,io
直接引用:指的是從棧區出發直接引用到的內存地址。 也就是經過棧區的變量名能夠直接找到堆區值的引用
間接引用: 指的是從棧區出發引用到堆區後,再經過進一步引用才能到達的內存地址。 沒法經過棧區的名字一步實現找到堆區的值
直接引用舉例
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)以空間換取時間的方式來進一步提升垃圾回收的效率
下面分別講一下三種計數的實現
引用計數其實就是指的 變量的值被關聯的次數,也就是有多少個直接或者間接指向改值的內存地址,計算個數 若是這個數爲0 則回收改值的內存空間
舉例
a=10 10 這個值的內存空間 有一個計數了
x=a 在內存中x這個棧區指向的堆區也是 10的內存空間 計數+1 變成2 了
del a
del x 當兩個變量名都刪除後 x a 的棧區沒有了 10的內存空間的引用計數變爲0 10的內存空間被回收。
標記清除是爲了解決引用計數的漏洞
下面例子
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 經過引用計數的方式是沒法被清除的.
循環引用會致使 內存變量沒法被訪問到,同時經過標記清除的方式沒法清除,形成大量無用的變量佔用內存。
標記清除實現的算法:當應用程序內存耗盡的時候,中止整個程序,進行兩項操做,一個是標記,一個操做是清除。
標記: 咱們知道當一個變量存在內存中的時候,會產生棧區和堆區 棧區存的是內存地址和內存值的對應關係
而堆區存的是真正的變量的值,當我訪問堆區內容的時候,只能經過棧區直接或者間接的訪問到堆區的內容
標記的作法是 凡事全部從棧區出發,能夠訪問到的堆區內容的值標記 不管是棧區-->堆區 仍是 棧區---->**---->堆區,只要是棧區出發,最終能夠訪問到的堆區的值都標記,棧區出發 沒法訪問到的值則標記爲須要清除的值,
由於咱們沒法繞過棧區 直接訪問到堆區的值。
清除:遍歷堆區中的全部對象值,將沒有標記的對象所有清除
這樣就解決了變量之間循環引用形成的內存泄漏問題
問題: 咱們知道標記-清除是要遍歷整個程序內存中全部變量的值,整個遍歷的過程會很是消耗資源的,同時全部的值都變量一次,效率上也不高,因此下面引用了分代回收機制 「 空間換時間」 犧牲一部份內存空間換來更高的執行效率、分代回收機制
存活時間:遍歷一邊內存的值 存活沒有被清除的記爲存活時間,屢次掃描 仍然存活的 咱們稱爲老年代(變量建立的時間早,可是屢次掃描仍然在使用)
那有老年代 就是 青年代 青春代 新生代 以此建立時間愈來愈短。不一樣的」代「 掃描的權重值不一樣,年輕的權重值高
把不一樣的變量分代之後,年輕的變量掃描次數多 年老的掃描次數少,例如 新生代 半小時一次,青春帶 2小時一次
到了 老年代 能夠一天掃描一次。
經過分代回收機制,減小 遍歷的次數和個數,提升 標記-清除的效率。