自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Python垃圾回收和GC模塊

譯文 精選
開(kāi)發(fā)
Python內(nèi)存管理是如何工作的,它的垃圾回收系統(tǒng)如何幫助優(yōu)化Python程序中的內(nèi)存,以及如何使用標(biāo)準(zhǔn)庫(kù)和第三方模塊來(lái)控制內(nèi)存使用和垃圾回收。

譯者 | 王德朕

策劃 | 云昭

內(nèi)存管理對(duì)于編程的重要性不言而喻。不管是技術(shù)面試,還是實(shí)際生產(chǎn)環(huán)境,始終都是開(kāi)發(fā)者繞不開(kāi)的一個(gè)門檻。

在Java領(lǐng)域,“JVM調(diào)優(yōu)”成為了一個(gè)熱議的話題。那么作為時(shí)不時(shí)占據(jù)編程排行榜的榜一大哥——Python,它是如何處理內(nèi)存管理的呢?

本文就帶大家詳細(xì)了解Python垃圾回收系統(tǒng)的來(lái)龍去脈,以及如何避免它的陷阱。

Python為編程者提供了許多簡(jiǎn)單上手的特性,其中最大的便利之一就是(幾乎)無(wú)障礙的內(nèi)存管理。在Python中,不需要手動(dòng)為Python中的對(duì)象和數(shù)據(jù)結(jié)構(gòu)分配、跟蹤和釋放內(nèi)存,Python運(yùn)行時(shí)為你完成了所有這些工作,你可以專注于解決實(shí)際問(wèn)題,而不是處理機(jī)器層面的細(xì)節(jié)。

盡管如此,對(duì)于經(jīng)驗(yàn)不足的Python程序員來(lái)說(shuō),了解Python的垃圾收集和內(nèi)存管理是如何工作的還是很有好處的。理解這些機(jī)制將有助于避免復(fù)雜項(xiàng)目出現(xiàn)性能問(wèn)題,還可以使用Python的內(nèi)置工具來(lái)監(jiān)視程序的內(nèi)存管理行為。

在這篇文章中,我們將看看Python內(nèi)存管理是如何工作的,它的垃圾回收系統(tǒng)如何幫助優(yōu)化Python程序中的內(nèi)存,以及如何使用標(biāo)準(zhǔn)庫(kù)和第三方模塊來(lái)控制內(nèi)存使用和垃圾回收。

1、Python如何管理內(nèi)存

每個(gè)Python對(duì)象都有一個(gè)引用計(jì)數(shù),也被稱為refcount。refcount是對(duì)某個(gè)對(duì)象引用其它對(duì)象總數(shù)的統(tǒng)計(jì)。當(dāng)你增加或刪除對(duì)一個(gè)對(duì)象的引用時(shí),這個(gè)數(shù)字會(huì)上升或下降,當(dāng)一個(gè)對(duì)象的refcount變?yōu)榱銜r(shí),該對(duì)象就會(huì)被刪除,其內(nèi)存被釋放。

什么是引用?允許通過(guò)名稱或通過(guò)另一個(gè)對(duì)象中的訪問(wèn)器訪問(wèn)對(duì)象的任何東西。

這里有一個(gè)簡(jiǎn)單的例子:

x = "Hello there"

運(yùn)行這段 Python代碼,會(huì)發(fā)生兩件事:

1.字符串 “Hello there” 作為一個(gè) Python 對(duì)象被創(chuàng)建并存儲(chǔ)在內(nèi)存中;

2.在本地命名空間中創(chuàng)建變量 x ,并指向該對(duì)象,此時(shí)該對(duì)象的引用計(jì)數(shù)加1。

如果接下來(lái)的代碼是“y=x”,那么引用計(jì)數(shù)將再次提高到2。

每當(dāng)x和y超出作用域或者從它們的名稱空間中刪除時(shí),字符串x和y的引用計(jì)數(shù)都會(huì)減少1。一旦x和y都超出作用域或被刪除,字符串的refcount就變?yōu)?并被刪除。

什么是作用域

作用域是名稱空間的通用術(shù)語(yǔ)。默認(rèn)情況下,在函數(shù)內(nèi)定義的變量的作用域只是該函數(shù),但在模塊級(jí)別定義的名稱的作用域是整個(gè)模塊。有關(guān)更多詳細(xì)信息,請(qǐng)參閱Python的文檔。

