您好,登錄后才能下訂單哦!
這篇文章主要介紹Python跑循環時內存泄露怎么辦,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
Python跑循環時內存泄露
今天在用Tensorflow跑回歸做測試時,僅僅需要循環四千多次 (補充說一句,我在個人PC上跑的)。運行以后,我就吃飯去了。等我回來后,Console窗口直接亮紅了!!!
import numpy as np import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import tensorflow as tf import matplotlib.font_manager as fm myfont = fm.FontProperties(fname='C:/Windows/Fonts/simsun.ttc') sess = tf.Session() for j in range(0,4096): print('第' + str(j) + '次回歸') ......
此處忘了截圖,反正就是說Keras出現了什么什么錯誤。然后我就順手重啟了工程。
接著就瞪著屏幕,為什么跑一半出錯了???
這個時候我發現電腦管家的小火箭好像有點‘暴躁',打開任務管理器一看,果然。。。
Python占用內存都快兩個G了,但是平時跑沒有占用這么多呀。我就猜想是不是因為跑循環,內存沒有釋放,導致最后溢出,然后code就崩了。
抱著猜想的心態等了一分鐘,然后。。
WTF 飆到兩千三百多兆的占用了。結論很明顯了,就是沒有釋放內存!!!
這讓我想起來一個月前我在集群上并行GPU跑LSTM時,出現了Out of memory,
這是2080ti呀,足足十一個G的內存,雖然當時我就用了一塊顯卡,但也不至于溢出吧。當時沒有想到是這個問題,現在回想一下十有八九就是沒有釋放內存。
至于為什么沒有釋放內存,那就要從下面這個人出生的時候說起了—>
還記得那是一個月黑風高夜晚,天空電閃雷鳴。。。。。。
hhh,開個玩笑,這個人是Python之父,不過我覺得接下來我要說的Python垃圾收集機制或多或少和他有一定的關系。
Python垃圾收集機制
現在的高級語言如java,c#等,都采用了垃圾收集機制,而不再是c,c++里用戶自己管理維護內存的方式。自己管理內存極其自由,可以任意申請內存,但如同一把雙刃劍,為大量內存泄露,懸空指針等bug埋下隱患。
對于一個字符串、列表、類甚至數值都是對象,且定位簡單易用的語言,自然不會讓用戶去處理如何分配回收內存的問題。
python里也同java一樣采用了垃圾收集機制,不過不一樣的是:
python采用的是引用計數機制為主,標記-清除和分代收集兩種機制為輔的策略。
Python中的內存管理過程非常簡單。Python通過保持對每個對象在程序中的引用計數來處理其對象,這意味著每個對象存儲在程序中被引用的次數。此計數隨程序運行時更新,并且當計數為零時,這意味著程序不再可訪問該計數。因此,解釋器可以回收和釋放該對象的內存。
class User(object): def __del__(self): print("No reference left for {}".format(self)) user1 = User() user2 = user1 user3 = user1
在此示例中,我們制作了一個類和3個引用變量指向同一對象。讓我們將其可視化:
現在,讓變量user1,user2和user3指向None而不是User實例。
>>> user1 = None >>> user2 = None >>> user3 = None No reference left for <__main__.User object at 0x212bee9d9>
通過以上代碼,引用已更改為:
將最后一個變量分配user3給后None,該對象將被垃圾回收,這將調用該__del__函數。
從根本上講,每當引用對象時,Python對象的引用計數都會增加,而在取消引用對象時,Python的引用計數會減少。如果對象的引用計數為0,則將釋放該對象的內存。您程序的代碼無法禁用Python的引用計數。
python跑循環為什么沒有釋放內存
有些人認為,引用計數是A poor man's garbage collector 。很大一部分原因是它存在一些缺點,其中就包括無法檢測到循環應用。
如果在循環引用中的對象定義了__del__函數,那么在循環引用中Python解釋器無法判斷析構對象的順序,因此就不做處理,python gc不能進行回收。
解決辦法
在提出解決問題之前,我們要先找到問題。因為我這個Code需要畫圖,然后保存到本地。我就想是不是畫的圖導致占用過多內存。
F_max = max(y_result) plt.figure(figsize=(15, 5)) plt.subplot(131) plt.scatter(Yp_data, Yt_data, alpha=0.8) plt.title(u'回歸前結果',fontproperties=myfont) plt.plot([0,F_max], [0,F_max], color = 'r') plt.subplot(132) plt.scatter(y_result, Yt_data, alpha=0.8) plt.title(u'回歸后結果',fontproperties=myfont) plt.plot([0,F_max], [0,F_max], color = 'r') plt.subplot(133) plt.plot(loss_vec, 'k-') plt.title('loss per Generation') plt.xlabel('Generation') plt.ylabel('Loss') plt.savefig('D:/lwt/py/Regression/figure/{} .png'.format(_type), dpi=100) plt.show()
有想法你就要去做是吧,然后我就加了兩行代碼。
plt.close('all') plt.clf()
這兩句是用來清除內存中的圖像和清理掉 axes,并且為了盡可能的減少內存占用,把plt.show()
都刪除了
然后運行Code,打開任務管理器,測試是否管用。
貌似python占用的內存是沒有之前上升的那么快了,不知道是不是心理作用hh。反正效果不明顯,看來得從別的地方下手了。
排除掉這個可能,那就是回收機制的鍋了。既然你不主動,那我就主動點咯(猿式陰笑嘿嘿)。
for x in list(locals().keys())[:]: del locals()[x] gc.collect()
for循環就不要過多解釋了,先說del
語句的用法
del obj_name
這del
是一個Python關鍵字。而且,obj_name
可以是變量,用戶定義的對象,列表,列表中的項,字典等。可以用來刪除用戶定義的對象;刪除變量,列表和字典;從列表中刪除項目和切片;從字典中刪除鍵等等。具體用法大家可以查找相關文檔了解。
gc是python的垃圾回收器接口,gc.collect(generation=2)
若被調用時不包含參數,則啟動完全的垃圾回收。可選的參數 generation 可以是一個整數,指明需要回收哪一代(從 0 到 2 )的垃圾。當參數 generation 無效時,會引發 ValueError 異常。返回發現的不可達對象的數目。每當運行完整收集或最高代 (2) 收集時,為多個內置類型所維護的空閑列表會被清空。 由于特定類型特別是 float 的實現,在某些空閑列表中并非所有項都會被釋放。
實測,有明顯的效果,內存占用上升的速度明顯減小了,不過總體還是承上升的趨勢。相比之前,好太多了有沒有。
方法總比困難多,解決內存泄漏也還有其他的辦法。我們還可以用objgraph、weakref等工具來分析并解決內存泄露、循環引用問題。實在不行,還有一個超級硬核的辦法,自己動手寫一個騰訊電腦管家小火箭的腳本,時不時的讓它上上天(騰訊記得打錢啊啊啊)。哈哈哈
在任何環境,不管是服務器,客戶端,內存泄露都是非常嚴重的事情。如果是線上服務器,那么一定得有監控,如果發現內存使用率超過設置的閾值則立即報警,盡早發現些許還有救。
新的問題
不要強行收集垃圾太多次。這是因為,即使要釋放內存,仍然需要花費時間來評估對象是否符合垃圾收集條件。我在實測中的確發現運行速度有些下降。
還有就是他會把全局的變量都刪了,以至于出現np
和pd
等導入的包都not defined。我的建議是把循環寫到函數里,做到只對某個函數起作用。
以上是“Python跑循環時內存泄露怎么辦”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。