Python跑循环时内存泄露的解决方法


Posted in Python onJanuary 13, 2020

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跑循环时内存泄露的解决方法

这个时候我发现电脑管家的小火箭好像有点‘暴躁',打开任务管理器一看,果然。。。

Python跑循环时内存泄露的解决方法

Python占用内存都快两个G了,但是平时跑没有占用这么多呀。我就猜想是不是因为跑循环,内存没有释放,导致最后溢出,然后code就崩了。

抱着猜想的心态等了一分钟,然后。。

Python跑循环时内存泄露的解决方法

WTF 飙到两千三百多兆的占用了。结论很明显了,就是没有释放内存!!!
这让我想起来一个月前我在集群上并行GPU跑LSTM时,出现了Out of memory,

Python跑循环时内存泄露的解决方法

这是2080ti呀,足足十一个G的内存,虽然当时我就用了一块显卡,但也不至于溢出吧。当时没有想到是这个问题,现在回想一下十有八九就是没有释放内存。

至于为什么没有释放内存,那就要从下面这个人出生的时候说起了—>

Python跑循环时内存泄露的解决方法

还记得那是一个月黑风高夜晚,天空电闪雷鸣。。。。。。
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个引用变量指向同一对象。让我们将其可视化:

Python跑循环时内存泄露的解决方法

现在,让变量user1,user2和user3指向None而不是User实例。

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

通过以上代码,引用已更改为:

Python跑循环时内存泄露的解决方法

将最后一个变量分配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等工具来分析并解决内存泄露、循环引用问题。实在不行,还有一个超级硬核的办法,自己动手写一个腾讯电脑管家小火箭的脚本,时不时的让它上上天(腾讯记得打钱啊啊啊)。哈哈哈

在任何环境,不管是服务器,客户端,内存泄露都是非常严重的事情。如果是线上服务器,那么一定得有监控,如果发现内存使用率超过设置的阈值则立即报警,尽早发现些许还有救。

新的问题

不要强行收集垃圾太多次。这是因为,即使要释放内存,仍然需要花费时间来评估对象是否符合垃圾收集条件。我在实测中的确发现运行速度有些下降。
还有就是他会把全局的变量都删了,以至于出现nppd等导入的包都not defined。我的建议是把循环写到函数里,做到只对某个函数起作用。

笔者作为一个学生也是刚接触python不久,如有不对的地方还请指正,谢谢~

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python操作Excel之xlsx文件
Mar 24 Python
python中urllib.unquote乱码的原因与解决方法
Apr 24 Python
Python去除、替换字符串空格的处理方法
Apr 01 Python
用于业余项目的8个优秀Python库
Sep 21 Python
python动态进度条的实现代码
Jul 03 Python
python实现车牌识别的示例代码
Aug 05 Python
Python获取时间范围内日期列表和周列表的函数
Aug 05 Python
Python3.5 win10环境下导入kera/tensorflow报错的解决方法
Dec 19 Python
Python tkinter 下拉日历控件代码
Mar 04 Python
Python实现一个论文下载器的过程
Jan 18 Python
python引入其他文件夹下的py文件具体方法
May 23 Python
利用Python+OpenCV三步去除水印
May 28 Python
PyTorch使用cpu加载模型运算方式
Jan 13 #Python
Python如何读取文件中图片格式
Jan 13 #Python
详解python破解zip文件密码的方法
Jan 13 #Python
PyTorch 随机数生成占用 CPU 过高的解决方法
Jan 13 #Python
python批量处理txt文件的实例代码
Jan 13 #Python
Python hashlib常见摘要算法详解
Jan 13 #Python
Pytorch释放显存占用方式
Jan 13 #Python
You might like
咖啡产品发展的三大浪潮
2021/03/04 咖啡文化
PHP 高手之路(二)
2006/10/09 PHP
PHP插入排序实现代码
2013/04/04 PHP
Zend的MVC机制使用分析(二)
2013/05/02 PHP
鼠标拖动实现DIV排序示例代码
2013/10/14 Javascript
javascript中parentNode,childNodes,children的应用详解
2013/12/17 Javascript
红米手机抢购的js代码
2014/03/10 Javascript
JavaScript将取代AppleScript?
2014/09/18 Javascript
Node.js 的异步 IO 性能探讨
2014/10/08 Javascript
node中socket.io的事件使用详解
2014/12/15 Javascript
jQuery基于ajax操作json数据简单示例
2017/01/05 Javascript
jQuery实现Table表格隔行变色及高亮显示当前选择行效果示例
2017/02/14 Javascript
Vuejs 2.0 子组件访问/调用父组件的方法(示例代码)
2018/02/08 Javascript
Nodejs模块的调用操作实例分析
2018/12/25 NodeJs
vue-cli设置css不生效的解决方法
2020/02/07 Javascript
[09:59]DOTA2-DPC中国联赛2月7日Recap集锦
2021/03/11 DOTA
Python新手在作用域方面经常容易碰到的问题
2015/04/03 Python
Python使用smtplib模块发送电子邮件的流程详解
2016/06/27 Python
Python基础之getpass模块详细介绍
2017/08/10 Python
python自定义函数实现一个数的三次方计算方法
2019/01/20 Python
python日期相关操作实例小结
2019/06/24 Python
python 实现12bit灰度图像映射到8bit显示的方法
2019/07/08 Python
python dataframe NaN处理方式
2019/12/26 Python
Keras—embedding嵌入层的用法详解
2020/06/10 Python
用Python爬取LOL所有的英雄信息以及英雄皮肤的示例代码
2020/07/13 Python
欧洲最大的滑雪假期供应商之一:Sunweb Holidays
2018/01/06 全球购物
表达自我的市场:Society6
2018/08/01 全球购物
护士岗位职责
2014/02/16 职场文书
婚前协议书范本
2014/04/15 职场文书
文秘自荐信
2014/06/28 职场文书
老龄工作先进事迹
2014/08/15 职场文书
实习生工作证明范本
2014/09/14 职场文书
2014年幼儿园个人工作总结
2014/11/10 职场文书
签订劳动合同通知书
2015/04/16 职场文书
Golang 编译成DLL文件的操作
2021/05/06 Golang
Python实战之实现简易的学生选课系统
2021/05/25 Python