現(xiàn)在,假設(shè)我們創(chuàng)建一個(gè)包含字符串的列表,如下所示:

x = ["Hello there", 2, False]

字符串一直保留在內(nèi)存中,直到列表本身被刪除或者包含字符串的元素從列表中被刪除。這兩個(gè)操作都會(huì)導(dǎo)致持有該字符串引用的對(duì)象消失。

現(xiàn)在考慮一下這個(gè)例子:

x="Hello there"
y=[x]

如果我們從y中刪除第一個(gè)元素,或者完全刪除列表y,那么字符串仍然在內(nèi)存中,這是因?yàn)閤包含對(duì)它的引用。

2、Python循環(huán)引用

大多數(shù)情況下,引用計(jì)數(shù)都是正常工作的,但有時(shí)你會(huì)遇到這樣的情況:兩個(gè)對(duì)象各自持有對(duì)方的一個(gè)引用,這就是所謂的循環(huán)引用。在這種情況下,對(duì)象的引用計(jì)數(shù)將永遠(yuǎn)不會(huì)達(dá)到零,它們也永遠(yuǎn)不會(huì)從內(nèi)存中刪除。

這里有一個(gè)人為的例子:

x = SomeClass()
y = SomeOtherClass()
x.item = y
y.item = x

由于x和y保持對(duì)彼此的引用,即使沒(méi)有其它引用,它們也永遠(yuǎn)不會(huì)從系統(tǒng)中刪除。

實(shí)際上,對(duì)于Python來(lái)說(shuō),為對(duì)象生成循環(huán)引用是相當(dāng)常見(jiàn)的。一個(gè)例子是跟蹤對(duì)象的異常,該對(duì)象包含對(duì)異常本身的引用。

在Python的早期版本中,具有循環(huán)引用的對(duì)象可能會(huì)隨著時(shí)間積累,這對(duì)于長(zhǎng)時(shí)間運(yùn)行的應(yīng)用程序來(lái)說(shuō)是一個(gè)大問(wèn)題。但Python后來(lái)引入了循環(huán)檢測(cè)和垃圾回收系統(tǒng),用于管理循環(huán)引用。

3、Python垃圾回收器(GC)

Python的垃圾回收器檢測(cè)具有循環(huán)引用的對(duì)象。它通過(guò)跟蹤作為“容器”的對(duì)象--例如列表、字典、自定義類實(shí)例,并確定其中有哪些對(duì)象不被引用。

一旦這些對(duì)象被挑選出來(lái),垃圾回收器就會(huì)通過(guò)把它們的引用計(jì)數(shù)降低到0來(lái)刪除它們。(有關(guān)這種方法的詳細(xì)信息,請(qǐng)參閱Python開(kāi)發(fā)人員指南。)

絕大多數(shù)Python對(duì)象沒(méi)有循環(huán)引用,因此垃圾回收器不需要全天運(yùn)行。相反,垃圾回收器使用一些方法來(lái)減少運(yùn)行次數(shù),并盡可能高效地運(yùn)行。

當(dāng)Python解釋器啟動(dòng)時(shí),它會(huì)跟蹤已分配但未釋放的對(duì)象數(shù)量,絕大多數(shù)Python對(duì)象的生命周期非常短,因此它們很快就會(huì)出現(xiàn)或消失。但是隨著時(shí)間的推移,長(zhǎng)期存在的對(duì)象會(huì)逐漸積累,當(dāng)這種對(duì)象的數(shù)量超過(guò)一定數(shù)量時(shí),垃圾回收器就會(huì)運(yùn)行。(在Python 3.10中,默認(rèn)允許的長(zhǎng)生命周期對(duì)象數(shù)是700。)

每次垃圾回收器運(yùn)行時(shí),它都會(huì)將收集后的所有對(duì)象放在一起,并將它們放在一個(gè)稱為“分代”的組中,在循環(huán)引用內(nèi),這些“第1代”對(duì)象被掃描的頻率較低。任何在垃圾回收器中幸存下來(lái)的第1代對(duì)象最終都會(huì)遷移到第2代,在第2代中,它們很少被掃描。

