1. 程式人生 > 其它 >python函式多次呼叫記憶體溢位——python記憶體洩漏和記憶體溢位的解決方案

python函式多次呼叫記憶體溢位——python記憶體洩漏和記憶體溢位的解決方案

一、 記憶體洩漏

python 本身雖然也有垃圾回收的功能, 但是同樣也會產生記憶體洩漏問題;
對於一個python實現的,長期執行的後臺服務程序來說,如果記憶體持續增長,則很可能是有了 “記憶體洩漏” 。

記憶體洩漏原因:

有以下三種原因:

1 所用到C語言開發的底層模組中出現了記憶體洩漏;
2 程式碼中用到了全域性的list, dict或者其他容器, 不停的往這些容器中插入物件, 而忘記了在使用完之後進行刪除回收
3 程式碼中有“引用迴圈”, 並且被引用的物件定義了 __del__ 方法, 就會發生記憶體洩漏;

question:
1、 為什麼迴圈引用的物件定義了__del__ 方法之後, collect就不起作用了呢?
gc模組最常用的方法,就是gc.collect()

, 使用collect() 方法對迴圈引用的物件進行垃圾回收
如果我們在類中過載了__del__方法,__del__方法定義了在del語句刪除物件時,除了釋放記憶體空間以外的操作。
一般而言, 在使用del語句時,直譯器會檢視被刪除物件的引用計數, 如果為0,則釋放記憶體,並執行del方法;
迴圈引用,首先del語句出現時, 本身引用計數就不為0(因為迴圈引用存在), 所以直譯器不釋放記憶體
再者,執行collect方法時,會清除迴圈引用所產生的無效引用計數,從而達到del的目的,對於這兩個迴圈引用物件而言
python無法判斷呼叫它們的del方法會不會要用到對方那個物件,比如在進行b.del()時,可能會用到b.a也就a, 如果
在那之前a已經被釋放,則無法使用。
為了避免這種情況, collect方法預設不對過載了del方法的迴圈引用,進行物件回收,而它們的狀態會從unreachable轉變為
uncollectable。 由於是uncollectable的,自然就不會被collect,從而進入garbage表。
2、 記憶體洩漏診斷思路
無論哪一種方式的記憶體洩漏, 最終的表現形式都是python物件不停的增長;因此,首先需要找到這些異常物件。
3、 診斷步驟
工具: gc模組和objgraph模組
gc模組是python 垃圾收集器模組, gc使用標記清楚演算法回收垃圾
objgraph 診斷記憶體問題工具
1、 在服務程式迴圈邏輯中,選擇診斷點
2、 在診斷點,插入如下診斷語句
```python
import gc

import objgraph 

### 強制進行垃圾回收 

gc.collect() 

### 打印出物件數目最多的 50 個型別資訊 

objgraph.show_most_common_types(limit=50) 
```

4、 檢查統計資訊,找到異常物件

執行加入診斷語句的服務程式,並將列印到螢幕上的統計資訊重定向到日誌中。 
執行一段時間後,就可以來分析日誌,看看哪些物件在不停的增長。
比如,排查結果可能是:

  一個多執行緒程式,多個執行緒作為生產者,一個執行緒作為消費者,通過將一個 tuple 物件送入非同步佇列進行通訊。 
  由於消費者的處理速度跟不上生產者的速度,又沒有進行同步, 導致非同步佇列中的物件越來越多。

二、記憶體溢位

1、記憶體溢位原因

a 記憶體中載入的資料量過於龐大,如一次從資料庫取出過多資料

b 集合類中有對物件的引用,使用完後未清空,產生了堆積,使得JVM不能回收

c 程式碼中存在死迴圈或迴圈產生過多重複的物件實體

d 使用的第三方軟體中的BUG

e 啟動引數記憶體值設定的過小

2、記憶體溢位的解決方案

第一步,修改JVM啟動引數,直接增加記憶體(-Xms,-Xmx引數一定不要忘記加); 

第二步,檢查錯誤日誌,檢視“OutOfMemory”錯誤前是否有其 它異常或錯誤;

第三步,對程式碼進行走查和分析,找出可能發生記憶體溢位的位置。

第四步,使用記憶體檢視工具動態檢視記憶體使用情況

重點排查以下幾點:

  a 檢查對資料庫查詢中,是否有一次獲得全部資料的查詢。一般來說,如果一次取十萬條記錄到記憶體, 

  就可能引起記憶體溢位。這個問題比較隱蔽,在上線前,資料庫中資料較少,不容易出問題,

  上線後,資料庫中資料多了,一次查詢就有可能引起記憶體溢位。 

  因此對於資料庫查詢儘量採用*分頁的方式查詢*。

  b 檢查程式碼中是否有死迴圈或遞迴呼叫。

  c 檢查是否有大迴圈重複產生新物件實體。

  d 檢查List、MAP等集合物件是否有使用完後,未清除的問題。List、MAP等集合物件會始終存有對物件的引用,使得這些物件不能被GC回收。

三、記憶體洩漏和記憶體溢位的區別

記憶體溢位: 是指向JVM申請記憶體空間時沒有足夠的可用記憶體了,就會丟擲OOM即記憶體溢位。

記憶體洩漏: 是指向JVM申請了一塊記憶體空間,使用完後沒有釋放,由於沒有釋放,這塊記憶體區域其他類載入的時候無法申請,

  同時當前類又沒有這塊記憶體空間的記憶體地址了也無法使用,相當於丟了一塊記憶體,這就是記憶體洩漏。

值得注意的是記憶體洩漏最終會導致記憶體溢位,很好理解,記憶體丟了很多最後當然記憶體不夠用了。