如何釋放Python佔用的內存?

最近剛剛接觸python,整天開著shell對著網站和書本輸命令,但是輸著輸著我就有了一個疑問,輸入進去的這些指令和指令運算的結果都是存在哪裡的?當我退出shell(指令都輸在shell裡面並沒有往sublime裡面寫)後,這些指令和結果佔用的內存會不會被釋放?在matlab這些軟體裡面我們可以使用一個非常簡單的clear來清空變數,python呢?求解答哈


試一下

&>&>&> from pympler import tracker
&>&>&> import random
&>&>&> tr = tracker.SummaryTracker()
&>&>&> a = [[random.random() for i in range(2000)] for i in range(2000)]
&>&>&> tr.print_diff()
types | # objects | total size
========================================================== | =========== | ============
float | 4000000 | 91.55 MB
list | 4286 | 31.82 MB
str | 2035 | 109.42 KB
dict | 86 | 87.34 KB
int | 233 | 5.46 KB
&&>&> import gc
&>&>&> gc.collect()
0
&>&>&> from sys import getsizeof
&>&>&> getsizeof(a)
16560

用getsizeof(a) 取到的只是a一個list的object的大小 實際的大小要加上a里的4000000個float和2000個list的大小 所以這個2000 * 2000的list大小應該是120多mb

gc也沒什麼好collect的

想要佔的少一點 最簡單的方法是 用Numpy

&>&>&> tr = tracker.SummaryTracker()
&>&>&> a = array([[random.random() for i in range(2000)] for i in range(2000)])
&>&>&> tr.print_diff()
types | # objects | total size
========================================================== | =========== | ============
numpy.ndarray | 1 | 30.52 MB
list | 3586 | 351.42 KB
str | 3315 | 178.17 KB
dict | 93 | 94.45 KB
int | 352 | 8.25 KB
&


LS 有人提到了引用計數,這是 Python 的垃圾回收策略。補充一下。

解釋器(也就是你說的 Shell)負責跟蹤對象的引用計數,垃圾收集器負責釋放內存。

如何釋放?可以通過銷毀對象的引用,使引用計數減少至 0。假設 x = 3,以下情況會使 3 這個整型對象的引用計數減少;

  1. 函數運行結束,所有局部變數都被銷毀,對象的引用計數也就隨之減少。例如 foo(x) 運行結束,x 被銷毀;

  2. 當變數被賦值給另一個對象時,原對象的引用計數也會減少。例如 x = 4,這時候 3 這個對象的引用計數就減 1 了;

  3. 使用 del 刪除一個變數也會導致對象引用減少。例如 del x;

  4. 對象從集合對象中移除。例如 lst.remove(x);

  5. 包含對象的集合對象被銷毀。例如 del lst;

這些操作都可能使對象變成垃圾回收對象,由垃圾收集器負責收集,當然垃圾收集器也負責處理循環引用對象。


這是關於Python的垃圾回收機制的問題。Python用了引用計數的方法,每有一個指針引用了一個變數,計數就+1,取消引用則-1。當某塊變數的引用計數為0時,它就自動地被回收了。

話說Python明明就有del命令好吧……


我用專欄里的一篇文章來答題。

專欄鏈接:給妹子講python,歡迎大家關注,提意見!

這裡我聊聊python中的垃圾收集機制。想系統的了解這個問題我們需要涉及引用機制、動態類型和共享引用這些基本概念。

先談談python中的對象引用機制和動態類型。的確,python使用變數的時候都沒有聲明變數的類型,這一點和C語言不同。但是,變數還可以工作,因為在python中類型是在運行的過程中自動決定的,而不是通過代碼聲明的,這意味著沒有必要事先聲明變數。

在python中,我們要明確一個概念:變數名和對象是劃分開的,變數名永遠沒有任何關聯的類型信息,類型是和對象關聯的,而不存在於變數名中。一個變數名當第一次被賦值的時候被創建,而當新的賦值表達式出現時,他會馬上被當前新引用的對象所代替。這就是python所謂的動態類型機制。具體看一個例子:

a = "abcde"
print(a)
a = [1,2,3,4,5]
print(a)

abcde
[1, 2, 3, 4, 5]

結合上面這個例子,我們再來從頭仔細理一理:

1、創建了一個字元串對象』abcde』,然後創建了一個變數a,將變數a和字元串對象』abcde』相連接,

2、之後又創建了一個列表對象[1,2,3,4,5],然後又將他和a相連接。

這種從變數到對象的連接,我們稱之為引用,以內存中的指針形式實現。因此直白的說,在內部,變數事實上是到對象內存空間的一個指針,而且指向的對象可以隨著程序賦值語句而不斷變化。

總結一下:變數名沒有類型,只有對象才有類型,變數只是引用了不同類型的對象而已。每一個對象都包含了兩個頭部信息,一個是類型標誌符,標識這個對象的類型,以及一個引用的計數器,用來表示這個對象被多少個變數名所引用,如果此時沒有變數引用他,那麼就可以回收這個對象。

基於上面談到的引用機制,我們再說說Python的垃圾收集機制

還是上面那個例子,每當一個變數名被賦予了一個新的對象,那麼之前的那個對象佔用的空間就會被回收,前提是如果他沒有被其他變數名或者對象引用。這種自動回收對象空間的機制叫做垃圾收集機制。

即當a被賦值給列表對象[1,2,3,4,5]時,字元串對象的內存空間就被自動回收(如果他沒有被別的變數引用)

具體的內部機制是這樣的:python在每個對象中保存了一個計數器,計數器記錄了當前指向該對象的引用的數目。一旦這個計數器被設置為0,這個對象的內存空間就會自動回收。當a被賦值給列表對象後,原來的字元串對象『abcde』的引用計數器就會變為0,導致他的空間被回收。這就使得我們不必像C++那樣需要專門編寫釋放內存空間的代碼了

最後再說說共享引用的內容吧:

如下所示,多個變數名引用了同一個對象,稱為共享引用

a = "abcde"
b = a
print(a)
print(b)

abcde
abcde

此時字元串對象』abcde』的引用計數是2,我們進一步往下看如果我們此時對變數a重新賦值呢?

a = "abcde"
b = a
a = [1,2,3,4]
print(a)
print(b)

[1, 2, 3, 4]
Abcde

結果是顯而易見的,變數a變成了列表對象的引用,而變數b依然是字元串對象』abcde』的引用,並且字元串對象的引用計數為由2變為1.

如果此時再對b進行重新賦值,字元串對象』abcde』的引用計數就會變為0,然後這個對象就被垃圾回收了。

總結一下,給一個變數賦一個新的值,並不是替換原始的對象,而是讓這個變數去引用完全不同的另一個對象,而原來的對象的引用計數會隨之減1。


在ipython 里輸入 reset 然後再輸入 y,可以清楚所有變數


沒有在交互模式定義函數、類之類的話,直接生成的對象的引用一直存在,不會被gc。

可以輸入dir() 查看一下所有變數,del 你自己的變數。

也可以直接退出python進程,操作系統會回收內存的。


推薦閱讀:

看遊戲引擎架構內存管理有個地方不太清楚?
C語言和內存管理有什麼關係?為什麼說學習C語言的關鍵在內存管理?
講C語言內存管理的書籍或者博客?
筆記本內存佔用突然變高,本來29%左右,現在開機就58%,沒開任何程序啊,急求,感謝?
asp.net 應用佔用內存過大如何排查?

TAG:Python | 內存管理 |