亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

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

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

怎么理解Python中的垃圾回收

發布時間:2021-11-19 15:36:21 來源:億速云 閱讀:121 作者:iii 欄目:編程語言

這篇文章主要介紹“怎么理解Python中的垃圾回收”,在日常操作中,相信很多人在怎么理解Python中的垃圾回收問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么理解Python中的垃圾回收”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

前言

對于python來說,一切皆為對象,所有的變量賦值都遵循著對象引用機制。程序在運行的時候,需要在內存中開辟出一塊空間,用于存放運行時產生的臨時變量;計算完成后,再將結果輸出到永久性存儲器中。如果數據量過大,內存空間管理不善就很容易出現  OOM(out of memory),俗稱爆內存,程序可能被操作系統中止。而對于服務器,內存管理則顯得更為重要,不然很容易引發內存泄漏-  這里的泄漏,并不是說你的內存出現了信息安全問題,被惡意程序利用了,而是指程序本身沒有設計好,導致程序未能釋放已不再使用的內存。-  內存泄漏也不是指你的內存在物理上消失了,而是意味著代碼在分配了某段內存后,因為設計錯誤,失去了對這段內存的控制,從而造成了內存的浪費。也就是這塊內存脫離了gc的控制。

計數引用

因為python中一切皆為對象,你所看到的一切變量,本質上都是對象的一個指針。當一個對象不再調用的時候,也就是當這個對象的引用計數(指針數)為 0  的時候,說明這個對象永不可達,自然它也就成為了垃圾,需要被回收。可以簡單的理解為沒有任何變量再指向它。

import os  import psutil    # 顯示當前 python 程序占用的內存大小  def show_memory_info(hint):      pid = os.getpid()      p = psutil.Process(pid)      info = p.memory_full_info()      memory = info.uss / 1024./ 1024  print(  {} memory used: {} MB .format(hint, memory))

可以看到調用函數 func(),在列表 a 被創建之后,內存占用迅速增加到了 433  MB:而在函數調用結束后,內存則返回正常。這是因為,函數內部聲明的列表 a 是局部變量,在函數返回后,局部變量的引用會注銷掉;此時,列表 a  所指代對象的引用數為 0,Python 便會執行垃圾回收,因此之前占用的大量內存就又回來了。

def func():      show_memory_info(  initial  )  global a     a = [i for  i in  range( 10000000 )]      show_memory_info( after a created ) func()  show_memory_info(  finished  ) ########## 輸出 ##########  initial memory used: 48.88671875 MB  after a created memory used:433.94921875 MB  finished memory used:433.94921875 MB

新的這段代碼中,global a 表示將 a  聲明為全局變量。那么,即使函數返回后,列表的引用依然存在,于是對象就不會被垃圾回收掉,依然占用大量內存。同樣,如果我們把生成的列表返回,然后在主程序中接收,那么引用依然存在,垃圾回收就不會被觸發,大量內存仍然被占用著:

def func():      show_memory_info(  initial )      a = [i for  i in  derange( 10000000 )]      show_memory_info(  after a created )  return a  a = func() show_memory_info( finished)  ########## 輸出 ##########  initial memory used:  47.96484375 MB after a created memory used:434.515625 MB finished memory used: 434.515625 MB

那怎么可以看到變量被引用了多少次呢?通過 sys.getrefcount。

import sys  a = []  # 兩次引用,一次來自 a,一次來自 getrefcount print (sys.getrefcount(a))   def func(a):  # 四次引用,a,python 的函數調用棧,函數參數,和 getrefcount  print (sys.getrefcount(a))  func(a)  # 兩次引用,一次來自 a,一次來自 getrefcount,函數 func 調用已經不存在  print (sys.getrefcount(a))   ########## 輸出 ##########  2  4  2

如果其中涉及函數調用,會額外增加兩次1. 函數棧2.  函數調用。

從這里就可以看到python不再需要像C那種的認為的釋放內存,但是python同樣給我們提供了手動釋放內存的方法 gc.collect()。

import gc  show_memory_info( initial)  a = [i for  i in range( 10000000 )]  show_memory_info(  after a created) del a gc.collect() show_memory_info( finish )  print (a)  ########## 輸出 ########## initial memory used: 48.1015625 MB after a created memory used: 434.3828125 MB  finish memory used: 48.33203125 MB --------------------------------------------------------------------------- NameErrorTraceback (most recent call last)  <ipython-input- 12 - 153e15063d8a > in<module>  11  12 show_memory_info(  finish ) --->  13 print (a)  NameError : name  a  isnotdefined

截止目前,貌似python的垃圾回收機制非常的簡單,只要對象引用次數為0,必定為觸發gc,那么引用次數為0是否是觸發gc的充要條件呢?

