在開始進入正題以前,咱們先來回憶下,計算機基礎原理的知識,爲何須要內存。咱們都知道計算機的CPU至關於人類的大腦,其運算速度很是的快,而咱們平時寫的數據,好比:文檔、代碼等都是存儲在磁盤上的。磁盤的存取速度徹底不能匹配cpu的運算速度,所以就須要一箇中間層來適配二者的不對等,內存由此而來,內存的存取速率很快,可是存儲空間不大。java
舉一個圖書館的例子,便於你們理解,咱們圖書館的書架就至關於磁盤,存放了大量的圖書能夠供咱們閱讀,可是若是書放在書架上,咱們沒辦法直接閱讀(效率低),只能將書取出來,放在書桌上看,那書桌就至關於內存。python
內存資源畢竟是有限的,因此在使用以後,必須被回收掉,不然系統運行一段時間後就會因無內存可用而癱瘓。咱們軟件測試領域經常使用的兩種語言:java和python,所有都採用內存自動回收的方法,也就是咱們只管申請內存,可是無論釋放內存,由jvm和python解釋器來按期觸發內存回收。做爲對比,C語言和C++中,程序員須要使用malloc申請內存,使用free去釋放內存,malloc和free必須成對的出現,不然很是容易出現內存問題。程序員
還拿上面圖書館的例子,假如圖書館的書看完以後放在書桌上就能夠(由於圖書可自動回收),那麼很快的,就沒有位置給新進來的同窗看書了。這時候就須要圖書館管理員(jvm或python解釋器)按期的回收圖書,清空書桌。不過正常狀況下,咱們離開圖書館時,要本身清空書桌,將書放回書架(相似C語言和C++的內存回收方式)。緩存
python經過引用計數來進行內存管理,每個python對象,都維護了一個指向該對象的引用計數。python的sys庫提供了getrefcount()函數來獲取對象的引用計數。下面咱們看個例子(注意:不一樣版本的python,運行結果不一樣,我這裏採用的是python3.7.4):bash
""" @author: xuanke @time: 2019/11/27 @function: 測試python內存 """ import sys class RefClass(object): def __init__(self): print("this is init") def ref_count_test(): # 驗證普通字符串 str1 = "abc" print(sys.getrefcount(str1)) # 驗證稍微複雜點的字符串 print(sys.getrefcount("xuankeTester")) # 驗證小的數字 print(sys.getrefcount(12)) # 驗證大的數字 print(sys.getrefcount(257)) # 驗證類 a = RefClass() print(sys.getrefcount(a)) # 驗證引用計數增長 b = a print(sys.getrefcount(a)) # 驗證引用計數減小 b = None print(sys.getrefcount(a)) if __name__ == '__main__': ref_count_test()
你們先來思考下,最終的結果會是什麼?!我以爲應該不少人都會答錯,由於不一樣版本的python,對引用變量個數有影響(主要是可複用的對象)。咱們先貼出來運行結果,再來分析產生結果的緣由:python2.7
27 4 9 3 this is init 2 3 2
不過提早聲明一點:sys.getrefcount函數在使用時,由於將對象(好比上例中的str1)做爲參數傳入,因此會額外增長一個變量(至關於getrefcount持有了str1的引用),所以實際每一個對象的實際引用計數都得減1。下面分別介紹下上面的幾種狀況:jvm
咱們能夠經過打印內存地址的方式來驗證上面這幾種狀況:函數
def memory_address_test(): str1 = 'xuankeTester' str2 = 'xuankeTester' print(id(str1)) print(id(str2)) str3 = 'abc' str4 = 'abc' print(id(str3)) print(id(str4)) a = 12 b = 12 print(id(a)) print(id(b)) c = 257 d = 257 print(id(c)) print(id(d))
按照咱們上面的分析,c和d的地址應該是不同的,a和b的地址是同樣的,字符串str1和str二、str3和str4內存地址都是同樣的。可是我在pycharm中,直接運行py文件,結果卻和預想的不一致,結果以下:測試
2854496960176 2854496960176 2854496857840 2854496857840 140724423258720 140724423258720 2854498931120 2854498931120
全部狀況的內存地址都是同樣的,這是爲何呢?我考慮到是否是pycharm對py文件作了優化,因而我又在命令行嘗試執行,結果仍是同樣的。因此,我猜想多是python解釋器在執行文件時,爲了提升py文件的執行效率,對文件的內存地址作了優化—相同內容的對象內存地址都同樣。優化
爲了驗證這個想法,我直接在python交互模式下執行,果真獲得了我想要的結果:
Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> a=12 >>> b=12 >>> id(a) 140724423258720 >>> id(b) 140724423258720 >>> a=257 >>> b=257 >>> id(a) 2559155778384 >>> id(b) 2559155778192 >>> a='xuankeTester' >>> b='xuankeTester' >>> id(a) 2559155711280 >>> id(b) 2559155711280 >>>
從上面能夠看到兩個257對應的地址確實是不同的,和咱們最初判斷的是一致的。
python經過對象的引用計數來管理內存,其實java的JVM也有用引用計數,因此理解了引用計數,爲咱們理解python的垃圾回收方法打下了基礎。本計劃這一篇文章就將python內存管理的機制講完的,可是發現一個內存引用計數就有不少東西得寫,因此索性就分兩篇文章來寫,以後再寫一篇文章來介紹python的垃圾回收方式。