Cpython解釋器的垃圾回收機(jī)制:
什么是垃圾色难?
當(dāng)一個(gè)值身上沒有綁定任何變量名(該值的引用數(shù)為零)該值就是垃圾拇砰。
小整數(shù)對(duì)象池:
整數(shù)在程序中使用非常規(guī)范,python為了優(yōu)化速度扬虚,使用了小整數(shù)對(duì)象池重罪,避免為了頻繁申請(qǐng)和銷毀內(nèi)存空間樱哼。python對(duì)小整數(shù)的定義是[-5,256]這些整數(shù)對(duì)象是提前建好的哀九,不會(huì)被垃圾回收。在python中小整數(shù)對(duì)象池范圍內(nèi)的整數(shù)使用的都是同一個(gè)對(duì)象搅幅。同理單個(gè)字母也是阅束,但當(dāng)定義相同的2個(gè)字符串時(shí),引用計(jì)數(shù)為零時(shí)茄唐,才會(huì)調(diào)用垃圾回收機(jī)制息裸。
大整數(shù)對(duì)象池:
每一個(gè)大整數(shù)都定義一個(gè)新的對(duì)象。
intern機(jī)制
a1 = 'xiake'
a2 = 'xiake'
a3 = 'xiake'
python不會(huì)創(chuàng)建三個(gè)對(duì)象沪编,如果是的話內(nèi)存恒容易溢出呼盆,所以python有intern機(jī)制,可以讓他們只占用一個(gè) xiake 蚁廓。
小總結(jié):
小整數(shù)[-5,256]共用對(duì)象访圃,常駐內(nèi)存
單個(gè)字符共用對(duì)象,常駐內(nèi)存
單個(gè)單詞相嵌,不可修改腿时,默認(rèn)開啟了intern機(jī)制,共用對(duì)象,引用計(jì)數(shù)為零時(shí)饭宾,銷毀
字符串(含有空格)批糟,不可以修改,沒開啟intern看铆,不共用對(duì)象徽鼎,引用計(jì)數(shù)為零時(shí)銷毀
大整數(shù)不共用內(nèi)存,引用計(jì)數(shù)為零時(shí)銷毀
數(shù)值類型與字符串類型在python中都是不可變的弹惦,這就意味著你無法修改對(duì)象值否淤,每次對(duì)變量修改實(shí)際上創(chuàng)建一個(gè)新的對(duì)象
引用計(jì)數(shù)機(jī)制
python每個(gè)東西都是對(duì)象,它的核心就是一個(gè)結(jié)構(gòu)體:pyObject
引用計(jì)數(shù)的優(yōu)點(diǎn)
簡(jiǎn)單
實(shí)時(shí)性:一旦沒有引用肤频,內(nèi)存就直接釋放叹括。處理回收內(nèi)存的時(shí)間分?jǐn)偟狡綍r(shí)。
缺點(diǎn)
維護(hù)引用計(jì)數(shù)消耗資源
循環(huán)引用帶來的案例沒有解決
list1= []
list2= []
list1.append(list2)
list2.append(list1)
list1與list2相互引用宵荒,如果不存在其他對(duì)象對(duì)它們的引用,list1與list2的引用計(jì)數(shù)也仍然為1净嘀,所占用的內(nèi)存永遠(yuǎn)無法被回收报咳,這將是致命的。 對(duì)于如今的強(qiáng)大硬件挖藏,缺點(diǎn)1尚可接受暑刃,但是循環(huán)引用導(dǎo)致內(nèi)存泄露,注定python還將引入新的回收機(jī)制:分代收集膜眠。
分代收集垃圾機(jī)制
第一岩臣, Python使用一種不同的鏈表來持續(xù)追蹤活躍的對(duì)象溜嗜,Python的內(nèi)部C代碼將其稱為零代鏈表(Generation Zero)。每次當(dāng)你創(chuàng)建一個(gè)什么對(duì)象或其他什么值的時(shí)候架谎,Python會(huì)將其添加到零代鏈表炸宵。
第二,隨后谷扣,Python會(huì)循環(huán)遍歷零代列表上的每個(gè)對(duì)象土全,找出列表中每個(gè)互相引用的對(duì)象,根據(jù)規(guī)則減掉其引用計(jì)數(shù)会涎。在這個(gè)過程中裹匙,Python會(huì)一個(gè)接一個(gè)的統(tǒng)計(jì)內(nèi)部引用的數(shù)量以防過早地釋放它們。
第三末秃,通過識(shí)別內(nèi)部引用概页,Python能夠減少許多零代鏈表循環(huán)引用的對(duì)象,這意味著回收器可以釋放它們并回收內(nèi)存了练慕。剩下的活躍對(duì)象則被移動(dòng)到一個(gè)新的鏈表:一代鏈表惰匙。
第四,周期性地從一個(gè)對(duì)象到另一個(gè)對(duì)象追蹤引用以確定對(duì)象是否還是活躍的贺待,正在被程序所使用的徽曲。清理完一代鏈表后,把活躍的對(duì)象麸塞,或者正在被程序使用的對(duì)象秃臣,移動(dòng)到二代鏈表中,最終在二代鏈表中清除數(shù)據(jù)哪工。這就是所謂的分代收集垃圾機(jī)制奥此。
Python中的GC閥值
1,Python什么時(shí)候會(huì)進(jìn)行這個(gè)標(biāo)記過程雁比?隨著你的程序運(yùn)行稚虎,Python解釋器保持對(duì)新創(chuàng)建的對(duì)象,以及因?yàn)橐糜?jì)數(shù)為零而被釋放掉的對(duì)象的追蹤偎捎。從理論上說蠢终,這兩個(gè)值應(yīng)該保持一致,因?yàn)槌绦蛐陆ǖ拿總€(gè)對(duì)象都應(yīng)該最終被釋放掉茴她。
2寻拂,當(dāng)然,事實(shí)并非如此丈牢。因?yàn)檠h(huán)引用的原因祭钉,并且因?yàn)槟愕某绦蚴褂昧艘恍┍绕渌麑?duì)象存在時(shí)間更長(zhǎng)的對(duì)象,從而被分配對(duì)象的計(jì)數(shù)值與被釋放對(duì)象的計(jì)數(shù)值之間的差異在逐漸增長(zhǎng)己沛。一旦這個(gè)差異累計(jì)超過某個(gè)閾值慌核,則Python的收集機(jī)制就啟動(dòng)了距境,并且觸發(fā)上邊所說到的零代算法,釋放“浮動(dòng)的垃圾”垮卓,并且將剩下的對(duì)象移動(dòng)到一代列表垫桂。
3,隨著時(shí)間的推移扒接,程序所使用的對(duì)象逐漸從零代列表移動(dòng)到一代列表伪货。而Python對(duì)于一代列表中對(duì)象的處理遵循同樣的方法,一旦被分配計(jì)數(shù)值與被釋放計(jì)數(shù)值累計(jì)到達(dá)一定閾值钾怔,Python會(huì)將剩下的活躍對(duì)象移動(dòng)到二代列表碱呼。
4,通過這種方法宗侦,你的代碼所長(zhǎng)期使用的對(duì)象愚臀,那些你的代碼持續(xù)訪問的活躍對(duì)象,會(huì)從零代鏈表轉(zhuǎn)移到一代再轉(zhuǎn)移到二代矾利。通過不同的閾值設(shè)置姑裂,Python可以在不同的時(shí)間間隔處理這些對(duì)象。Python處理零代最為頻繁男旗,其次是一代然后才是二代舶斧。
查看一個(gè)對(duì)象的引用計(jì)數(shù)
import sys
name = "xiaoka"
print(sys.getrefcount(name))
什么時(shí)候觸發(fā)垃圾回收機(jī)制
當(dāng)gc模塊的計(jì)數(shù)器達(dá)到閾值時(shí),自動(dòng)回收垃圾
手動(dòng)調(diào)用gc.collect(),手動(dòng)回收垃圾
程序退出的時(shí)候察皇,python解釋器會(huì)回收垃圾
引用計(jì)數(shù)的問題
引用計(jì)數(shù)增加
age=18#18引用計(jì)數(shù)增加1
x=age#18的引用計(jì)數(shù)為2
print(age)
print(x)
引用計(jì)數(shù)減少
age=19#18的引用次數(shù)為1
del x#18的引用值為0
變量值的三個(gè)特征
id反映了內(nèi)存地址
type表示數(shù)據(jù)類型
值
age=18
print(id(age))
print(type(age))
print(age)
總結(jié)
id相同茴厉,值一定相同。
值相同什荣,id值不一定相同矾缓。
x='name:zyc,age:18'
y='name:zyc,age:18'
print(id(x))
print(id(y))
2847698422856
2847698422856
is與==的區(qū)別
is表示id是否相同
==表示值是否相等
可變類型與不可變類型
可變類型: 值改變,id不變稻爬,證明就是在改變?cè)?br> 不可變類型:值改變嗜闻,id也變,證明根本不是在改變?cè)滴Τ莿?chuàng)建了新值琉雳,原值就是不可變類型
x=10#不可變類型
print(id(x))
x=11
print(id(x))
l=['a','b','c']#可變類型
print(id(l))
l[0]='A'
print(id(l))
print(l)