欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python跑循環(huán)時(shí)內(nèi)存泄露的解決方法

 更新時(shí)間:2020年01月13日 09:52:46   作者:L_W_T_  
這篇文章主要介紹了Python跑循環(huán)時(shí)內(nèi)存泄露的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

Python跑循環(huán)時(shí)內(nèi)存泄露

今天在用Tensorflow跑回歸做測試時(shí),僅僅需要循環(huán)四千多次 (補(bǔ)充說一句,我在個(gè)人PC上跑的)。運(yùn)行以后,我就吃飯去了。等我回來后,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出現(xiàn)了什么什么錯(cuò)誤。然后我就順手重啟了工程。

接著就瞪著屏幕,為什么跑一半出錯(cuò)了???


這個(gè)時(shí)候我發(fā)現(xiàn)電腦管家的小火箭好像有點(diǎn)‘暴躁',打開任務(wù)管理器一看,果然。。。


Python占用內(nèi)存都快兩個(gè)G了,但是平時(shí)跑沒有占用這么多呀。我就猜想是不是因?yàn)榕苎h(huán),內(nèi)存沒有釋放,導(dǎo)致最后溢出,然后code就崩了。

抱著猜想的心態(tài)等了一分鐘,然后。。


WTF 飆到兩千三百多兆的占用了。結(jié)論很明顯了,就是沒有釋放內(nèi)存?。。?br /> 這讓我想起來一個(gè)月前我在集群上并行GPU跑LSTM時(shí),出現(xiàn)了Out of memory,


這是2080ti呀,足足十一個(gè)G的內(nèi)存,雖然當(dāng)時(shí)我就用了一塊顯卡,但也不至于溢出吧。當(dāng)時(shí)沒有想到是這個(gè)問題,現(xiàn)在回想一下十有八九就是沒有釋放內(nèi)存。

至于為什么沒有釋放內(nèi)存,那就要從下面這個(gè)人出生的時(shí)候說起了—>


還記得那是一個(gè)月黑風(fēng)高夜晚,天空電閃雷鳴。。。。。。
hhh,開個(gè)玩笑,這個(gè)人是Python之父,不過我覺得接下來我要說的Python垃圾收集機(jī)制或多或少和他有一定的關(guān)系。

Python垃圾收集機(jī)制

現(xiàn)在的高級(jí)語言如java,c#等,都采用了垃圾收集機(jī)制,而不再是c,c++里用戶自己管理維護(hù)內(nèi)存的方式。自己管理內(nèi)存極其自由,可以任意申請(qǐng)內(nèi)存,但如同一把雙刃劍,為大量內(nèi)存泄露,懸空指針等bug埋下隱患。

對(duì)于一個(gè)字符串、列表、類甚至數(shù)值都是對(duì)象,且定位簡單易用的語言,自然不會(huì)讓用戶去處理如何分配回收內(nèi)存的問題。

python里也同java一樣采用了垃圾收集機(jī)制,不過不一樣的是:

python采用的是引用計(jì)數(shù)機(jī)制為主,標(biāo)記-清除和分代收集兩種機(jī)制為輔的策略。

Python中的內(nèi)存管理過程非常簡單。Python通過保持對(duì)每個(gè)對(duì)象在程序中的引用計(jì)數(shù)來處理其對(duì)象,這意味著每個(gè)對(duì)象存儲(chǔ)在程序中被引用的次數(shù)。此計(jì)數(shù)隨程序運(yùn)行時(shí)更新,并且當(dāng)計(jì)數(shù)為零時(shí),這意味著程序不再可訪問該計(jì)數(shù)。因此,解釋器可以回收和釋放該對(duì)象的內(nèi)存。

class User(object):
  def __del__(self):
    print("No reference left for {}".format(self))
user1 = User()
user2 = user1
user3 = user1

在此示例中,我們制作了一個(gè)類和3個(gè)引用變量指向同一對(duì)象。讓我們將其可視化:


現(xiàn)在,讓變量user1,user2和user3指向None而不是User實(shí)例。

>>> user1 = None
>>> user2 = None
>>> user3 = None
No reference left for <__main__.User object at 0x212bee9d9>

通過以上代碼,引用已更改為:


將最后一個(gè)變量分配user3給后None,該對(duì)象將被垃圾回收,這將調(diào)用該__del__函數(shù)。
從根本上講,每當(dāng)引用對(duì)象時(shí),Python對(duì)象的引用計(jì)數(shù)都會(huì)增加,而在取消引用對(duì)象時(shí),Python的引用計(jì)數(shù)會(huì)減少。如果對(duì)象的引用計(jì)數(shù)為0,則將釋放該對(duì)象的內(nèi)存。您程序的代碼無法禁用Python的引用計(jì)數(shù)。

