电竞比分网-中国电竞赛事及体育赛事平台

分享

python變量的內存機制

 南校區(qū)于瀟瀟 2019-08-09

python變量的內存機制

作為一門簡單易用的語言,且配備海量的庫,python可謂是程序員手中的掌中寶,編程本身就是一種將人類思維轉化為計算機思維的技術,如果不需要去追求極致的運行效率同時又不限制于計算機內存空間,python無疑是目前最方便的語言了。

作為一個合格的程序員,自然是要知其然并知其所以然,除了能夠應用python來放飛自我之外,同時也要探究python其內部的運行原理,首當其沖的python編程中必須要用到的變量以及背后的運行機制。

注:以下示例在linux平臺下編寫,使用python2.7

引用機制

python的變量-內存模型更像是C++中的引用機制,python中的每個變量不一定占用內存空間,變量更像是一份內存的引用,通過這個變量可以訪問到內存中的數(shù)據(jù),舉個例子:

>>>a=10>>>b=a>>>c=[1,2,3,4]>>>d=c>>>print '%x%x' %(id(a),id(b))>>>print '%x%x' %(id(c),id(d))

輸出結果:

b51080.b510807f28bf69b758.7f28bf69b758  

其中id()是python的系統(tǒng)函數(shù),返回對象的內存起始地址。

從結果可以看出,a與b,c與d變量對應的地址事實上為同一個地址,也就是當我們使用變量a和b時,使用的是同一個對象,而a,b是這個對象的引用,我們可以通過系統(tǒng)函數(shù)sys.getrefcount()來查看一個對象的引用數(shù)量:

>>>import sys>>>a=257>>>print sys.getrefcount(a)>>>b=a>>>print sys.getrefcount(a)

輸出結果:

23

顯然,這個結果并不在我們的預料當中,由于a和b在同一個地址,結果應該是1、2,為什么是2,3呢?

這是因為在sys.getrefcount()函數(shù)調用時,a作為參數(shù)也被引用了一次,所以出現(xiàn)了2、3的結果。

緩存小數(shù)據(jù)機制

上面講了python變量賦值時的內存機制,事情就這么完美結束了嗎?

并沒有?。?!

我們再來看一個例子:

>>> a=10>>> b=10>>> print '%x.%x' %(id(a),id(b))

輸出結果:

b51080.b51080

看到這個結果,我緩緩摘下我的眼鏡,拿95%濃度的醫(yī)用酒精仔仔細細擦了三遍之后再戴上看,沒看錯!這兩個變量還是同一個地址內容的引用,這一次兩個變量的初始化是獨立的,并非賦值初始化,為什么兩個變量還是同一個地址的引用呢?

答案是:

在Python中,Python會有一個緩存對象的機制,以便重復使用。當我們創(chuàng)建多個等于1的引用時,實際上是讓所有這些引用指向同一個對象,以達到節(jié)省資源的目的

原來是這樣!??!

但是仔細一想,這不對吧?如果每個數(shù)據(jù)都進行緩存,那豈不是對內存空間的極度浪費?還是說內存回收機制會過一段時間回收一次垃圾內存?
我們再來看下面一個例子:

>>> a=100>>> b=100>>> print '%d%d' %(id(a),id(b))>>> a=256>>> b=256>>> print '%d%d' %(id(a),id(b))>>> a=257 >>> b=257>>> print '%d%d' %(id(a),id(b))

輸出結果:

5223836.52238365225932.52259325241840.5241864

從結果來看,當a小于256時,這個值會被系統(tǒng)緩存循環(huán)利用,而當a>256時,系統(tǒng)并不會進行緩存(當然不僅僅是三次實驗的結果,博主后續(xù)還試了很多值,就不一一列出了)

我們來用另一種方法來驗證這個問題,即sys.getrefcount():

>>>import sys>>>a=10>>>print sys.getrefcount(a)>>>a=257>>>print sys.getrefcount(a)

輸出結果為:

152

結果顯而易見,10這個值被系統(tǒng)緩存,且在別處引用了多次,而257這個值為2(為什么為2而不是1在上面有解釋)

那么問題又來了,如果是其他類型的數(shù)據(jù)呢?我們接著看

>>>a='downey'>>>b='downey'>>>print '%d%d' %(id(a),id(b))

結果為:

39422528.39422528

短字符串也會有緩存機制

然后是list:

