溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Python深度探索(1):內(nèi)存管理機(jī)制

發(fā)布時(shí)間:2020-08-07 11:39:55 來(lái)源:網(wǎng)絡(luò) 閱讀:1143 作者:androidguy 欄目:編程語(yǔ)言

任何編程語(yǔ)言都會(huì)有一個(gè)內(nèi)存模型,以便管理為變量分配的內(nèi)存空間。不同的編程語(yǔ)言,如C、C++、Java、C#,Python,它們的內(nèi)存模型都是不相同的,本文將以現(xiàn)在最流行的Python語(yǔ)言為例,來(lái)說(shuō)明動(dòng)態(tài)類型語(yǔ)言的內(nèi)存管理方式。

1. 重復(fù)使用內(nèi)存空間

賦值語(yǔ)句是Python語(yǔ)言中最簡(jiǎn)單的語(yǔ)句之一,雖然賦值語(yǔ)言很簡(jiǎn)單,但卻內(nèi)含玄機(jī)。

例如,將一個(gè)值賦給一個(gè)變量是最常見的賦值操作。

n = 1 # 將1賦給變量n

整數(shù)1是一個(gè)值,而n是一個(gè)對(duì)象。這是最簡(jiǎn)單不過(guò)的賦值語(yǔ)句了。那么在內(nèi)存中是如何操作的呢?其實(shí)在Python中,任何值都可以看做是一個(gè)對(duì)象,例如,1是int類的實(shí)例,True是bool類的實(shí)例。所以將1賦給變量n,其實(shí)是n指向了int類型的對(duì)象,所以n本質(zhì)上就是一個(gè)對(duì)象的引用。

Python作為動(dòng)態(tài)語(yǔ)言,采用了引用與對(duì)象分離的策略,這也使得任何引用都可以指向任何對(duì)象,而且可以動(dòng)態(tài)改變引用指向的對(duì)象類型,也就是說(shuō),可以將一個(gè)指向int類型的對(duì)象的引用重新指向bool類型的對(duì)象。所以可以將Python語(yǔ)言的對(duì)象模型看做是超市里的儲(chǔ)物柜(這里只是用儲(chǔ)物柜作為內(nèi)存模型的比喻,不要與超市儲(chǔ)物柜實(shí)際的操作進(jìn)行比較)。

Python深度探索(1):內(nèi)存管理機(jī)制
每一個(gè)小柜子相當(dāng)于一塊內(nèi)存區(qū)域,這塊內(nèi)存區(qū)域保存了不同類型的值。對(duì)于像C++、Java一樣的靜態(tài)語(yǔ)言,一旦分配了某一個(gè)小柜子,就意味著這個(gè)柜子只能保存特定的物品,如只能放鞋子、只能放手套、只能放衣服。而對(duì)于打開小柜子的鑰匙(相當(dāng)于變量),同時(shí)也只能打開某一個(gè)特定的小柜子,相當(dāng)于一個(gè)變量同時(shí)只能指向一個(gè)對(duì)象一樣。當(dāng)然,在鑰匙上進(jìn)行設(shè)置后,該鑰匙可以指向其他同類型的小柜子(相當(dāng)于改變變量指向的對(duì)象,如將一個(gè)指向int類型對(duì)象的變量指向了另外一個(gè)int類型的對(duì)象)。

不過(guò)Python語(yǔ)言就不一樣了。在Python版的儲(chǔ)物柜中,每一個(gè)小柜子并不限定存儲(chǔ)物品的類型,而一把鑰匙經(jīng)過(guò)設(shè)置后,可以打開任意一個(gè)小柜子(相當(dāng)于任意改變變量指向的對(duì)象)。這樣做的好處是更靈活,沒(méi)必要為存儲(chǔ)特定的物品,增加新的儲(chǔ)物柜,只要還有空的小柜子,就可以放任何物品。但缺點(diǎn)也很明顯,就是打開一個(gè)小柜子后,需要多進(jìn)行一步判斷的操作,判斷這個(gè)小柜子到底是存儲(chǔ)的什么物品。

當(dāng)然,對(duì)于同一個(gè)特定的小柜子,可能會(huì)配有多把鑰匙,這些鑰匙都可以打開這個(gè)特定的小柜子,這就相當(dāng)于多個(gè)變量指向同一個(gè)對(duì)象。例如,

x = 10y = 10z = 10

