Python内存泄漏和内存溢出的解决方案


Posted in Python onSeptember 26, 2020

一、内存泄漏

像Java程序一样,虽然Python本身也有垃圾回收的功能,但是同样也会产生内存泄漏的问题。
对于一个用 python 实现的,长期运行的后台服务进程来说,如果内存持续增长,那么很可能是有了“内存泄露”。

1、内存泄露的原因

对于 python 这种支持垃圾回收的语言来说,怎么还会有内存泄露? 概括来说,有以下三种原因:

  • 所用到的用 C 语言开发的底层模块中出现了内存泄露。
  • 代码中用到了全局的 list、 dict 或其它容器,不停的往这些容器中插入对象,而忘记了在使用完之后进行删除回收
  • 代码中有“引用循环”,并且被循环引用的对象定义了__del__方法,就会发生内存泄露。

为什么循环引用的对象定义了__del__方法后collect就不起作用了呢?

gc模块最常使用的方法就是gc.collect()方法,使用collect方法对循环引用的对象进行垃圾回收。
如果我们在类中重载了__del__方法。__del__方法定义了在用del语句删除对象时除了释放内存空间以外的操作。
一般而言,在使用了del语句的时候解释器首先会看要删除对象的引用计数,如果为0,那么就释放内存并执行del方法。
在这里,首先del语句出现时本身引用计数就不为0(因为有循环引用的存在),所以解释器不释放内存;
再者,执行collect方法时应该会清除循环引用所产生的无效引用计数从而达到del的目的,对于这两个循环引用对象而言,
python无法判断调用它们的del方法时会不会要用到对方那个对象,比如在进行b.del()时可能会用到b._a也就是a,如果在那之前a已经被释放,那么就彻底GG了。
为了避免这种情况,collect方法默认不对重载了del方法的循环引用对象进行回收,而它们俩的状态也会从unreachable转变为uncollectable。由于是uncollectable的,自然就不会被collect处理,所以就进入了garbage列表。

2、内存泄露的诊断思路

无论是哪种方式的内存泄露,最终表现的形式都是某些 python 对象在不停的增长;因此,首先是要找到这些异常的对象。

3、诊断步骤

用到的工具: gc 模块和 objgraph 模块

gc模块 是Python的垃圾收集器模块,gc使用标记清除算法回收垃圾

objgraph 是一个用于诊断内存问题的工具

  • 1、 在服务程序的循环逻辑中,选择出一个诊断点
  • 2、 在诊断点,插入如下诊断语句  
import gc
import objgraph


### 强制进行垃圾回收 
gc.collect() 

### 打印出对象数目最多的 50 个类型信息 
objgraph.show_most_common_types(limit=50)

4、检查统计信息,找到异常对象

运行加入诊断语句的服务程序,并将打印到屏幕上的统计信息重定向到日志中。运行一段时间后,就可以来分析日志,看看哪些对象在不停的增长。

比如,排查结果可能是:
一个多线程程序,多个线程作为生产者,一个线程作为消费者,通过将一个 tuple 对象送入异步队列进行通信。
由于消费者的处理速度跟不上生产者的速度,又没有进行同步, 导致异步队列中的对象越来越多。

二、内存溢出

1、内存溢出原因

  1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据
  2. 集合类中有对对象的引用,使用完后未清空,产生了堆积,使得JVM不能回收
  3. 代码中存在死循环或循环产生过多重复的对象实体
  4. 使用的第三方软件中的BUG
  5. 启动参数内存值设定的过小

2、内存溢出的解决方案

第一步,修改JVM启动参数,直接增加内存(-Xms,-Xmx参数一定不要忘记加)

第二步,检查错误日志,查看“OutOfMemory”错误前是否有其 它异常或错误

第三步,对代码进行走查和分析,找出可能发生内存溢出的位置

重点排查以下几点:

  1. 检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
  2. 检查代码中是否有死循环或递归调用。
  3. 检查是否有大循环重复产生新对象实体。
  4. 检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。

第四步,使用内存查看工具动态查看内存使用情况

三、内存泄漏和内存溢出的区别

内存溢出是指向JVM申请内存空间时没有足够的可用内存了,就会抛出OOM即内存溢出。

内存泄漏是指,向JVM申请了一块内存空间,使用完后没有释放,由于没有释放,这块内存区域其他类加载的时候无法申请,

同时当前类又没有这块内存空间的内存地址了也无法使用,相当于丢了一块内存,这就是内存泄漏。

值得注意的是内存泄漏最终会导致内存溢出,很好理解,内存丢了很多最后当然内存不够用了。

