粗略分析Python中的内存泄漏


Posted in Python onApril 23, 2015

引子

之前一直盲目的认为 Python 不会存在内存泄露, 但是眼看着上线的项目随着运行时间的增长 而越来越大的内存占用, 我意识到我写的程序在发生内存泄露, 之前 debug 过 logging 模块导致的内存泄露.

目前看来, 还有别的地方引起的内存泄露. 经过一天的奋战, 终于找到了内存泄露的地方, 目前项目 跑了很长时间, 在业务量较小的时候内存还是能回到刚启动的时候的内存占用.
什么情况下不用这么麻烦

如果你的程序只是跑一下就退出大可不必大费周章的去查找是否有内存泄露, 因为 Python 在退出时 会释放它所分配的所有内存, 如果你的程序需要连续跑很长时间那么就要仔细的查找是否 产生了内存泄露.
场景

如何产生的内存泄露呢, 项目是一个 TCP server, 每当有连接过来时都会创建一个连接实例来进行 管理, 每次断开时连接实例还被占用并没有释放. 没有被释放的原因肯定是因为有某个地方对连接 实例的引用没有释放, 所以随着时间的推移, 连接创建分配内存, 连接断开并没有释放掉内存, 所以 就会产生内存泄露.
调试方法

由于不知道具体是哪里引起的内存泄露, 所以要耐心的一点点调试.

由于知道了断开连接时没有释放, 所以我就不停的模拟创建连接然后发送一些包后断开连接, 然后通过下面一行 shell 来观察内存占用情况:

PID=50662;while true; do; ps aux | grep $PID | grep -v grep | awk '{print $5" "$6}' >> t; sleep 1; done

如果在增长了一定的量后保持住就说明已经没有产生泄露.

同时可以在对象该释放的时候查看对象的引用计数, 通过 sys.getrefcount(obj). 如果引用计数变为了 2 则说明该对象在跳出命名空间后就会被正确回收.
产生原因

项目中两种情况导致对象没有被正确回收:

  •     被退出才回收的对象引用
  •     交叉引用

被退出才回收的对象引用

为了追踪连接所以把连接对象同时放在一个列表里, 而这个列表只有在程序退出时才会被回收, 如果不正确处理, 那么分配的对象将也会只在程序退出时才会被回收.

全局变量和类变量都只会在程序退出的时候才会被回收:

_CONNECTIONS = []

# ...
class Connection(object):
 def __init__(self, sock, address)
  pass

def server_loop():
 # ...
 sock, address = server_sock.accept()
 connection = Connection(sock, address)
 _CONNECTIONS.append(connection)
 # ...
 sock.close()

上面把所有建立的连接都放在全局变量 _CONNECTIONS 里, 如果在关闭的时候不从这个列表 里取出(减少引用)则 connection 对象就不会被回收, 则每建立一次连接就会有个连接对象和连接 对象引用的对象不会被回收.

如果把对象放在一个类属性里也是一样的, 因为类对象在程序一开始就分配, 并在程序退出时才被回收.

解决办法就是在退出时从列表(或其他对象)里解除对对象的引用(删除)

_CONNECTIONS = []

# ...
class Connection(object):
 def __init__(self, sock, address)
  pass

def server_loop():
 # ...
 sock, address = server_sock.accept()
 connection = Connection(sock, address)
 _CONNECTIONS.append(connection)
 try:
  # ...
  sock.close()
 finally:
  _CONNECTIONS.remove(connection) # XXX

交叉引用

有时候我们为对象分配一个实例属性时需要将自己本身赋值给实例属性, 作为实例属性的实例属性, 说着很拗口, 看一下代码:

class ConnectionHandler(object):
 def __init__(self, connection):
  self._conn = connection


class Connection(object):
 def __init__(self, sock, address)
  self._conn_handler = ConnectionHandler(self) # XXX

上面的代码就会产生交叉引用, 交叉引用会让解释器困惑, 从而之后只能靠2代和3代回收, 这个过程可能会很慢.

解决这种问题的方法就是使用 弱引用

import weakref

class ConnectionHandler(object):
 def __init__(self, connection):
  self._conn = connection


class Connection(object):
 def __init__(self, sock, address)
  self._conn_handler = ConnectionHandler(weakref.proxy(self)) # XXX