x、y和z三個(gè)變量的值都是10,這個(gè)10就相當(dāng)于要保存在小柜子中的物品。x、y和z相當(dāng)于3把鑰匙。而3個(gè)變量中的值都是10,所以被認(rèn)為是同一個(gè)值(物品),因此,就只需要?jiǎng)佑靡粋€(gè)小柜子保存10,而3個(gè)變量都會(huì)指向這個(gè)小柜子(由于計(jì)算機(jī)中值具有無(wú)限可復(fù)制性,所以只要有一個(gè)物品,就可以無(wú)限復(fù)制,所以不必考慮現(xiàn)實(shí)中將小柜子中的東西拿走了就為空的情況)。所以其實(shí)x、y和z這3個(gè)變量指向了同一個(gè)內(nèi)存地址(相當(dāng)于小柜子的序號(hào))。可以用id函數(shù)驗(yàn)證這3個(gè)變量的內(nèi)存地址是否相同,代碼如下:

print(id(x))print(id(y))print(id(z))

輸出結(jié)果如下:

4470531424

4470531424

4470531424

也可以用下面的代碼將內(nèi)存地址轉(zhuǎn)換為十六進(jìn)制形式。

print(hex(id(x)))print(hex(id(y)))print(hex(id(z)))

輸出結(jié)果如下:

0x10a76e560

0x10a76e560

0x10a76e560

根據(jù)前面的輸出結(jié)果,很顯然,x、y和z指向的是同一個(gè)內(nèi)存地址。讀者可以將10換成其他的對(duì)象,如True、10.12、"hello world",結(jié)果都是一樣(由于機(jī)器不同,輸出的內(nèi)存地址可能不同,但3個(gè)變量的內(nèi)存地址肯定都是相同的)。

也可以用is運(yùn)算符判斷這3個(gè)變量是否指向同一個(gè)值。

print(x is y is z) # 輸出結(jié)果:True

但要注意,只有不可變類型,如int、float、bool、string等,才會(huì)使用同一個(gè)儲(chǔ)物柜。如果是可變類型,如列表、對(duì)象,每次都會(huì)分配新的內(nèi)存空間。這里的不可變是指值一旦確定,值本身無(wú)法修改。例如int類型的10,這個(gè)10是固定的,不能修改,如果修改成11,那么就是新的值了,需要申請(qǐng)新的小柜子。而列表,如空列表[],以后還可以向空列表中添加任何類型的值,也可以修改和刪除列表中的值。所以沒(méi)有辦法為所有的空列表分配同一個(gè)小柜子,因?yàn)橛械目樟斜?,現(xiàn)在是空,以后不一定是空。所以每一個(gè)列表類型的值都會(huì)新分配一個(gè)小柜子,但元組就不同了,由于元組是只讀的,所以一開始是空的元組,那么這個(gè)元組今生今世將永遠(yuǎn)是空,所以可以為所有的空元組,以及所有相同元素個(gè)數(shù)和值的元組分配同一個(gè)小柜子。看下面代碼:

class MyClass:passa = []
b = []
c = MyClass()
d = MyClass()
t1 = (1,2,3)
t2 = (1,2,3)print(a is b) # False 元素個(gè)數(shù)和類型相同的列表不會(huì)使用同一個(gè)內(nèi)存空間(小柜子)print(c is d) # False MyClass類的不同實(shí)例不會(huì)使用同一個(gè)內(nèi)存空間(小柜子)print(t1 is t2) # True 元素個(gè)數(shù)和類型相同的元組會(huì)使用同一個(gè)內(nèi)存空間(小柜子)

這種將相同,但不可變的值保存在同一個(gè)內(nèi)存空間的方式也稱為值的緩存,這樣做非常節(jié)省內(nèi)存空間,而且程序的執(zhí)行效率更高。因?yàn)槭∪チ舜罅糠峙鋬?nèi)存空間的時(shí)間。

2. 引用計(jì)數(shù)器

在Python語(yǔ)言中是無(wú)法自己釋放變量?jī)?nèi)存的,所以Python虛擬機(jī)提供了自動(dòng)回收內(nèi)存的機(jī)制,那么Python虛擬機(jī)是如何知道哪一個(gè)變量占用的內(nèi)存可以被回收呢?通常的做法是為每一塊被占用的內(nèi)存設(shè)置一個(gè)引用計(jì)數(shù)器,如果該內(nèi)存塊沒(méi)有被任何變量引用(也就是引用計(jì)數(shù)器為0),那么該內(nèi)存塊就可以被釋放,否則無(wú)法被釋放。

