当前位置: 首页 > news >正文

当退出python时是否释放全部内存_Python跑循环时内存泄露的解决方法

Python跑循环时内存泄露

今天在用Tensorflow跑回归做测试时,仅仅需要循环四千多次 (补充说一句,我在个人PC上跑的)。运行以后,我就吃饭去了。等我回来后,Console窗口直接亮红了!!!

?

1

2

3

4

5

6

7

8

9

10

11

12

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

from mpl_toolkits.mplot3dimport Axes3D

import tensorflow as tf

import matplotlib.font_manager as fm

myfont= fm.FontProperties(fname='C:/Windows/Fonts/simsun.ttc')

sess= tf.Session()

for jin range(0,4096):

print('第' + str(j)+ '次回归')

......

此处忘了截图,反正就是说Keras出现了什么什么错误。然后我就顺手重启了工程。

接着就瞪着屏幕,为什么跑一半出错了???

1-200426101047.jpg

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

1-200426101047.png

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

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

1-200426101048.png

WTF 飙到两千三百多兆的占用了。结论很明显了,就是没有释放内存!!!

这让我想起来一个月前我在集群上并行GPU跑LSTM时,出现了Out of memory,

1-200426101049.png

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

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

1-200426101049.jpg

还记得那是一个月黑风高夜晚,天空电闪雷鸣。。。。。。

hhh,开个玩笑,这个人是Python之父,不过我觉得接下来我要说的Python垃圾收集机制或多或少和他有一定的关系。

Python垃圾收集机制

现在的高级语言如java,c#等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的方式。自己管理内存极其自由,可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患。

对于一个字符串、列表、类甚至数值都是对象,且定位简单易用的语言,自然不会让用户去处理如何分配回收内存的问题。

python里也同java一样采用了垃圾收集机制,不过不一样的是:

python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。

Python中的内存管理过程非常简单。Python通过保持对每个对象在程序中的引用计数来处理其对象,这意味着每个对象存储在程序中被引用的次数。此计数随程序运行时更新,并且当计数为零时,这意味着程序不再可访问该计数。因此,解释器可以回收和释放该对象的内存。

?

1

2

3

4

5

6

class User(object):

def __del__(self):

print("No reference left for {}".format(self))

user1= User()

user2= user1

user3= user1

在此示例中,我们制作了一个类和3个引用变量指向同一对象。让我们将其可视化:

1-200426101049-50.png

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

?

1

2

3

4

>>> user1= None

>>> user2= None

>>> user3= None

No reference leftfor <__main__.Userobject at0x212bee9d9>

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

1-200426101050.png

将最后一个变量分配user3给后None,该对象将被垃圾回收,这将调用该__del__函数。

从根本上讲,每当引用对象时,Python对象的引用计数都会增加,而在取消引用对象时,Python的引用计数会减少。如果对象的引用计数为0,则将释放该对象的内存。您程序的代码无法禁用Python的引用计数。

python跑循环为什么没有释放内存

有些人认为,引用计数是A poor man's garbage collector 。很大一部分原因是它存在一些缺点,其中就包括无法检测到循环应用。

如果在循环引用中的对象定义了__del__函数,那么在循环引用中Python解释器无法判断析构对象的顺序,因此就不做处理,python gc不能进行回收。

解决办法

在提出解决问题之前,我们要先找到问题。因为我这个Code需要画图,然后保存到本地。我就想是不是画的图导致占用过多内存。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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()

有想法你就要去做是吧,然后我就加了两行代码。

?

1

2

plt.close('all')

plt.clf()

这两句是用来清除内存中的图像和清理掉 axes,并且为了尽可能的减少内存占用,把plt.show()都删除了

然后运行Code,打开任务管理器,测试是否管用。

貌似python占用的内存是没有之前上升的那么快了,不知道是不是心理作用hh。反正效果不明显,看来得从别的地方下手了。

排除掉这个可能,那就是回收机制的锅了。既然你不主动,那我就主动点咯(猿式阴笑嘿嘿)。

?

1

2

3

for xin list(locals().keys())[:]:

del locals()[x]

gc.collect()

for循环就不要过多解释了,先说del 语句的用法

?

1

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不久,如有不对的地方还请指正,谢谢~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/L_W_T_/article/details/103916415

相关文章:

  • 为什么parsefloat加出来还是字符串_为什么股票资金流出了1000万,却还是封住了涨停板?知道套路的我眼泪都掉出来了...
  • java web项目github_3月份Github上“最热门”的十大开源项目,竟被Java承包了!
  • python协程实现一万并发_求你别再花大价钱学 Python 之协程高并发爬虫
  • 什么是python编程例子_什么是Python编程的逻辑判断?
  • python读取odb_python - 从.odb文件中提取von mises应力值 - 堆栈内存溢出
  • sqlserver union执行后变慢_Zabbix如何监控SQL Server服务状态
  • 事件总线第一次点击_干货Spring Cloud Bus 消息总线介绍
  • cgi web 调用多次启动_漏洞预警|Web系统管理工具Webmin远程命令执行高危漏洞分析(CVE201915107)...
  • flashplayer离线安装包 64位_离线安装NET Framework 3.5的一般方法
  • node 获取表单数据 为空_Python数据结构(二)单向循环链表
  • javascript案例大全_JavaScript 类型 — 重学 JavaScript
  • ajax如何提交多表单的值_Ajax完整详细教程(一)
  • 五金手册钢材理论重量计算小程序_33个造价实用小工具,工作效率提高80%,造价人的终极神器,手慢无...
  • 什么是常驻内存式的开发模式?_为什么 APP 纷纷开发“暗黑模式”?优酷最佳实践总结...
  • mysql blob类型_MySQL数据类型 字符串数据类型(1)
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • Android系统模拟器绘制实现概述
  • CentOS学习笔记 - 12. Nginx搭建Centos7.5远程repo
  • Invalidate和postInvalidate的区别
  • Js基础——数据类型之Null和Undefined
  • Laravel核心解读--Facades
  • nodejs:开发并发布一个nodejs包
  • python学习笔记-类对象的信息
  • 从重复到重用
  • 订阅Forge Viewer所有的事件
  • 对象引论
  • 警报:线上事故之CountDownLatch的威力
  • 模型微调
  • 使用 @font-face
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 使用Gradle第一次构建Java程序
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 因为阿里,他们成了“杭漂”
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (二开)Flink 修改源码拓展 SQL 语法
  • (六)c52学习之旅-独立按键
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (转)mysql使用Navicat 导出和导入数据库
  • ******之网络***——物理***
  • .gitignore文件---让git自动忽略指定文件
  • .Net 代码性能 - (1)
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • .net6Api后台+uniapp导出Excel
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • .net生成的类,跨工程调用显示注释
  • .NET下ASPX编程的几个小问题
  • .NET中 MVC 工厂模式浅析
  • [ SNOI 2013 ] Quare
  • [2016.7 day.5] T2
  • [ai笔记9] openAI Sora技术文档引用文献汇总
  • [Android] Upload package to device fails #2720
  • [BJDCTF2020]The mystery of ip1