粗略分析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实现协同过滤的教程
Apr 08 Python
使用Python脚本操作MongoDB的教程
Apr 16 Python
Python增量循环删除MySQL表数据的方法
Sep 23 Python
Python内置函数—vars的具体使用方法
Dec 04 Python
Python 内置函数进制转换的用法(十进制转二进制、八进制、十六进制)
Apr 30 Python
用Python一键搭建Http服务器的方法
Jun 01 Python
python批量识别图片指定区域文字内容
Apr 30 Python
Python拆分大型CSV文件代码实例
Oct 07 Python
python如何使用socketserver模块实现并发聊天
Dec 14 Python
matlab灰度图像调整及imadjust函数的用法详解
Feb 27 Python
如何Tkinter模块编写Python图形界面
Oct 14 Python
python 实现德洛内三角剖分的操作
Apr 22 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
GD输出汉字的函数的分析
2006/10/09 PHP
ajax+php打造进度条代码[readyState各状态说明]
2010/04/12 PHP
php学习笔记 php中面向对象三大特性之一[封装性]的应用
2011/06/13 PHP
php防止sql注入代码实例
2013/12/18 PHP
一个严格的PHP Session会话超时时间设置方法
2014/06/10 PHP
ThinkPHP模板Switch标签用法示例
2014/06/30 PHP
PHP生成指定长度随机数最简洁的方法
2014/07/14 PHP
php给每个段落添加空格的方法
2015/03/20 PHP
php计算title标题相似比的方法
2015/07/29 PHP
php 解决扫描二维码下载跳转问题
2017/01/13 PHP
php表单文件iframe异步上传实例讲解
2017/07/26 PHP
几个常用的JavaScript字符串处理函数 - split()、join()、substring()和indexOf()
2009/06/02 Javascript
js常用代码段收集
2011/10/28 Javascript
js给onclick事件赋值,动态传参数实例解说
2013/03/28 Javascript
jQuery函数的等价原生函数代码示例
2013/05/27 Javascript
用js+iframe形成页面的一种遮罩效果的具体实现
2013/12/31 Javascript
通过伪协议解决父页面与iframe页面通信的问题
2015/04/05 Javascript
node模块机制与异步处理详解
2016/03/13 Javascript
js实现弹窗暗层效果
2017/01/16 Javascript
vue实现动态显示与隐藏底部导航的方法分析
2019/02/11 Javascript
详解vue的数据劫持以及操作数组的坑
2019/04/18 Javascript
vue递归组件实战之简单树形控件实例代码
2019/08/27 Javascript
Python生成随机密码的方法
2017/06/16 Python
windows下Virtualenvwrapper安装教程
2017/12/13 Python
在python中利用最小二乘拟合二次抛物线函数的方法
2018/12/29 Python
Python 从subprocess运行的子进程中实时获取输出的例子
2019/08/14 Python
Python3.5 win10环境下导入kera/tensorflow报错的解决方法
2019/12/19 Python
Veronica Beard官网:在酷、经典和别致之间找到了平衡
2018/01/11 全球购物
美国牛仔品牌:True Religion
2018/11/16 全球购物
工地安全检查制度
2014/02/04 职场文书
分公司总经理岗位职责
2014/07/30 职场文书
电气工程师岗位职责
2015/02/12 职场文书
幼儿园家长工作总结2015
2015/04/25 职场文书
Python语言内置数据类型
2022/02/24 Python
古见同学有交流障碍症 第二季宣传CM公开播出
2022/04/11 日漫
python实现简单的三子棋游戏
2022/04/28 Python