在sys模塊中有一個(gè)getrefcount函數(shù),可以用來(lái)獲取任何變量指向的內(nèi)存塊的引用計(jì)數(shù)器當(dāng)前的值。用法如下:

from sys import getrefcount

a = [1, 2, 3]print(getrefcount(a)) # 輸出2 b = aprint(getrefcount(b)) # 輸出3print(getrefcount(a)) # 輸出3 x = 1print(getrefcount(x)) #輸出1640y = 1print(getrefcount(x)) # 輸出1641print(getrefcount(y)) # 輸出1641

要注意,使用getrefcount函數(shù)獲得引用計(jì)數(shù)器的值時(shí),實(shí)際上會(huì)創(chuàng)建一個(gè)臨時(shí)的引用,所以getrefcount函數(shù)返回的值會(huì)比實(shí)際的值多1。而對(duì)于具體的值(如本例的1),系統(tǒng)可能在很多地方都引用了該值,所以根據(jù)Python版本和當(dāng)前運(yùn)行的應(yīng)用不同,getrefcount函數(shù)返回的值是不確定的。

3. 對(duì)象引用

像C++這樣的編程語(yǔ)言,對(duì)象的傳遞分為值傳遞和指針傳遞。如果是值傳遞,就會(huì)將對(duì)象中的所有成員屬性的值都一起復(fù)制,而指針傳遞,只是復(fù)制了對(duì)象的內(nèi)存首地址。不過(guò)在Python中,并沒(méi)有指針的概念。只有一個(gè)對(duì)象引用。也就是說(shuō),Python語(yǔ)言中對(duì)象的復(fù)制與C++中的對(duì)象指針復(fù)制是一樣的。只是將對(duì)象引用計(jì)數(shù)器加1而已。具體看下面的代碼:

from sys import getrefcount 
# 類的構(gòu)造方法傳入另外一個(gè)對(duì)象的引用class MyClass(object):def __init__(self, other_obj):
self.other_obj = other_obj # 這里的other_obj與后面的data指向了同一塊內(nèi)存地址 data = {'name':'Bill','Age':30}print(getrefcount(data)) # 輸出2my = MyClass(data)print(id(my.other_obj)) # 輸出4364264288print(id(data)) #輸出4364264288
 print(getrefcount(data)) # 輸出3

在Python中,一切都是對(duì)象,包括值。如1、2、3、"abcd"等。所以Python會(huì)在使用這些值時(shí),先將其保存在一塊固定的內(nèi)存區(qū)域,然后將所有賦給這些值的變量指向這塊內(nèi)存區(qū)域,同時(shí)引用計(jì)數(shù)器加1。

例如,

a = 1

b = 1

其中a和b指向了同一塊內(nèi)存空間,這兩個(gè)變量其實(shí)都保存了對(duì)1的引用。使用id函數(shù)查看這兩個(gè)變量的引用地址是相同的。

4. 循環(huán)引用與拓?fù)鋱D

如果對(duì)象引用非常多,就可能會(huì)構(gòu)成非常復(fù)雜的拓?fù)浣Y(jié)果。例如,下面代碼的引用拓?fù)潢P(guān)系就非常復(fù)雜。估計(jì)大多數(shù)同學(xué)都無(wú)法一下子看出這段程序中各個(gè)對(duì)象的拓?fù)潢P(guān)系。

class MyClass1:def __init__(self, obj):
self.obj = obj 
class MyClass2:def __init__(self,obj1,obj2):
self.obj1 = obj1
self.obj2 = obj2

data1 = ['hello', 'world']
data2 = [data1, MyClass1(data1),3,dict(data = data1)]
data3 = [data1,data2,MyClass2(data1,data2),MyClass1(MyClass2(data1,data2))]

看不出來(lái)也不要緊,可以使用objgraph模塊繪制出某個(gè)變量與其他變量的拓?fù)潢P(guān)系,objgraph是第三方模塊,需要使用pip install objgraph命令安裝,如果機(jī)器上安裝了多個(gè)Python環(huán)境,要注意看看pip命令是否屬于當(dāng)前正在使用的Python環(huán)境,不要將objgraph安裝在其他的Python環(huán)境中。

安裝完objgraph后,可以使用下面命令看看data3與其他對(duì)象的引用關(guān)系。

