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中定义结构体的方法
Mar 04 Python
Python不规范的日期字符串处理类
Jun 10 Python
简单分析Python中用fork()函数生成的子进程
May 04 Python
简单介绍Python的Django框架的dj-scaffold项目
May 30 Python
Python中的urllib模块使用详解
Jul 07 Python
使用Python解析JSON数据的基本方法
Oct 15 Python
python实现识别相似图片小结
Feb 22 Python
Python过滤列表用法实例分析
Apr 29 Python
Python列表list内建函数用法实例分析【insert、remove、index、pop等】
Jul 24 Python
Python中的单行、多行、中文注释方法
Jul 19 Python
Empty test suite.(PyCharm程序运行错误的解决方法)
Nov 30 Python
python实现批量命名照片
Jun 18 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
php面向对象全攻略 (十) final static const关键字的使用
2009/09/30 PHP
深入PHP许愿墙模块功能分析
2013/06/25 PHP
常见PHP数据库解决方案分析介绍
2015/09/24 PHP
PHP中STDCLASS用法实例分析
2016/11/11 PHP
Javascript操作select方法大全[新增、修改、删除、选中、清空、判断存在等]
2008/09/26 Javascript
js multiple全选与取消全选实现代码
2012/12/04 Javascript
jQuery中操控hidden、disable等无值属性的方法
2014/01/06 Javascript
jquery插件开发之实现md5插件
2014/03/17 Javascript
如何调试异步加载页面里包含的js文件
2014/10/30 Javascript
zTree插件下拉树使用入门教程
2016/04/11 Javascript
如何使用Vuex+Vue.js构建单页应用
2016/10/27 Javascript
详解JS中定时器setInterval和setTImeout的this指向问题
2017/01/06 Javascript
JS实现css hover操作的方法示例
2017/04/07 Javascript
利用n工具轻松管理Node.js的版本
2017/04/21 Javascript
JavaScript基础之流程控制语句的用法
2017/08/31 Javascript
JS中call和apply函数用法实例分析
2018/06/20 Javascript
详解Vue微信授权登录前后端分离较为优雅的解决方案
2018/06/29 Javascript
使用taro开发微信小程序遇到的坑总结
2019/04/08 Javascript
js实现随机抽奖
2020/03/19 Javascript
基于PHP pthreads实现多线程代码实例
2020/06/24 Javascript
Python常用模块用法分析
2014/09/08 Python
Python简明入门教程
2015/08/04 Python
实例探究Python以并发方式编写高性能端口扫描器的方法
2016/06/14 Python
python OpenCV学习笔记之绘制直方图的方法
2018/02/08 Python
对Django 转发和重定向的实例详解
2019/08/06 Python
Pytorch中膨胀卷积的用法详解
2020/01/07 Python
NumPy排序的实现
2020/01/21 Python
用python 绘制茎叶图和复合饼图
2021/02/26 Python
美国顶级奢侈茶:Mighty Leaf Tea(美泰茶)
2016/11/26 全球购物
Rhone官方网站:男士运动服装、健身服装和高级运动服
2019/05/01 全球购物
List, Set, Map是否继承自Collection接口?
2016/05/16 面试题
软件缺陷的分类都有哪些
2014/08/22 面试题
Python里面如何拷贝一个对象
2014/02/17 面试题
团日活动总结书格式
2014/05/08 职场文书
医院反腐倡廉演讲稿
2014/09/16 职场文书
MySQL 分区表中分区键为什么必须是主键的一部分
2022/03/17 MySQL