Python跑循环时内存泄露的解决方法
Python跑循环时内存泄露
今天在用Tensorflow跑回归做测试时,仅仅需要循环四千多次(补充说一句,我在个人PC上跑的)。运行以后,我就吃饭去了。等我回来后,Console窗口直接亮红了!!!
importnumpyasnp importpandasaspd importmatplotlib.pyplotasplt frommpl_toolkits.mplot3dimportAxes3D importtensorflowastf importmatplotlib.font_managerasfm myfont=fm.FontProperties(fname='C:/Windows/Fonts/simsun.ttc') sess=tf.Session() forjinrange(0,4096): print('第'+str(j)+'次回归') ......
此处忘了截图,反正就是说Keras出现了什么什么错误。然后我就顺手重启了工程。
接着就瞪着屏幕,为什么跑一半出错了???
这个时候我发现电脑管家的小火箭好像有点‘暴躁',打开任务管理器一看,果然。。。
Python占用内存都快两个G了,但是平时跑没有占用这么多呀。我就猜想是不是因为跑循环,内存没有释放,导致最后溢出,然后code就崩了。
抱着猜想的心态等了一分钟,然后。。
这让我想起来一个月前我在集群上并行GPU跑LSTM时,出现了Outofmemory,
hhh,开个玩笑,这个人是Python之父,不过我觉得接下来我要说的Python垃圾收集机制或多或少和他有一定的关系。
Python垃圾收集机制
现在的高级语言如java,c#等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的方式。自己管理内存极其自由,可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患。
对于一个字符串、列表、类甚至数值都是对象,且定位简单易用的语言,自然不会让用户去处理如何分配回收内存的问题。
python里也同java一样采用了垃圾收集机制,不过不一样的是:
python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。
Python中的内存管理过程非常简单。Python通过保持对每个对象在程序中的引用计数来处理其对象,这意味着每个对象存储在程序中被引用的次数。此计数随程序运行时更新,并且当计数为零时,这意味着程序不再可访问该计数。因此,解释器可以回收和释放该对象的内存。
classUser(object): def__del__(self): print("Noreferenceleftfor{}".format(self)) user1=User() user2=user1 user3=user1
在此示例中,我们制作了一个类和3个引用变量指向同一对象。让我们将其可视化:
现在,让变量user1,user2和user3指向None而不是User实例。
>>>user1=None >>>user2=None >>>user3=None Noreferenceleftfor<__main__.Userobjectat0x212bee9d9>
通过以上代码,引用已更改为:
从根本上讲,每当引用对象时,Python对象的引用计数都会增加,而在取消引用对象时,Python的引用计数会减少。如果对象的引用计数为0,则将释放该对象的内存。您程序的代码无法禁用Python的引用计数。
python跑循环为什么没有释放内存
有些人认为,引用计数是Apoorman'sgarbagecollector。很大一部分原因是它存在一些缺点,其中就包括无法检测到循环应用。
如果在循环引用中的对象定义了__del__函数,那么在循环引用中Python解释器无法判断析构对象的顺序,因此就不做处理,pythongc不能进行回收。
解决办法
在提出解决问题之前,我们要先找到问题。因为我这个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('lossperGeneration') 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。反正效果不明显,看来得从别的地方下手了。
排除掉这个可能,那就是回收机制的锅了。既然你不主动,那我就主动点咯(猿式阴笑嘿嘿)。
forxinlist(locals().keys())[:]: dellocals()[x] gc.collect()
for循环就不要过多解释了,先说del语句的用法
delobj_name
这del是一个Python关键字。而且,obj_name可以是变量,用户定义的对象,列表,列表中的项,字典等。可以用来删除用户定义的对象;删除变量,列表和字典;从列表中删除项目和切片;从字典中删除键等等。具体用法大家可以查找相关文档了解。
gc是python的垃圾回收器接口,gc.collect(generation=2)若被调用时不包含参数,则启动完全的垃圾回收。可选的参数generation可以是一个整数,指明需要回收哪一代(从0到2)的垃圾。当参数generation无效时,会引发ValueError异常。返回发现的不可达对象的数目。每当运行完整收集或最高代(2)收集时,为多个内置类型所维护的空闲列表会被清空。由于特定类型特别是float的实现,在某些空闲列表中并非所有项都会被释放。
实测,有明显的效果,内存占用上升的速度明显减小了,不过总体还是承上升的趋势。相比之前,好太多了有没有。
方法总比困难多,解决内存泄漏也还有其他的办法。我们还可以用objgraph、weakref等工具来分析并解决内存泄露、循环引用问题。实在不行,还有一个超级硬核的办法,自己动手写一个腾讯电脑管家小火箭的脚本,时不时的让它上上天(腾讯记得打钱啊啊啊)。哈哈哈
在任何环境,不管是服务器,客户端,内存泄露都是非常严重的事情。如果是线上服务器,那么一定得有监控,如果发现内存使用率超过设置的阈值则立即报警,尽早发现些许还有救。
新的问题
不要强行收集垃圾太多次。这是因为,即使要释放内存,仍然需要花费时间来评估对象是否符合垃圾收集条件。我在实测中的确发现运行速度有些下降。
还有就是他会把全局的变量都删了,以至于出现np和pd等导入的包都notdefined。我的建议是把循环写到函数里,做到只对某个函数起作用。
笔者作为一个学生也是刚接触python不久,如有不对的地方还请指正,谢谢~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。