python跑循環(huán)為什么沒有釋放內(nèi)存

有些人認(rèn)為,引用計(jì)數(shù)是A poor man's garbage collector 。很大一部分原因是它存在一些缺點(diǎn),其中就包括無法檢測到循環(huán)應(yīng)用。
如果在循環(huán)引用中的對(duì)象定義了__del__函數(shù),那么在循環(huán)引用中Python解釋器無法判斷析構(gòu)對(duì)象的順序,因此就不做處理,python gc不能進(jìn)行回收。

解決辦法

在提出解決問題之前,我們要先找到問題。因?yàn)槲疫@個(gè)Code需要畫圖,然后保存到本地。我就想是不是畫的圖導(dǎo)致占用過多內(nèi)存。

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'回歸前結(jié)果',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'回歸后結(jié)果',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() 

這兩句是用來清除內(nèi)存中的圖像和清理掉 axes,并且為了盡可能的減少內(nèi)存占用,把plt.show()都刪除了
然后運(yùn)行Code,打開任務(wù)管理器,測試是否管用。
貌似python占用的內(nèi)存是沒有之前上升的那么快了,不知道是不是心理作用hh。反正效果不明顯,看來得從別的地方下手了。
排除掉這個(gè)可能,那就是回收機(jī)制的鍋了。既然你不主動(dòng),那我就主動(dòng)點(diǎn)咯(猿式陰笑嘿嘿)。

for x in list(locals().keys())[:]:
  del locals()[x]
gc.collect()

for循環(huán)就不要過多解釋了,先說del 語句的用法

del obj_name

del是一個(gè)Python關(guān)鍵字。而且,obj_name 可以是變量,用戶定義的對(duì)象,列表,列表中的項(xiàng),字典等??梢杂脕韯h除用戶定義的對(duì)象;刪除變量,列表和字典;從列表中刪除項(xiàng)目和切片;從字典中刪除鍵等等。具體用法大家可以查找相關(guān)文檔了解。

gc是python的垃圾回收器接口,gc.collect(generation=2)若被調(diào)用時(shí)不包含參數(shù),則啟動(dòng)完全的垃圾回收。可選的參數(shù) generation 可以是一個(gè)整數(shù),指明需要回收哪一代(從 0 到 2 )的垃圾。當(dāng)參數(shù) generation 無效時(shí),會(huì)引發(fā) ValueError 異常。返回發(fā)現(xiàn)的不可達(dá)對(duì)象的數(shù)目。每當(dāng)運(yùn)行完整收集或最高代 (2) 收集時(shí),為多個(gè)內(nèi)置類型所維護(hù)的空閑列表會(huì)被清空。 由于特定類型特別是 float 的實(shí)現(xiàn),在某些空閑列表中并非所有項(xiàng)都會(huì)被釋放。

實(shí)測,有明顯的效果,內(nèi)存占用上升的速度明顯減小了,不過總體還是承上升的趨勢。相比之前,好太多了有沒有。
方法總比困難多,解決內(nèi)存泄漏也還有其他的辦法。我們還可以用objgraph、weakref等工具來分析并解決內(nèi)存泄露、循環(huán)引用問題。實(shí)在不行,還有一個(gè)超級(jí)硬核的辦法,自己動(dòng)手寫一個(gè)騰訊電腦管家小火箭的腳本,時(shí)不時(shí)的讓它上上天(騰訊記得打錢啊啊啊)。哈哈哈

在任何環(huán)境,不管是服務(wù)器,客戶端,內(nèi)存泄露都是非常嚴(yán)重的事情。如果是線上服務(wù)器,那么一定得有監(jiān)控,如果發(fā)現(xiàn)內(nèi)存使用率超過設(shè)置的閾值則立即報(bào)警,盡早發(fā)現(xiàn)些許還有救。

新的問題

不要強(qiáng)行收集垃圾太多次。這是因?yàn)?,即使要釋放?nèi)存,仍然需要花費(fèi)時(shí)間來評(píng)估對(duì)象是否符合垃圾收集條件。我在實(shí)測中的確發(fā)現(xiàn)運(yùn)行速度有些下降。
還有就是他會(huì)把全局的變量都刪了,以至于出現(xiàn)nppd等導(dǎo)入的包都not defined。我的建議是把循環(huán)寫到函數(shù)里,做到只對(duì)某個(gè)函數(shù)起作用。

筆者作為一個(gè)學(xué)生也是剛接觸python不久,如有不對(duì)的地方還請(qǐng)指正,謝謝~

 以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論