import objgraph
objgraph.show_refs([data3], filename='對(duì)象引用關(guān)系.png')

show_refs函數(shù)會(huì)在當(dāng)前目錄下生成一個(gè)”對(duì)象引用關(guān)系.png“的圖像文件,如下圖所示。

Python深度探索(1):內(nèi)存管理機(jī)制
如果對(duì)象之間互相引用,有可能會(huì)形成循環(huán)引用。也就是a引用b,b引用a,見下面的代碼。

import objgraphfrom sys import getrefcount
a = {}
b = {'data':a}
a['value'] = b
objgraph.show_refs([b], filename='循環(huán)引用1.png')

在這段代碼中。a和b都是一個(gè)字典,b中的一個(gè)value引用了a,而a的一個(gè)value引用了b,所以產(chǎn)生了一個(gè)循環(huán)引用。這段代碼的引用拓?fù)鋱D如下:
Python深度探索(1):內(nèi)存管理機(jī)制

很明顯,這兩個(gè)字典是循環(huán)引用的。

不光是多個(gè)對(duì)象之間的引用可以產(chǎn)生循環(huán)引用,只有一個(gè)對(duì)象也可以產(chǎn)生循環(huán)引用,代碼如下:

a = {}
a['value'] = a
a = []
a.append(a)print(getrefcount(a))
objgraph.show_refs([a], filename='循環(huán)引用2.png')

在這段代碼中,字典a的一個(gè)值是自身,拓?fù)鋱D如下:
Python深度探索(1):內(nèi)存管理機(jī)制

5. 減少引用計(jì)數(shù)的兩種方法

前面一直說(shuō)讓引用計(jì)數(shù)器增加的方法,那么如何讓引用計(jì)數(shù)器減少呢?通常有如下兩種方法:

(1)用del刪除某一個(gè)引用

(2)將變量指向另外一個(gè)引用,或設(shè)置為None,也就是引用重定向。

用del刪除某一個(gè)引用

del語(yǔ)句可以刪除一個(gè)變量對(duì)某一個(gè)塊內(nèi)存空間的引用,也可以刪除集合對(duì)象中的某個(gè)item,代碼如下:

from sys import getrefcount

person = {'name':'Bill','age':40}
person1 = personprint(getrefcount(person1)) # 輸出3
 del person # 刪除person對(duì)字典的引用print(getrefcount(person1)) # 由于引用少了一個(gè),所以輸出為2# print(person) # 拋出異常 # 被刪除的變量相當(dāng)于重來(lái)沒(méi)定義過(guò),所以這條語(yǔ)句會(huì)拋出異常
 del person1['age'] # 刪除字典中key為age的值對(duì)print(person1)

引用重定向

from sys import getrefcount

value1 = [1,2,3,4]
value2 = value1
value3 = value2print(getrefcount(value2)) # 輸出4value1 = 20print(getrefcount(value2)) # 輸出3,因?yàn)関alue1重新指向了20value3 = Noneprint(getrefcount(value2)) # 輸出2,因?yàn)関alue3被設(shè)置為None,也就是不指向任何內(nèi)存空間,相當(dāng)于空指針

6. 垃圾回收

像Java、JavaScript、Python這樣的編程語(yǔ)言,都不允許直接通過(guò)代碼釋放變量占用的內(nèi)存,虛擬機(jī)會(huì)自動(dòng)釋放這些內(nèi)存區(qū)域。所以很多程序員就會(huì)認(rèn)為在這些語(yǔ)言中可以放心大膽地申請(qǐng)各種類型的變量,并不用擔(dān)心變量的釋放問(wèn)題,因?yàn)橄到y(tǒng)會(huì)自動(dòng)替我們完成這些煩人的工作。

沒(méi)錯(cuò),這些語(yǔ)言的虛擬機(jī)會(huì)自動(dòng)釋放一些不需要的內(nèi)存塊,用專業(yè)術(shù)語(yǔ)描述就是:垃圾回收。 相當(dāng)于為系統(tǒng)減肥或減負(fù)。因?yàn)椴还苣愕挠?jì)算機(jī)有多少內(nèi)存,只要不斷創(chuàng)建新的變量,哪怕該變量只占用了1個(gè)字節(jié)的內(nèi)存空間,內(nèi)存也有用完的一天。所以虛擬機(jī)會(huì)在適當(dāng)?shù)臅r(shí)候釋放掉不需要的內(nèi)存塊。