同樣,并不是所有的對(duì)象都會(huì)被垃圾回收器追蹤到,例如像用戶創(chuàng)建類這樣的復(fù)雜對(duì)象總是被跟蹤的,但是一個(gè)只保存簡(jiǎn)單對(duì)象,例如整數(shù)和字符串的字典不會(huì)被跟蹤,因?yàn)樵谀莻€(gè)特定的字典中沒(méi)有對(duì)象持有對(duì)其的引用,不持有對(duì)其它元素的引用的簡(jiǎn)單對(duì)象,如整數(shù)和字符串,永遠(yuǎn)不會(huì)被跟蹤。

4、如何使用GC模塊

一般來(lái)說(shuō),垃圾回收器不需要調(diào)整就可以運(yùn)行良好,Python的開(kāi)發(fā)團(tuán)隊(duì)選擇了常見(jiàn)情況的默認(rèn)值,如果你確實(shí)需要調(diào)整垃圾回收的工作方式,你可以使用Python的GC?模塊,GC模塊為垃圾回收器的行為提供了編程接口,并可配置對(duì)哪些對(duì)象進(jìn)行跟蹤。

GC讓你做的一件有用的事情是,當(dāng)你確定不需要垃圾回收器的時(shí)候,可以關(guān)掉它。如果你有一個(gè)短期運(yùn)行的腳本,堆積了大量的對(duì)象,你就不需要垃圾回收器。所有的東西都會(huì)在腳本結(jié)束時(shí)被清除掉。為此,你可以用gc.disable()命令禁用垃圾回收器,之后可以用gc.enable()重新啟用它。

你還可以使用gc.collect() 手動(dòng)運(yùn)行垃圾回收,這方面的一個(gè)常見(jiàn)應(yīng)用是管理程序中生成許多臨時(shí)對(duì)象的部分,你可以在程序的這一部分禁用垃圾回收,然后在結(jié)束時(shí)手動(dòng)運(yùn)行回收并重新啟用回收。

另一個(gè)有用的垃圾回收優(yōu)化是gc.free(),當(dāng)運(yùn)行該代碼后,垃圾回收器跟蹤的所有內(nèi)容都被“凍結(jié)”,或者被列為免于回收掃描,這樣,未來(lái)的掃描可以跳過(guò)這些對(duì)象。如果你有一個(gè)導(dǎo)入庫(kù)并在啟動(dòng)前設(shè)置大量?jī)?nèi)部狀態(tài)的程序,那么可以在完成所有工作之后發(fā)出gc.free()。這樣可以防止垃圾回收器搜尋那些無(wú)論如何都不可能被移除的東西。(如果希望將凍結(jié)的對(duì)象再次執(zhí)行垃圾回收,請(qǐng)使用gc.unfree()。)

5、使用GC調(diào)試?yán)厥?/span>

還可以使用GC調(diào)試?yán)厥招袨?,如果?nèi)存中堆積的對(duì)象數(shù)量過(guò)多,而且沒(méi)有被垃圾回收,那么可以使用GC的檢查工具來(lái)確定哪些對(duì)象保存著對(duì)這些對(duì)象的引用。

如果想知道哪些對(duì)象保存著對(duì)給定對(duì)象的引用,可以使用gc.get_reference (obj)來(lái)列出它們,還可以使用gc.get_reference(obj)查找給定對(duì)象引用的任何。

如果不確定給定對(duì)象是否是垃圾回收的候選對(duì)象,gc.is_trace (obj)會(huì)告訴垃圾回收器是否跟蹤該對(duì)象,如前所述,請(qǐng)記住垃圾回收器不會(huì)跟蹤“原子”對(duì)象(如整數(shù))或僅包含原子對(duì)象的元素。

如果希望親自查看正在收集的對(duì)象,可以使用gc.set_debug(gc.DEBUG _ LEAK | gc.DEBUG_STATS)設(shè)置垃圾收集器的調(diào)試標(biāo)志。這將有關(guān)垃圾收集的信息寫入stderr,它將所有作為垃圾收集的對(duì)象保存在只讀列表gc.garbage中。

6、避免Python內(nèi)存管理中的陷阱

