Python內存溢出
- 存在循環引用,gc不能釋放;
- 存在全局對象,該對象不斷的變大,占據內存;
- 使用了c或者c++擴展,擴展內存溢出了;
有時候使用pykafka, pykafka的balancedconsumer類有個參數queued_max_messages。
這個參數的意思balancedconsumer會為每個分區緩存消息,默認一個分區是2000條。
如果一條消息是1M,只有一個分區的話,就緩存了2000M,對於不夠內存的機器那就gg了。
將調用方法的參數調小來使用。
Python內存泄露
循環引用的變量並不會被回收,它會一直駐留在內存中,就會造成了內存泄漏(內存空間在使用完畢后未釋放)。
Python垃圾回收機制
GC作為現代編程語言的自動內存管理機制,
專注於兩件事:1. 找到內存中無用的垃圾資源 2. 清除這些垃圾並把內存讓出來給其他對象使用。
GC徹底把程序員從資源管理的重擔中解放出來,讓他們有更多的時間放在業務邏輯上。
但這並不意味着碼農就可以不去了解GC,畢竟多了解GC知識還是有利於我們寫出更健壯的代碼。
- 引用計數(Reference Counting)
原理: 每個對象維護一個ob_ref字段,用來記錄該對象當前被引用的次數,每當新的引用指向該對象時,它的引用計數ob_ref加1,
每當該對象的引用失效時計數ob_ref減1,一旦對象的引用計數為0,該對象立即被回收。
缺點: 額外空間+不能解決循環計數問題
- 標記清除(Mark—Sweep)
算法是一種基於追蹤回收(tracing GC)技術實現的垃圾回收算法。
它分為兩個階段:
第一階段是標記階段,GC會把所有的『活動對象』打上標記,
第二階段是把那些沒有標記的對象『非活動對象』進行回收。
那么GC又是如何判斷哪些是活動對象哪些是非活動對象的呢?
對象之間通過引用(指針)連在一起,構成一個有向圖,對象構成這個有向圖的節點,而引用關系構成這個有向圖的邊。
從根對象(root object)出發,沿着有向邊遍歷對象,可達的(reachable)對象標記為活動對象,不可達的對象就是要被清除的非活動對象。
根對象就是全局變量、調用棧、寄存器。
標記清除算法作為Python的輔助垃圾收集技術主要處理的是一些容器對象,比如list、dict、tuple,instance等,
因為對於字符串、數值對象是不可能造成循環引用問題。
缺點: 清除非活動的對象前它必須順序掃描整個堆內存,哪怕只剩下小部分活動對象也要掃描所有對象。
- 分代回收
分代回收是一種以空間換時間的操作方式,Python將內存根據對象的存活時間划分為不同的集合,每個集合稱為一個代。
Python將內存分為了3“代”,分別為年輕代(第0代)、中年代(第1代)、老年代(第2代),
他們對應的是3個鏈表,它們的垃圾收集頻率與對象的存活時間的增大而減小。
新創建的對象都會分配在年輕代,年輕代鏈表的總數達到上限時,Python垃圾收集機制就會被觸發,
把那些可以被回收的對象回收掉,而那些不會回收的對象就會被移到中年代去,
依此類推,老年代中的對象是存活時間最久的對象,甚至是存活於整個系統的生命周期內。
同時,分代回收是建立在標記清除技術基礎之上。
分代回收同樣作為Python的輔助垃圾收集技術處理那些容器對象
Python回收模塊
回收循環引用
import gc
sys.getrefcount(a)
unreachable_count = gc.collect()
gc.collect()方法專門用來處理這些循環引用,返回處理這些循環引用一共釋放掉的對象個數。
設置debug泄露模式
gc.set_debug(gc.DEBUG_LEAK),會把所有已經回收掉的unreachable的對象也都加入到garbage里面。
set_debug還有很多參數諸如gc.DEBUG_STAT|DEBUG_COLLECTABLE|DEBUG_UNCOLLECTABLE|DEBUG_SAVEALL等等,
設置了相關參數后gc模塊會自動檢測垃圾回收狀況並給出實時地信息反映。
設置分代回收參數
gc.get_threshold()
gc.set_threshold(threashold0,threshold1,threshold2)
這個方法涉及到之前說過的分代回收的策略。
python中默認把所有對象分成三代。第0代包含了最新的對象,第2代則是最早的一些對象。在一次垃圾回收中,所有未被回收的對象會被移到高一代的地方。
這個方法返回的是(700,10,10),這也是gc的默認值。
這個值的意思是說,在第0代對象數量達到700個之前,不把未被回收的對象放入第一代;
而在第一代對象數量達到10個之前也不把未被回收的對象移到第二代。
可以是使用gc.set_threshold(threashold0,threshold1,threshold2)來手動設置這組閾值。
相關書籍
《垃圾回收的算法與實現》
《Python源碼剖析》