在前面已經(jīng)提到過(guò),虛擬機(jī)會(huì)回收引用計(jì)數(shù)為0的內(nèi)存塊,因?yàn)檫@些內(nèi)存塊沒(méi)有任何變量指向他們,所以留著沒(méi)有任何意義。那么到底虛擬機(jī)在什么時(shí)候才會(huì)回收這些內(nèi)存塊呢?通常來(lái)講,虛擬機(jī)會(huì)設(shè)置一個(gè)內(nèi)存閾值,一旦超過(guò)了這個(gè)閾值,就會(huì)自動(dòng)啟動(dòng)垃圾回收器來(lái)回收不需要的內(nèi)存空間。對(duì)于不同編程語(yǔ)言的這個(gè)閾值是不同的。對(duì)于Python來(lái)說(shuō),會(huì)記錄其中分配對(duì)象(object allocation)和取消分配對(duì)象(object deallocation)的次數(shù)。當(dāng)兩者的差值高于某個(gè)閾值時(shí),垃圾回收才會(huì)啟動(dòng)。

我們可以通過(guò)gc模塊的get_threshold()方法,查看該閾值:

import gcprint(gc.get_threshold())

輸出的結(jié)果為:

(700, 10, 10)

這個(gè)700就是這個(gè)閾值。后面的兩個(gè)10是與分代回收相關(guān)的閾值,后面會(huì)詳細(xì)介紹??梢允褂胓c模塊中的set_threshold方法設(shè)置這個(gè)閾值。

由于垃圾回收是一項(xiàng)昂貴的工作,所以如果計(jì)算機(jī)的內(nèi)存足夠大,可以將這個(gè)閾值設(shè)置的大一點(diǎn),這樣可以避免垃圾回收器頻繁調(diào)用。

當(dāng)然,如果覺得必要,也可以使用下面的代碼手工啟動(dòng)垃圾回收器。不過(guò)要注意,手工啟動(dòng)垃圾回收器后,垃圾回收器也不一定會(huì)立刻啟動(dòng),通常會(huì)在系統(tǒng)空閑時(shí)啟動(dòng)垃圾回收器。

gc.collect()

7. 變量不用了要設(shè)置為None

有大量?jī)?nèi)存被占用,是一定要被釋放的。但釋放這些內(nèi)存有一個(gè)前提條件,就是這個(gè)內(nèi)存塊不能有任何變量引用,也就是引用計(jì)數(shù)器為0。如果有多個(gè)變量指向同一個(gè)內(nèi)存塊,而且有一些變量已經(jīng)不再使用了,一個(gè)好的習(xí)慣是將變量設(shè)置為None,或用del刪除該變量。

person = {'Name':'Bill'}
value = [1,2,3]del person
value = None

當(dāng)刪除person變量,以及將value設(shè)置為None后,就不會(huì)再有任何變量指向字典和列表了,所以字典和列表占用的內(nèi)存空間會(huì)被釋放。

8. 解決循環(huán)引用的回收問(wèn)題

在前面講了Python GC(垃圾回收器)的一種算法策略,就是引用計(jì)數(shù)法,這種方法是Python GC采用的主要方法。不過(guò)這種策略也有其缺點(diǎn)。下面就看一下引用計(jì)數(shù)法的優(yōu)缺點(diǎn)。

優(yōu)點(diǎn):簡(jiǎn)單,實(shí)時(shí)(一旦為0就會(huì)立刻釋放內(nèi)存空間,毫不猶豫)

缺點(diǎn): 維護(hù)性高(簡(jiǎn)單實(shí)時(shí),但是額外占用了一部分資源,雖然邏輯簡(jiǎn)單,但是麻煩。好比你吃草莓,吃一次洗一下手,而不是吃完洗手。),不能解決循環(huán)引用的問(wèn)題。

那么Python到底是如何解決循環(huán)引用釋放的問(wèn)題呢?先看下面的代碼。

import objgraphfrom sys import getrefcount
a = {}
b = {'data':a}
a['value'] = bdel adel b

在這段代碼中,很明顯,a和b互相引用。最后通過(guò)del語(yǔ)句刪除a和b。由于a和b是循環(huán)引用,如果按前面引用計(jì)數(shù)器的方法,在刪除a和b之前,兩個(gè)字典分別由兩個(gè)引用(引用計(jì)數(shù)器為2),一個(gè)是自身引用,另一個(gè)是a或b中的value引用的自己。如果只是刪除了a和b,似乎這兩個(gè)字典各自還剩一個(gè)引用。但其實(shí)這兩個(gè)字典的內(nèi)存空間已經(jīng)釋放。那么Python是如何做到的呢?

