2018-08-12 python內(nèi)存管理

python內(nèi)存管理

1. 引用和對(duì)象

??我們先看這樣一個(gè)賦值語(yǔ)句 a=1

??在 python 中把介,整數(shù) 1 為一個(gè)對(duì)象定躏,而 a 是一個(gè)引用 a→1

??Python是動(dòng)態(tài)類型的語(yǔ)言(動(dòng)態(tài)類型)万矾,對(duì)象與引用分離。Python通過(guò)引用來(lái)操作對(duì)象痛悯。

??在Python中嵌巷,整數(shù)和短小的字符,Python都會(huì)緩存這些對(duì)象丹皱,以便重復(fù)使用妒穴。

# id()是Python內(nèi)置的函數(shù),它用于返回對(duì)象的身份(identity)摊崭,也就是內(nèi)存地址讼油。
# hex 返回一個(gè)數(shù)的十六進(jìn)制表示
a = 1
b = 1
print(id(1))
print(id(a))
print(id(b))
print(hex(id(a)))
1644917568
1644917568
1644917568
0x620b7340

??從上邊代碼的輸出可以看出,a 和 b 其實(shí)是指向同一個(gè)對(duì)象的兩個(gè)引用呢簸。

??我們現(xiàn)在使用 is 來(lái)檢測(cè)一下矮台,整數(shù)和短小的字符指的是什么。

a = 1
b = 1
print(a is b)
True
a = 'good'
b = 'good'
print(a is b)
True
a = 'good night'
b = 'good night'
print(a is b)
print(a[:4] is 'good')
print(a[:4] is b[:4])
False
False
False
a = [1]
b = [1]
print(a is b)
False

??可以看到根时,python緩存了整數(shù)和短小的字符瘦赫,所以指向它們的引用都是指向的緩存好的對(duì)象。

??對(duì)于較長(zhǎng)的字符蛤迎,則是另外分配的內(nèi)存耸彪,即使是相同的字符串的引用,由于它們指向的對(duì)象是另外分配的內(nèi)存忘苛,它們的內(nèi)存地址也是不同的。

a = 'good night'
b = 'good night'
print(hex(id(a)))
print(hex(id(b)))
0x19b922e1070
0x19b922e17b0
a 和 b 指向的內(nèi)存

??在 Python 中唱较,每個(gè)對(duì)象都有指向該對(duì)象的引用計(jì)數(shù)扎唾。我們可以使用 sys.getrefcount() 來(lái)查看某個(gè)對(duì)象的引用計(jì)數(shù),參數(shù)傳遞給 getrefcount()時(shí)南缓,會(huì)創(chuàng)建一個(gè)臨時(shí)的引用胸遇,所以 getrefcount() 會(huì)比期望結(jié)果多 1。

import sys
a = [1, 2]
sys.getrefcount(a)
2

??Python 中的容器對(duì)象汉形,如 list纸镊、tuple、dict 等概疆,可以包含多個(gè)對(duì)象逗威。實(shí)際上,它們包含的只是對(duì)象的引用而已岔冀。

a = [1, 2]
b = [a]
print(b)
[[1, 2]]
a[0] = -1
print(b)
[[-1, 2]]

??使用 del 可以刪除一個(gè)引用凯旭,同時(shí)也會(huì)減少相應(yīng)對(duì)象的引用計(jì)數(shù)。此外,將引用指向別的對(duì)象也會(huì)使引用計(jì)數(shù)減少罐呼。

a = [1]
b = a
c = b
print( sys.getrefcount(c) )
del a # del 刪除變量鞠柄,減少引用計(jì)數(shù)
print( sys.getrefcount(c) )
b = None # 指向別的對(duì)象,減少引用計(jì)數(shù)
print( sys.getrefcount(c) )
4
3
2

??兩個(gè)對(duì)象還可以相互引用嫉柴,這樣會(huì)形成一個(gè)引用環(huán)厌杜。

a = [1]
b = [a]
a.append(b)
print(a)
[1, [[...]]]

??一個(gè)對(duì)象也有可能會(huì)形成引用環(huán)。

a = [1]
a.append(a) # 如果是 a = [a]计螺,則不會(huì)形成引用環(huán)夯尽,此時(shí) a 為 [[1]]
print(a)
[1, [...]]

2.垃圾回收