>>>a=[1,2,3]>>>b=[1,2,3]>>>print '%d%d' %(id(a),id(b))39704576.39745176

list并沒有緩存機制,從這里可以看出,python的緩存機制并不針對所有變量類型

變量緩存結論

根據(jù)各種實驗以及多方查證,結果表明:

  • python的變量其實是一種堆內存的引用,可以理解為一個實體的標簽,而在不同變量之間的拷貝復制(如a=b),他們所表示的對象實體是同一個
  • python會對-5-256(包括256)的整型數(shù)據(jù)和短字符串進行緩存以節(jié)省多次分配銷毀的開銷

看到這里,喜歡思考的朋友們不禁就要問了,緩存這些整型數(shù)據(jù)和短字符串真的對性能有明顯提升嗎?python代碼中能有多少個整型變量?

答案是:整型變量對應整型的內存對象,但是整型的內存對象并不僅僅對應整型的變量類型,容器中的整形元素可能也是整形變量的引用

如果你還有疑惑,我們來看看下面的例子:

>>> import sys>>> a=1>>> sys.getrefcount(a)128>>> b=[1,2,3]>>> sys.getrefcount(a)129

從打印的結果可以看出,整型變量a=1,表示a指向對象1,為1的引用,b[0]也被初始化為1,同樣的,b[0]同時也是對象1的引用,對于所有容器而言,都是這種形式,看到這里,各位觀眾老爺們應該是有所理解了吧。

關于python變量內存機制對變量使用的影響可以參考這一篇博客:python函數(shù)調用時參數(shù)傳遞方式

內存回收

既然說到了內存機制,必然涉及到分配和回收的機制,內存分配就很簡單,在定義對象的時候用到進行內存的分配,而內存的回收則沒那么簡單,因為在內存回收的過程中,python無法執(zhí)行其他任務,所以頻繁地內存回收會導致嚴重的效率問題,而內存回收間隔時間過長則會導致內存浪費嚴重,所以一般只有在特定時間內啟動內存回收。

python運行時,會記錄下來分配和釋放的次數(shù),只有當兩個值的差大于某個數(shù)值時,即

分配次數(shù)-釋放次數(shù)>觸發(fā)回收的閾值

時,python進行垃圾回收,我們可以使用get_threshold()方法來獲取閾值:

>>>import gc>>>print gc.get_threshold()

輸出結果:

(700,10,10)

這個700便是觸發(fā)內存回收的閾值。但是后面的兩個10又是什么意思呢?

這也是內存回收中的一種機制,叫做分代回收,這一策略的基本假設是:存在時間越久的對象,越不可能成為垃圾對象,即給予一些長期使用的對象更多信任。

Python將所有的對象分為0,1,2三代。所有的新建對象都是0代對象。當某一代對象經(jīng)歷過垃圾回收,依然存活,那么它就被歸入下一代對象。垃圾回收啟動時,一定會掃描所有的0代對象。如果0代經(jīng)過一定次數(shù)垃圾回收,那么就啟動對0代和1代的掃描清理。當1代也經(jīng)歷了一定次數(shù)的垃圾回收后,那么會啟動對0,1,2,即對所有對象進行掃描

這兩個次數(shù)即上面get_threshold()返回的(700, 10, 10)返回的兩個10。也就是說,每10次0代垃圾回收,會配合1次1代的垃圾回收;而每10次1代的垃圾回收,才會有1次的2代垃圾回收。

我們也可以手動地調整觸發(fā)回收的閾值,聰明的朋友們可以猜到這個方法了,既然有get,必然相對應的就是set:

import gcgc.set_threshold(600,8,7)

除了被動地等待系統(tǒng)回收,當然也可以手動地進行內存回收:

import gcgc.collect()  

其實java也好,python也好,每一種語言的內存機制將從根本上影響語言的執(zhí)行效率,所以在內存的處理上會有很多更加復雜的細節(jié),這里只是介紹了一個大體的框架,班門弄斧,歡迎路過的大神們指正和補充。

好了,關于python變量內存機制的問題就到此為止了,如果朋友們對于這個有什么疑問或者發(fā)現(xiàn)有文章中有什么錯誤,歡迎留言

個人郵箱:linux_downey@sina.com
原創(chuàng)博客,轉載請注明出處!

祝各位早日實現(xiàn)項目叢中過,bug不沾身.
(完)

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多