以上就是Python内存泄漏和内存溢出的解决方案的详细内容,更多关于Python内存泄漏和内存溢出的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
python命令行参数sys.argv使用示例
Jan 28 Python
python正则表达式及使用正则表达式的例子
Jan 22 Python
基于python requests库中的代理实例讲解
May 07 Python
django中模板的html自动转意方法
May 27 Python
mac下给python3安装requests库和scrapy库的实例
Jun 13 Python
python针对不定分隔符切割提取字符串的方法
Oct 26 Python
python下载微信公众号相关文章
Feb 26 Python
Flask框架学习笔记之路由和反向路由详解【图文与实例】
Aug 12 Python
Python 列表中的修改、添加和删除元素的实现
Jun 11 Python
Pandas把dataframe或series转换成list的方法
Jun 14 Python
Python自动化之批量处理工作簿和工作表
Jun 03 Python
Python内置包对JSON文件数据进行编码和解码
Apr 12 Python
python 两种方法修改文件的创建时间、修改时间、访问时间
Sep 26 #Python
如何使用Python调整图像大小
Sep 26 #Python
小白教你PyCharm从下载到安装再到科学使用PyCharm2020最新激活码
Sep 25 #Python
PyCharm2020最新激活码+激活码补丁(亲测最新版PyCharm2020.2激活成功)
Nov 25 #Python
详解Python中第三方库Faker
Sep 25 #Python
python对批量WAV音频进行等长分割的方法实现
Sep 25 #Python
python连接mysql数据库并读取数据的实现
Sep 25 #Python
You might like
php session 错误
2009/05/21 PHP
php function用法如何递归及return和echo区别
2014/03/07 PHP
CI框架表单验证实例详解
2016/11/21 PHP
thinkPHP5框架auth权限控制类与用法示例
2018/06/12 PHP
PHP二维索引数组的遍历实例分析【2种方式】
2019/06/24 PHP
laravel orm 关联条件查询代码
2019/10/21 PHP
基于php+MySql实现学生信息管理系统实例
2020/08/04 PHP
清除网页历史记录,屏蔽后退按钮!
2008/12/22 Javascript
动态加载js和css(外部文件)
2013/04/17 Javascript
jquery实现的网页自动播放声音
2014/04/30 Javascript
jQuery制作仿Mac Lion OS滚动条效果
2015/02/10 Javascript
jQuery form插件的使用之处理server返回的JSON, XML,HTML数据
2016/01/26 Javascript
盘点javascript 正则表达式中 中括号的【坑】
2016/03/16 Javascript
让浏览器崩溃的12行JS代码(DoS攻击分析及防御)
2016/10/10 Javascript
nodejs基础知识
2017/02/03 NodeJs
jQuery插件HighCharts绘制的基本折线图效果示例【附demo源码下载】
2017/03/07 Javascript
jstree单选功能的实现方法
2017/06/07 Javascript
iview实现select tree树形下拉框的示例代码
2018/12/21 Javascript
微信小程序 WXML节点信息查询详解
2019/07/29 Javascript
vue不操作dom实现图片轮播的示例代码
2019/12/18 Javascript
[11:01]2014DOTA2西雅图邀请赛 冷冷带你探秘威斯汀
2014/07/08 DOTA
[01:10]DOTA2次级职业联赛 - EP战队宣传片
2014/12/01 DOTA
[48:54]VGJ.T vs infamous Supermajor小组赛D组败者组第一轮 BO3 第二场 6.3
2018/06/04 DOTA
python遍历一个目录,输出所有的文件名的实例
2018/04/23 Python
Python删除n行后的其他行方法
2019/01/28 Python
解决Django删除migrations文件夹中的文件后出现的异常问题
2019/08/31 Python
香港万宁官方海外旗舰店:香港健与美连锁店
2018/09/27 全球购物
Hudson Jeans官网:高级精制牛仔裤
2018/11/28 全球购物
Linux如何命名文件--使用文件名时应注意
2012/01/22 面试题
JavaScript获取当前url根目录(路径)
2014/02/19 面试题
关于幼儿的自我评价
2013/12/18 职场文书
学校后勤人员职责
2013/12/27 职场文书
省级优秀毕业生主要事迹
2014/05/29 职场文书
写给妈妈的感谢信
2015/01/22 职场文书
Html5通过数据流方式播放视频的实现
2021/04/27 HTML / CSS
Java实现超大Excel文件解析(XSSF,SXSSF,easyExcel)
2022/07/15 Java/Android