其實(shí)Python GC在檢測(cè)所有引用時(shí),會(huì)檢測(cè)哪些引用之間是循環(huán)引用,如果檢測(cè)到某些變量之間循環(huán)引用,例如,a引用b,b引用a,就會(huì)在檢測(cè)a時(shí),將b的引用計(jì)數(shù)器減1,在檢測(cè)b時(shí),會(huì)將a的引用計(jì)數(shù)器減1。也就是說(shuō),Python GC當(dāng)發(fā)現(xiàn)某些引用是循環(huán)引用后,會(huì)將這些引用的計(jì)數(shù)器多減一個(gè)1。所以這些循環(huán)引用指向的空間仍然會(huì)被釋放。

9. 分代回收

如果是多年的朋友,或一起做了多年的生意,有多年的業(yè)務(wù)往來(lái),往往會(huì)產(chǎn)生一定的信任。通常來(lái)講,合作的時(shí)間越長(zhǎng),產(chǎn)生的信任感就會(huì)越深。Python GC采用的垃圾回收策略中,也會(huì)使用這種信任感作為輔助算法,讓GC運(yùn)行得更有效率。這種策略就是分代(generation)回收。

分代回收的策略有一個(gè)基本假設(shè),就是存活的越久,越可能被經(jīng)常使用,所以出于信任和效率,對(duì)這些“長(zhǎng)壽”對(duì)象給予特殊照顧,在GC對(duì)所有對(duì)象進(jìn)行檢測(cè)時(shí),就會(huì)盡可能少地檢測(cè)這些“長(zhǎng)壽”對(duì)象。就是現(xiàn)在有很多企業(yè)是免檢企業(yè)一樣,政府出于對(duì)這些企業(yè)的信任,給這些企業(yè)生產(chǎn)出的產(chǎn)品予以免檢的特殊優(yōu)待。

那么Python對(duì)什么樣的對(duì)象會(huì)給予哪些特殊照顧呢?Python將對(duì)象共分為3代,分別用0、1、2表示。任何新創(chuàng)建的對(duì)象是0代,不會(huì)給予任何特殊照顧,當(dāng)某一個(gè)0代對(duì)象經(jīng)過(guò)若干次垃圾回收后仍然存活,那么就會(huì)將這個(gè)對(duì)象歸入1代對(duì)象,如果這個(gè)1代對(duì)象,再經(jīng)過(guò)若干次回收后,仍然存活,就會(huì)將該對(duì)象歸為2代對(duì)象。

在前面的描述中,涉及到一個(gè)“若干次”回收,那么這個(gè)“若干次”是指什么呢?在前面使用get_threshold函數(shù)獲取閾值時(shí)返回了(700,10,10),這個(gè)700就是引用計(jì)數(shù)策略的閾值,而后面的兩個(gè)10與分代策略有關(guān)。第1個(gè)10是指第0代對(duì)象經(jīng)過(guò)了10次垃圾回收后仍然存在,就會(huì)將其歸為第1代對(duì)象。第2個(gè)10是指第1代對(duì)象經(jīng)過(guò)了10次垃圾回收后仍然存在,就會(huì)將其歸為第2代對(duì)象。也就是說(shuō),GC需要執(zhí)行100次,才會(huì)掃描到第2代對(duì)象。當(dāng)然,也可以通過(guò)set_threshold函數(shù)來(lái)調(diào)整這些值。

import gc 
gc.set_threshold(600, 5, 6)

總結(jié)

本文主要講了Python如何自動(dòng)釋放內(nèi)存。主要有如下3種策略:

  1. 引用計(jì)數(shù)策略(為0時(shí)釋放)

  2. 循環(huán)引用策略(將相關(guān)引用計(jì)數(shù)器多減1)

  3. 分代策略(解決了GC的效率問(wèn)題)

通過(guò)這些策略的共同作用,可以讓Python更加有效地管理內(nèi)存,更進(jìn)一步地提高Python的性能。

獲取更多學(xué)習(xí)資源,可以關(guān)注“極客起源”公眾號(hào)

Python深度探索(1):內(nèi)存管理機(jī)制

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI