日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

人生苦短,只談風月,談什么垃圾回收。

Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

據說上圖是某語言的垃圾回收機制。。。

我們寫過C語言、C++的朋友都知道,我們的C語言是沒有垃圾回收這種說法的。手動分配、釋放內存都需要我們的程序員自己完成。不管是“內存泄漏” 還是野指針都是讓開發者非常頭疼的問題。所以C語言開發這個討論得最多的話題就是內存管理了。但是對于其他高級語言來說,例如JAVA、C#、Python/ target=_blank class=infotextkey>Python等高級語言,已經具備了垃圾回收機制。這樣可以屏蔽內存管理的復雜性,使開發者可以更好地關注核心的業務邏輯。

對我們的Python開發者來說,我們可以當甩手掌柜。不用操心它怎么回收程序運行過程中產生的垃圾。但是這畢竟是一門語言的內心功法,難道我們甘愿一輩子做一個API調參俠嗎?

1. 什么是垃圾?

當我們的Python解釋器在執行到定義變量的語法時,會申請內存空間來存放變量的值,而內存的容量是有限的,這就涉及到變量值所占用內存空間的回收問題。

當一個對象或者說變量沒有用了,就會被當做“垃圾“。那什么樣的變量是沒有用的呢?

a = 10000

當解釋器執行到上面這里的時候,會劃分一塊內存來存儲 10000 這個值。此時的 10000 是被變量 a 引用的

a = 30000

當我們修改這個變量的值時,又劃分了一塊內存來存 30000 這個值,此時變量a引用的值是30000。

這個時候,我們的 10000 已經沒有變量引用它了,我們也可以說它變成了垃圾,但是他依舊占著剛才給他的內存。那我們的解釋器,就要把這塊內存地盤收回來。

Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

2. 內存泄露和內存溢出

上面我們了解了什么是程序運行過程中的“垃圾”,那如果,產生了垃圾,我們不去處理,會產生什么樣的后果呢?試想一下,如果你家從不丟垃圾,產生的垃圾就堆在家里會怎么樣呢?

  1. 家里堆滿垃圾,有個美女想當你的對象,但是已經沒有空間給她住了。
  2. 你還能住,但是家里的垃圾很占地方,而且很浪費空間,慢慢的,總有一天你的家里會堆滿垃圾

上面的結果其實就是計算機里面讓所有程序員都聞風喪膽的問題,內存溢出和內存泄露,輕則導致程序運行速度減慢,重則導致程序崩潰。

內存溢出:程序在申請內存時,沒有足夠的內存空間供其使用,出現 out of memory

內存泄露:程序在申請內存后,無法釋放已申請的內存空間,一次內存泄露危害可以忽略,但內存泄露堆積后果很嚴重,無論多少內存,遲早會被占光

 

3. 引用計數

前面我們提到過垃圾的產生是因為,對象沒有再被其他變量引用了。那么,我們的解釋器究竟是怎么知道一個對象還有沒有被引用的呢?

答案就是:引用計數。python內部通過引用計數機制來統計一個對象被引用的次數。當這個數變成0的時候,就說明這個對象沒有被引用了。這個時候它就變成了“垃圾”。

這個引用計數又是何方神圣呢?讓我們看看代碼

text = "hello,world"

上面的一行代碼做了哪些工作呢?

  • 創建字符串對象:它的值是hello,world,
  • 開辟內存空間:在對象進行實例化的時候,解釋器會為對象分配一段內存地址空間。把這個對象的結構體存儲在這段內存地址空間中。

我們再來看看這個對象的結構體

typedef struct_object {
        int ob_refcnt;
        struct_typeobject *ob_type;
} PyObject;

熟悉c語言或者c++的朋友,看到這個應該特別熟悉,他就是結構體。這是因為我們Python官方的解釋器是CPython,它底層調用了很多的c類庫與接口。所以一些底層的數據是通過結構體進行存儲的。看不懂的朋友也沒有關系。

這里,我們只需要關注一個參數:ob_refcnt

這個參數非常神奇,它記錄了這個對象的被變量引用的次數。所以上面 hello,world 這個對象的引用計數就是 1,因為現在只有text這個變量引用了它。

①變量初始化賦值:

text = "hello,world"

 

Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

②變量引用傳遞:

new_text = text
Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

③刪除第一個變量:

del text
Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

④刪除第二個變量:

del new_text
Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

此時 "hello,world" 對象的引用計數為:0,被當成了垃圾。下一步,就該被我們的垃圾回收器給收走了。

Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

4. 引用計數如何變化

上面我們了解了什么是引用計數。那這個參數什么時候會發生變化呢?

Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

4.1 引用計數加一的情況

  • 對象被創建
a = "hello,world"
  • 對象被別的變量引用(賦值給一個變量)
b = a
  • 對象被作為元素,放在容器中(比如被當作元素放在列表中)
list = [ ] 
list.Append(a)
  • 對象作為參數傳遞給函數
func(a)

 

4.2 引用計數減一

  • 對象的引用變量被顯示銷毀
del a
  • 對象的引用變量賦值引用其他對象
a = "hello, Python"    # a的原來的引用對象:a = "hello,world"
  • 對象從容器中被移除,或者容器被銷毀(例:對象從列表中被移除,或者列表被銷毀)
del list
list.remove(a)
  • 一個引用離開了它的作用域
func(): 
    a = "hello,world" 
    return 
func() # 函數執行結束以后,函數作用域里面的局部變量a會被釋放

 

