解決 python 在讀取 .json 檔案時佔用記憶體過多的問題

五 19 2月 2016 by leafwind
198

故事是這樣的

某天我發現原本執行都沒有問題的程式突然掛了

仔細一查發現是記憶體使用過多,因為有別人的程式需要資源,跑到一半就被 OS 給砍了

(題外話,砍的優先順序包括你的程式跑了多久、吃多少記憶體等等一堆參數)

而我心中以為,某些用不到的 object reference 在 function return 之後就會釋放

顯然並沒有

於是先找了一些方法像是 delgc.collect() 試圖手動釋放記憶體

但實驗證實也完全沒有用

最後找到一篇文章 (1)

python 要釋放指定的 object 所佔用的 memory 原來要用到 sub-process

著實讓我驚呆了,特此紀念解決的過程:

問題開始

給定一個 .json file,經過 json.load()json.loads() 讀取為 object 之後

佔用的 memory 可能會是原本 text file 的 8~10 倍(ex: 400M file => 5G memory,取決於 object content)

若這個 json file 本來就很大,讀取後系統資源可能就不夠,甚至有機會被 kernel kill

就算最後的 output 很小,使用 delgc.collect() 仍然無法在系統端看到記憶體被釋放

這個問題其實由兩個部分組成

  1. 沒有一個直接的方式釋放 process 在讀檔過程中所佔用的記憶體
  2. python 讀取 json 檔案非常佔用記憶體

Solution1. sub-process (multiprocessing)

使用 pyhton 內建的 multiprocessing module

可以間接做到「釋放用不到的記憶體」

我不知道 multiprocessing 這個 module 當初開發的目的有沒有包含記憶體釋放

但它確實可以做到 delgc.collect() 做不到的事情...

pros:

若程式處理完 .json 之後只會剩下少部分資料

我們可以把「讀檔->處理」這一段 code 寫成 subprocess

這樣只要 subprocess 執行完並 exit 後

所有暫時佔用的記憶體在 system 端就會被確實釋放

cons:

  • inter-process 之間的 object 傳遞似乎有某些限制:

object size 大到某個程度(ex. dict key size > 40k)之後就會卡死

(這個詞不精確,其實是跑很久沒回應,但不確定最後會不會停)

不過幾百個 key 的 dict 還是 OK 的

  • .json 檔案大到某個程度,一次讀取完所需記憶體比可用記憶體還大的話,此法就沒用了,只能求助法二。

Solution2. ijson (streaming)

pros:

  • 不管多大的 json file 都能處理(?)

cons:

  • 需要更動程式架構
  • ijson 雖然非常省記憶體,但讀取速度可能也較慢

(尚未做 benchmark,也無優化過,純粹體感)

[1.] [Huge memory usage of Python's json module?] (http://stackoverflow.com/questions/11057712/huge-memory-usage-of-pythons-json-module)


Comments