Python中的垃圾回收是以引用計數爲主,分代收集爲輔,引用計數的缺陷是循環引用的問題。在Python中,若是一個對象的引用數爲0,Python虛擬機就會回收這個對象的內存。python
sys.getrefcount(a)
能夠查看a對象的引用計數,可是比正常計數大1,由於調用函數的時候傳入a,這會讓a的引用計數+1函數
致使引用計數+1的狀況:測試
a=23
b=a
func(a)
list1=[a,a]
致使引用計數-1的狀況:spa
del a
a=24
在網上看到一段有意思的例子:code
import sys def func(c): print ('in func function', sys.getrefcount(c) - 1) print (id(func.__globals__['a'])) print ('init', sys.getrefcount(11) - 1) a = 11 # print (id(a)) print ('after a=11', sys.getrefcount(11) - 1) b = a print ('after b=a', sys.getrefcount(11) - 1) func(11) print ('after func(a)', sys.getrefcount(11) - 1) list1 = [a, 12, 14] print ('after list1=[a,12,14]', sys.getrefcount(11) - 1) a=12 print ('after a=12', sys.getrefcount(11) - 1) del a print ('after del a', sys.getrefcount(11) - 1) del b print ('after del b', sys.getrefcount(11) - 1) # list1.pop(0) # print 'after pop list1',sys.getrefcount(11)-1 del list1 print ('after del list1', sys.getrefcount(11) - 1)
輸出的init不必定一致,做爲計數基礎便可(小數int 在python中會默認維護,由於python不少內置量都是小數int,即計數不可能爲0),輸出中有一點比較奇怪:在傳入函數中後計數增長爲2,而非設想的1,這是爲何?對象
咱們對函數進行修改:blog
def func(c): print ('in func function', sys.getrefcount(c) - 1) # print (id(func.__globals__['a'])) for attr in dir(func): print (attr, getattr(func, attr))
替換掉以前的函數,運行之能夠發現func.__globals__屬性中記錄了全局變量鍵值對 {'a': 11} 這樣(以及其餘信息),這就是額外的計數來歷:局部變量和全局變量的值是相同的,這致使計數+2。內存
咱們知道,函數也是對象,即便不在函數體內咱們也能夠調用函數的屬性、方法,咱們把下面一句從函數體中拿出來單獨運行,就發現,因爲脫離了函數做用域,函數的__globals__屬性中對於全局變量的記載('a'、'b')都不見了,這能夠理解,脫離了做用域,局部變量和全局變量都失去了意義(二者都是針對某個做用域的概念)。作用域
for attr in dir(func):
print (attr, getattr(func, attr))
測試發現__globals__中記錄的{'a': 11}和函數體外的變量 a 是同一個對象(id相同),且在外面增長 b 的時候引用計數差值並無增長,因此這個解釋是不對的,實際上另外一個引用是函數棧保存了入參對形參的引用(知乎找到的解釋)。get
看到了知乎的解釋,我決定自行驗證一下,測試代碼以下:
import sys def func(c): print ('in func function', sys.getrefcount(c)-1) print ('init', sys.getrefcount(11) - 1) func(11) print ('init', sys.getrefcount(11) - 1)
init 106
in func function 108
init 106
進一步分析一下:
from dis import dis order = \ """ def func(c): print ('in func function', sys.getrefcount(c)-1) print ('init', sys.getrefcount(11) - 1) func(11) print ('init', sys.getrefcount(11) - 1) """ dis(order)
返回值以下,
2 0 LOAD_CONST 0 (<code object func at 0x0000029849AD5D20, file "<dis>", line 2>) 2 LOAD_CONST 1 ('func') 4 MAKE_FUNCTION 0 6 STORE_NAME 0 (func) 5 8 LOAD_NAME 1 (print) 10 LOAD_CONST 2 ('init') 12 LOAD_NAME 2 (sys) 14 LOAD_ATTR 3 (getrefcount) 16 LOAD_CONST 3 (11) 18 CALL_FUNCTION 1 20 LOAD_CONST 4 (1) 22 BINARY_SUBTRACT 24 CALL_FUNCTION 2 26 POP_TOP 6 28 LOAD_NAME 0 (func) 30 LOAD_CONST 3 (11) 32 CALL_FUNCTION 1 34 POP_TOP 7 36 LOAD_NAME 1 (print) 38 LOAD_CONST 2 ('init') 40 LOAD_NAME 2 (sys) 42 LOAD_ATTR 3 (getrefcount) 44 LOAD_CONST 3 (11) 46 CALL_FUNCTION 1 48 LOAD_CONST 4 (1) 50 BINARY_SUBTRACT 52 CALL_FUNCTION 2 54 POP_TOP 56 LOAD_CONST 5 (None) 58 RETURN_VALUE
着重看6:
6 28 LOAD_NAME 0 (func) 30 LOAD_CONST 3 (11) 32 CALL_FUNCTION 1 34 POP_TOP
這裏將函數 func 和常量11壓入了函數棧,會致使引用計數 +1。
咱們再看下面代碼:
dis(func)
返回的是 func 函數內部操做:
這裏會讀取變量 c(偏移量8的操做碼),最終致使了增長計數爲 2。