Python 相关文章推荐
Python 连连看连接算法
Nov 22 Python
在Django的模型和公用函数中使用惰性翻译对象
Jul 27 Python
Python处理XML格式数据的方法详解
Mar 21 Python
Python编程实现的简单Web服务器示例
Jun 22 Python
Python的CGIHTTPServer交互实现详解
Feb 08 Python
Django框架模板的使用方法示例
May 25 Python
python字典嵌套字典的情况下找到某个key的value详解
Jul 10 Python
Python 取numpy数组的某几行某几列方法
Oct 24 Python
Python:二维列表下标互换方式(矩阵转置)
Dec 02 Python
如何基于python实现脚本加密
Dec 28 Python
Tensorflow 使用pb文件保存(恢复)模型计算图和参数实例详解
Feb 11 Python
在keras里实现自定义上采样层
Jun 28 Python
使用beaker让Facebook的Bottle框架支持session功能
Apr 23 #Python
用Python编写脚本使IE实现代理上网的教程
Apr 23 #Python
在Python的Bottle框架中使用微信API的示例
Apr 23 #Python
最基础的Python的socket编程入门教程
Apr 23 #Python
利用Python实现简单的相似图片搜索的教程
Apr 23 #Python
以911新闻为例演示Python实现数据可视化的教程
Apr 23 #Python
Python中优化NumPy包使用性能的教程
Apr 23 #Python
You might like
实战mysql导出中文乱码及phpmyadmin导入中文乱码的解决方法
2010/06/11 PHP
深入PHP curl参数的详解
2013/06/17 PHP
php数组去除空值函数分享
2015/02/02 PHP
php使用memcoder将视频转成mp4格式的方法
2015/03/12 PHP
PHP面向对象程序设计类的定义与用法简单示例
2016/12/27 PHP
Smarty缓存机制实例详解【三种缓存方式】
2019/07/20 PHP
Jquery iframe内部出滚动条
2010/02/11 Javascript
javascript cookies操作集合
2010/04/12 Javascript
js实现在文本框光标处添加字符的方法介绍
2012/11/24 Javascript
使用GruntJS链接与压缩多个JavaScript文件过程详解
2013/08/02 Javascript
关于javascript event flow 的一个bug详解
2013/09/17 Javascript
使用jQuery简单实现模拟浏览器搜索功能
2014/12/21 Javascript
JavaScript 里的类数组对象
2015/04/08 Javascript
浅析JavaScript中break、continue和return的区别
2016/11/30 Javascript
利用jquery禁止外层滚动条的滚动
2017/01/05 Javascript
javascript添加前置0(补零)的几种方法
2017/01/05 Javascript
javascript将list转换成树状结构的实例
2017/09/08 Javascript
AngularJS模糊查询功能实现代码(过滤内容下拉菜单排序过滤敏感字符验证判断后添加表格信息)
2017/10/24 Javascript
JS基于递归实现网页版计算器的方法分析
2017/12/20 Javascript
从零开始实现Vue简单的Toast插件
2018/12/03 Javascript
微信小程序iBeacon测距及稳定程序的实现解析
2019/07/31 Javascript
在Python中使用SQLite的简单教程
2015/04/29 Python
浅谈python下含中文字符串正则表达式的编码问题
2018/12/07 Python
十个Python练手的实战项目,学会这些Python就基本没问题了(推荐)
2019/04/26 Python
Python 将代码转换为可执行文件脱离python环境运行(步骤详解)
2021/01/25 Python
CSS3中颜色线性渐变实战
2015/07/18 HTML / CSS
瑜伽服装品牌:露露柠檬(lululemon athletica)
2017/06/04 全球购物
Groupon荷兰官方网站:高达70%的折扣
2019/11/01 全球购物
adidas菲律宾官网:adidas PH
2020/02/07 全球购物
新闻专业个人自我评价
2013/09/21 职场文书
主管会计岗位责任制
2014/02/10 职场文书
2019军训心得体会
2019/06/27 职场文书
教您怎么制定西餐厅运营方案 ?
2019/07/05 职场文书
低版本Druid连接池+MySQL驱动8.0导致线程阻塞、性能受限
2021/07/01 MySQL
Python 详解通过Scrapy框架实现爬取百度新冠疫情数据流程
2021/11/11 Python
Mysql 8.x 创建用户以及授予权限的操作记录
2022/04/18 MySQL