循環回收

如果有兩個對象,它們互相引用,并且不再被別的對象所引用,那么它們應該被垃圾回收嗎?

def func():     show_memory_info( initial )      a = [i for  i in  range(10000000)]     b = [i for  i in  range(10000000)]      show_memory_info(  after a, b created )      a.append(b)      b.append(a) func() show_memory_info(  finished )  ########## 輸出 ##########  initial memory used: 47.984375 MB  after a, b created memory used:822.73828125 MB  finished memory used:  821.73046875 MB

從結果顯而易見,它們并沒有被回收,但是從程序上來看,當這個函數結束的時候,作為局部變量的a,b就已經從程序意義上不存在了。但是因為它們的互相引用,導致了它們的引用數都不為0。這時要如何規避呢1.  從代碼邏輯上進行整改,避免這種循環引用2. 通過人工回收。

import gc def func():      show_memory_info( initial)      a = [i for  i in  range(10000000)]      b = [i for  i in  range(10000000)]     show_memory_info( after a, b created)      a.append(b)     b.append(a) func() gc.collect() show_memory_info( finished )  ########## 輸出 ##########  initial memory used:49.51171875 MB  after a, b created memory used: 824.1328125 MB  finished memory used:49.98046875 MB

python針對循環引用,有它的自動垃圾回收算法1. 標記清除(mark-sweep)算法2. 分代收集(generational)。

標記清除

標記清除的步驟總結為如下步驟1. GC會把所有的『活動對象』打上標記2.  把那些沒有標記的對象『非活動對象』進行回收那么python如何判斷何為非活動對象?通過用圖論來理解不可達的概念。對于一個有向圖,如果從一個節點出發進行遍歷,并標記其經過的所有節點;那么,在遍歷結束后,所有沒有被標記的節點,我們就稱之為不可達節點。顯而易見,這些節點的存在是沒有任何意義的,自然的,我們就需要對它們進行垃圾回收。但是每次都遍歷全圖,對于  Python 而言是一種巨大的性能浪費。所以,在 Python 的垃圾回收實現中,mark-sweep  使用雙向鏈表維護了一個數據結構,并且只考慮容器類的對象(只有容器類對象,list、dict、tuple,instance,才有可能產生循環引用)。

怎么理解Python中的垃圾回收

圖中把小黑圈視為全局變量,也就是把它作為root  object,從小黑圈出發,對象1可直達,那么它將被標記,對象2、3可間接到達也會被標記,而4和5不可達,那么1、2、3就是活動對象,4和5是非活動對象會被GC回收。

分代回收

分代回收是一種以空間換時間的操作方式,Python將內存根據對象的存活時間劃分為不同的集合,每個集合稱為一個代,Python將內存分為了3“代”,分別為年輕代(第0代)、中年代(第1代)、老年代(第2代),他們對應的是3個鏈表,它們的垃圾收集頻率與對象的存活時間的增大而減小。新創建的對象都會分配在年輕代,年輕代鏈表的總數達到上限時(當垃圾回收器中新增對象減去刪除對象達到相應的閾值時),Python垃圾收集機制就會被觸發,把那些可以被回收的對象回收掉,而那些不會回收的對象就會被移到中年代去,依此類推,老年代中的對象是存活時間最久的對象,甚至是存活于整個系統的生命周期內。同時,分代回收是建立在標記清除技術基礎之上。事實上,分代回收基于的思想是,新生的對象更有可能被垃圾回收,而存活更久的對象也有更高的概率繼續存活。因此,通過這種做法,可以節約不少計算量,從而提高  Python 的性能。所以對于剛剛的問題,引用計數只是觸發gc的一個充分非必要條件,循環引用同樣也會觸發。

調試

可以使用 objgraph來調試程序,因為目前它的官方文檔,還沒有細讀,只能把文檔放在這供大家參閱啦~其中兩個函數非常有用 1. show_refs()  2. show_backrefs()。

到此,關于“怎么理解Python中的垃圾回收”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

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

AI

游戏| 芮城县| 白银市| 凭祥市| 贡嘎县| 商南县| 若尔盖县| 余庆县| 汕头市| 长葛市| 农安县| 桐城市| 曲沃县| 德安县| 酒泉市| 盐源县| 台中市| 苍山县| 弥勒县| 托克托县| 夏邑县| 旬阳县| 莫力| 吉木萨尔县| 广水市| 贵阳市| 安龙县| 铜梁县| 云霄县| 桦甸市| 陇西县| 什邡市| 海城市| 鄂托克前旗| 平安县| 德昌县| 炎陵县| 佛教| 布拖县| 高淳县| 伊金霍洛旗|