??當(dāng)Python中的對(duì)象越來(lái)越多,它們將占據(jù)越來(lái)越大的內(nèi)存危尿,這個(gè)時(shí)候就需要垃圾回收(Garbage Collection)了呐萌。當(dāng)Python的某個(gè)對(duì)象的引用計(jì)數(shù)降為0時(shí),說(shuō)明沒有任何引用指向該對(duì)象谊娇,該對(duì)象就成為要被回收的垃圾了肺孤。

??不過(guò),垃圾回收時(shí)济欢,Python不能進(jìn)行其它的任務(wù)赠堵。頻繁的垃圾回收將大大降低Python的工作效率。如果內(nèi)存中的對(duì)象不多法褥,就沒有必要總啟動(dòng)垃圾回收茫叭。所以,Python只會(huì)在特定條件下半等,自動(dòng)啟動(dòng)垃圾回收揍愁。當(dāng)Python運(yùn)行時(shí),會(huì)記錄其中分配對(duì)象(object allocation)和取消分配對(duì)象(object deallocation)的次數(shù)杀饵。當(dāng)兩者的差值高于某個(gè)閾值時(shí)莽囤,垃圾回收才會(huì)啟動(dòng)。我們可以通過(guò)gc模塊的get_threshold()方法切距,查看該閾值:

import gc
print(gc.get_threshold())
(700, 10, 10)

??后面的兩個(gè)10是與分代回收相關(guān)的閾值朽缎。700即是垃圾回收啟動(dòng)的閾值,可以通過(guò) gc.set_threshold() 方法重新設(shè)置谜悟。另外话肖,還可以手動(dòng)回收gc.collect()

??另外,引用環(huán)會(huì)給 GC 帶來(lái)很大的麻煩葡幸。對(duì)于上面提到的一個(gè)對(duì)象引用環(huán)的情況最筒,即使刪除了引用 a ,list 對(duì)象的引用計(jì)數(shù)也不會(huì)為0蔚叨。


單對(duì)象引用環(huán)

??Python 采用了分代回收的策略是钥。這一策略的基本假設(shè)是掠归,存活時(shí)間越久的對(duì)象,越不可能在后面的程序中變成垃圾悄泥。我們的程序往往會(huì)產(chǎn)生大量的對(duì)象虏冻,許多對(duì)象很快產(chǎn)生和消失,但也有一些對(duì)象長(zhǎng)期被使用弹囚。出于信任和效率厨相,對(duì)于這樣一些“長(zhǎng)壽”對(duì)象,我們相信它們的用處鸥鹉,所以減少在垃圾回收中掃描它們的頻率蛮穿。

??Python將所有的對(duì)象分為0,1毁渗,2三代践磅。所有的新建對(duì)象都是0代對(duì)象。當(dāng)某一代對(duì)象經(jīng)歷過(guò)垃圾回收灸异,依然存活府适,那么它就被歸入下一代對(duì)象。垃圾回收啟動(dòng)時(shí)肺樟,一定會(huì)掃描所有的0代對(duì)象檐春。如果0代經(jīng)過(guò)一定次數(shù)垃圾回收,那么就啟動(dòng)對(duì)0代和1代的掃描清理么伯。當(dāng)1代也經(jīng)歷了一定次數(shù)的垃圾回收后疟暖,那么會(huì)啟動(dòng)對(duì)0,1田柔,2俐巴,即對(duì)所有對(duì)象進(jìn)行掃描。

??這兩個(gè)次數(shù)即上面get_threshold()返回的(700, 10, 10)返回的兩個(gè)10硬爆。也就是說(shuō)窜骄,每10次0代垃圾回收,會(huì)配合1次1代的垃圾回收摆屯;而每10次1代的垃圾回收,才會(huì)有1次的2代垃圾回收糠亩。

??另外虐骑,引用環(huán)會(huì)給 GC 帶來(lái)很大的麻煩。

a = [1]
a.append(a) # 如果是 a = [a]赎线,則不會(huì)形成引用環(huán)廷没,此時(shí) a 為 [[1]]
print(a)
[1, [...]]

??對(duì)于上面提到的一個(gè)對(duì)象引用環(huán)的情況,即使刪除了引用 a 垂寥,list 對(duì)象的引用計(jì)數(shù)也不會(huì)為0颠黎,不會(huì)被垃圾回收另锋。


單對(duì)象引用環(huán)