如前所述,對(duì)象可能堆積在內(nèi)存中,如果在某個(gè)地方仍然有對(duì)它們的引用,則不會(huì)被回收。這并不是Python的垃圾回收本身的問(wèn)題,而是因?yàn)槔厥掌鳠o(wú)法判斷你是否意外地保留了對(duì)某些內(nèi)容的引用。

讓我們以一些防止對(duì)象出現(xiàn)永不回收的提示來(lái)結(jié)束本文。

注意對(duì)象作用域

如果你把對(duì)象1指定為對(duì)象2的一個(gè)屬性(比如一個(gè)類),對(duì)象2需要在對(duì)象1之前退出作用范圍。

obj1 = MyClass()
obj2.prop = obj1


更重要的是,如果這是以其它操作的副作用形式方式發(fā)生的,例如把對(duì)象2作為參數(shù)傳遞給對(duì)象1的構(gòu)造函數(shù),你可能沒(méi)有意識(shí)到對(duì)象1有一個(gè)引用。

obj1 = MyClass(obj2)

另一個(gè)例子,如果你把一個(gè)對(duì)象推到一個(gè)模塊級(jí)的列表中,然后忘記了這個(gè)列表,這個(gè)對(duì)象將一直存在,直到從列表中刪除,或者直到列表本身不再有任何引用。但是如果這個(gè)列表是一個(gè)模塊級(jí)的對(duì)象,它很可能會(huì)一直存在,直到程序終止。

簡(jiǎn)而言之,要意識(shí)到對(duì)象可能被另一個(gè)不明顯的對(duì)象引用。

使用weakref避免循環(huán)引用

Python的weakref模塊讓你創(chuàng)建對(duì)其它對(duì)象的弱引用,弱引用不會(huì)增加一個(gè)對(duì)象的引用數(shù),所以一個(gè)只有弱引用的對(duì)象是垃圾回收的候選對(duì)象。

weakref的一個(gè)常見(jiàn)用途是對(duì)象的緩存,如果不希望被引用的對(duì)象僅僅因?yàn)橛幸粋€(gè)緩存條目而被保留下來(lái),可以對(duì)緩存條目使用弱引用。

手動(dòng)中斷循環(huán)引用

最后,如果你知道一個(gè)給定的對(duì)象持有對(duì)另一個(gè)對(duì)象的引用,你可以手動(dòng)中斷對(duì)該對(duì)象的引用,如果你有instance_of_class.ref = other_object,當(dāng)你準(zhǔn)備移除instance_of_class時(shí),你可以設(shè)置instance_of_class.ref = None。

原文鏈接:

??https://www.infoworld.com/article/3671673/python-garbage-collection-and-the-gc-module.html??

譯者介紹

王德朕,51CTO社區(qū)編輯,10年互聯(lián)網(wǎng)產(chǎn)研經(jīng)驗(yàn),6年IT教培行業(yè)經(jīng)驗(yàn)。

責(zé)任編輯:薛彥澤 來(lái)源: 51CTO
相關(guān)推薦

2022-01-20 10:34:49

JVM垃圾回收算法

2014-06-19 10:48:18

RubyPython

2022-03-21 11:33:11

JVM垃圾回收器垃圾回收算法

2023-12-07 12:21:04

GCJVM垃圾

2021-11-05 15:23:20

JVM回收算法

2022-01-25 09:15:39

V8垃圾回收算法

2021-09-26 09:23:01

GC算法垃圾

2021-01-04 10:08:07

垃圾回收Java虛擬機(jī)

2017-08-04 10:53:30

回收算法JVM垃圾回收器

2017-08-17 15:40:08

大數(shù)據(jù)Python垃圾回收機(jī)制

2012-12-18 13:57:42

.NetC#

2017-06-12 17:38:32

Python垃圾回收引用

2024-05-23 12:40:06

2021-05-27 21:47:12

Python垃圾回收

2020-03-13 08:00:00

.NET對(duì)象清理垃圾回收

2021-06-09 06:24:03

java垃圾回收機(jī)Java語(yǔ)言

2023-05-12 07:38:46

Python基準(zhǔn)測(cè)試性能分析

2023-12-19 21:52:51

Go垃圾回收開(kāi)發(fā)

2009-06-25 17:48:24

Java垃圾回收

2020-07-09 08:26:42

Kubernetes容器開(kāi)發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)