4.3 查看對象的引用計數

如果要查看對象的引用計數,可以通過內置模塊 sys 提供的 getrefcount 方法去查看。

import sys
a = "hello,world"
print(sys.getrefcount(a))

注意:當使用某個引用作為參數,傳遞給 getrefcount() 時,參數實際上創建了一個臨時的引用。因此,getrefcount() 所得到的結果,會比期望的多 1

 

5. 垃圾回收機制

其實Python的垃圾回收機制,我們前面已經說得差不多了。

Python通過引用計數的方法來說實現垃圾回收,當一個對象的引用計數為0的時候,就進行垃圾回收。但是如果只使用引用計數也是有點問題的。所以,python又引進了 標記-清除 和 分代收集 兩種機制。

Python采用的是引用計數機制為主,標記-清除和分代收集兩種機制為輔的策略。

前面的引用計數我們已經了解了,那這個標記-清除跟分代收集又是什么呢?

Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

5.1 引用計數機制缺點

Python語言默認采用的垃圾收集機制是“引用計數法 ”,該算法最早George E. Collins在1960的時候首次提出,50年后的今天,該算法依然被很多編程語言使用。

引用計數法:每個對象維護一個 ob_refcnt 字段,用來記錄該對象當前被引用的次數,每當新的引用指向該對象時,它的引用計數 ob_refcnt 加1,每當該對象的引用失效時計數 ob_refcnt 減1,一旦對象的引用計數為0,該對象立即被回收,對象占用的內存空間將被釋放。

缺點:

  1. 需要額外的空間維護引用計數
  2. 無法解決循環引用問題

什么是循環引用問題?看看下面的例子

a = {"key":"a"}  # 字典對象a的引用計數:1
b = {"key":"b"}  # 字典對象b的引用計數:1

a["b"] = b  # 字典對象b的引用計數:2
b["a"] = a  # 字典對象a的引用計數:2

del a  # 字典對象a的引用計數:1
del b  # 字典對象b的引用計數:1

看上面的例子,明明兩個變量都刪除了,但是這兩個對象卻沒有得到釋放。原因是他們的引用計數都沒有減少到0。而我們垃圾回收機制只有當引用計數為0的時候才會釋放對象。這是一個無法解決的致命問題。這兩個對象始終不會被銷毀,這樣就會導致內存泄漏。

那怎么解決這個問題呢?這個時候 標記-清除 就排上了用場。標記清除可以處理這種循環引用的情況。

5.2 標記-清除策略

Python采用了標記-清除策略,解決容器對象可能產生的循環引用問題。

該策略在進行垃圾回收時分成了兩步,分別是:

  • 標記階段,遍歷所有的對象,如果是可達的(reachable),也就是還有對象引用它,那么就標記該對象為可達;
  • 清除階段,再次遍歷對象,如果發現某個對象沒有標記為可達,則就將其回收

這里簡單介紹一下標記-清除策略的流程

Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

可達(活動)對象:從root集合節點有(通過鏈式引用)路徑達到的對象節點

不可達(非活動)對象:從root集合節點沒有(通過鏈式引用)路徑到達的對象節點

流程:

  1. 首先,從root集合節點出發,沿著有向邊遍歷所有的對象節點
  2. 對每個對象分別標記可達對象還是不可達對象
  3. 再次遍歷所有節點,對所有標記為不可達的對象進行垃圾回收、銷毀。

標記-清除是一種周期性策略,相當于是一個定時任務,每隔一段時間進行一次掃描。

并且標記-清除工作時會暫停整個應用程序,等待標記清除結束后才會恢復應用程序的運行。

5.3 分代回收策略

分代回收建立標記清除的基礎之上,因為我們的標記-清除策略會將我們的程序阻塞。

簡單來說就是:對象存在時間越長,越可能不是垃圾,應該越少去收集

那什么時候會觸發分代回收呢?

import gc

print(gc.get_threshold())
# (700, 10, 10)
# 上面這個是默認的回收策略的閾值

# 也可以自己設置回收策略的閾值
gc.set_threshold(500, 5, 5)
  • 700:表示當分配對象的個數達到700時,進行一次0代回收
  • 10:當進行10次0代回收以后觸發一次1代回收
  • 10:當進行10次1代回收以后觸發一次2代回收
Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

5.4 gc模塊

  • gc.get_count():獲取當前自動執行垃圾回收的計數器,返回一個長度為3的列表
  • gc.get_threshold():獲取gc模塊中自動執行垃圾回收的頻率,默認是(700, 10, 10)
  • gc.set_threshold(threshold0[,threshold1,threshold2]):設置自動執行垃圾回收的頻率
  • gc.disable():python3默認開啟gc機制,可以使用該方法手動關閉gc機制
  • gc.collect():手動調用垃圾回收機制回收垃圾

其實,既然我們選擇了python,性能就不是最重要的了。我相信大部分的python工程師甚至都還沒遇到過性能問題,因為現在的機器性能可以彌補。而對于內存管理與垃圾回收,python提供了甩手掌柜的方式讓我們更關注業務層,這不是更加符合人生苦短,我用python的理念么。如果我還需要像C++那樣小心翼翼的進行內存的管理,那我為什么還要用python呢?咱不就是圖他的便利嘛。所以,放心去干吧!

Python垃圾回收機制,人生苦短,只談風月,談什么回收?

 

原文鏈接:
https://blog.51cto.com/u_14666251/4674779

分享到:
標簽:回收 垃圾 Python
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定