??為了回收這樣的引用環(huán),Python復(fù)制每個(gè)對(duì)象的引用計(jì)數(shù)狭归,可以記為 gc_ref夭坪。假設(shè),每個(gè)對(duì)象 i过椎,該計(jì)數(shù)為 gc_ref_i室梅。Python 會(huì)遍歷所有的對(duì)象 i。對(duì)于每個(gè)對(duì)象 i 引用的對(duì)象 j疚宇,將相應(yīng)的 gc_ref_j 減 1亡鼠。在結(jié)束遍歷后,gc_ref 不為0的對(duì)象敷待,和這些對(duì)象引用的對(duì)象间涵,以及繼續(xù)更下游引用的對(duì)象,需要被保留榜揖。而其它的對(duì)象則被垃圾回收勾哩。


解決引用環(huán)

??下面例子中 a 指向的對(duì)象的引用計(jì)數(shù)為 2(=3-1),在去除引用 a 后變?yōu)?1根盒,由于對(duì)象引用了本身钳幅,所以它的引用計(jì)數(shù)應(yīng)該減一,即 gc_ref = 0炎滞,這表明改對(duì)象應(yīng)該被垃圾回收敢艰。

a = [1]
a.append(a) # 如果是 a = [a],則不會(huì)形成引用環(huán)册赛,此時(shí) a 為 [[1]]
print(a)
print(sys.getrefcount(a))
[1, [...]]
3

參考鏈接:http://www.cnblogs.com/vamei/p/3232088.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钠导,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子森瘪,更是在濱河造成了極大的恐慌牡属,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扼睬,死亡現(xiàn)場(chǎng)離奇詭異逮栅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)窗宇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門措伐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人军俊,你說(shuō)我怎么就攤上這事侥加。” “怎么了粪躬?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵担败,是天一觀的道長(zhǎng)昔穴。 經(jīng)常有香客問(wèn)我,道長(zhǎng)提前,這世上最難降的妖魔是什么吗货? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮岖研,結(jié)果婚禮上卿操,老公的妹妹穿的比我還像新娘。我一直安慰自己孙援,他們只是感情好害淤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拓售,像睡著了一般窥摄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上础淤,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天崭放,我揣著相機(jī)與錄音,去河邊找鬼鸽凶。 笑死币砂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的玻侥。 我是一名探鬼主播决摧,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼凑兰!你這毒婦竟也來(lái)了掌桩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤姑食,失蹤者是張志新(化名)和其女友劉穎波岛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體音半,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡则拷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了曹鸠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涩拙。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡访雪,死狀恐怖监憎,靈堂內(nèi)的尸體忽然破棺而出乌询,到底是詐尸還是另有隱情趾痘,我是刑警寧澤现诀,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布矮慕,位于F島的核電站旱幼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏耗溜。R本人自食惡果不足惜组力,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抖拴。 院中可真熱鬧燎字,春花似錦、人聲如沸阿宅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)洒放。三九已至蛉鹿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間往湿,已是汗流浹背妖异。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留领追,地道東北人他膳。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像绒窑,于是被迫代替她去往敵國(guó)和親棕孙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • python內(nèi)存管理是通過(guò)引用計(jì)數(shù)來(lái)實(shí)現(xiàn)的回论。當(dāng)對(duì)象的引用計(jì)數(shù)為0時(shí)散罕,會(huì)被gc回收。 為了探索對(duì)象在內(nèi)存的存儲(chǔ)傀蓉,我們...
    冬季戀歌1218閱讀 1,665評(píng)論 0 2
  • 包(lib)欧漱、模塊(module) 在Python中,存在包和模塊兩個(gè)常見概念葬燎。 模塊:編寫Python代碼的py...
    清清子衿木子水心閱讀 3,805評(píng)論 0 27
  • 上一篇文章為:→1.3.1import垃圾回收 垃圾回收(二) 1. Garbage collection(GC垃...
    lyh165閱讀 462評(píng)論 0 2
  • 1.元類 1.1.1類也是對(duì)象 在大多數(shù)編程語(yǔ)言中谱净,類就是一組用來(lái)描述如何生成一個(gè)對(duì)象的代碼段窑邦。在Python中這...
    TENG書閱讀 1,268評(píng)論 0 3
  • 紫帛翠玉鬧春風(fēng),隔岸桃花相映紅壕探。 草芽不知何時(shí)勝冈钦,三月鶯燕已上頭。
    大同行者閱讀 125評(píng)論 0 0