粗略分析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连接字符串的方法小结
Jul 13 Python
django1.11.1 models 数据库同步方法
May 30 Python
使用Python更换外网IP的方法
Jul 09 Python
Python实现简单的用户交互方法详解
Sep 25 Python
Pandas分组与排序的实现
Jul 23 Python
django创建超级用户时指定添加其它字段方式
May 14 Python
浅谈pytorch 模型 .pt, .pth, .pkl的区别及模型保存方式
May 25 Python
python函数map()和partial()的知识点总结
May 26 Python
python 爬取小说并下载的示例
Dec 07 Python
对pytorch中x = x.view(x.size(0), -1) 的理解说明
Mar 03 Python
Python如何使用logging为Flask增加logid
Mar 30 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
全国FM电台频率大全 - 22 重庆市
2020/03/11 无线电
php下使用以下代码连接并测试
2008/04/09 PHP
PHP文件上传问题汇总(文件大小检测、大文件上传处理)
2015/12/24 PHP
PHP cookie与session会话基本用法实例分析
2019/11/18 PHP
ThinkPHP类似AOP思想的参数验证的实现方法
2019/12/18 PHP
js 目录列举函数
2008/11/06 Javascript
利用WebBrowser彻底解决Web打印问题(包括后台打印)
2009/06/22 Javascript
JS Map 和 List 的简单实现代码
2013/07/08 Javascript
高效的获取当前元素是父元素的第几个子元素
2013/10/15 Javascript
jquery渐隐渐显的图片幻灯闪烁切换实现方法
2015/02/26 Javascript
jQuery检测滚动条是否到达底部
2015/12/15 Javascript
JavaScript 2048 游戏实例代码(简单易懂)
2016/03/25 Javascript
jQuery Mobile框架中的表单组件基础使用教程
2016/05/17 Javascript
JS读取XML文件数据并以table形式显示数据的方法(兼容IE与火狐)
2016/06/02 Javascript
AngularJS 中的事件详解
2016/07/28 Javascript
ajax+node+request爬取网络图片的实例(宅男福利)
2017/08/28 Javascript
vue axios 表单提交上传图片的实例
2018/03/16 Javascript
解决v-for中使用v-if或者v-bind:class失效的问题
2018/09/25 Javascript
Vue分页插件的前后端配置与使用
2019/10/09 Javascript
Python中is与==判断的区别
2017/03/28 Python
基于Django用户认证系统详解
2018/02/21 Python
python实现快速排序的示例(二分法思想)
2018/03/12 Python
Python3单行定义多个变量或赋值方法
2018/07/12 Python
解决PyCharm IDE环境下,执行unittest不生成测试报告的问题
2020/09/03 Python
Python-split()函数实例用法讲解
2020/12/18 Python
FC-Moto美国:欧洲最大的摩托车服装和头盔商店之一
2019/08/24 全球购物
李维斯牛仔裤英国官方网站:Levi’s英国
2019/10/10 全球购物
视图的作用
2014/12/19 面试题
Prototype如何更新局部页面
2013/03/03 面试题
CSS实现fullpage.js全屏滚动效果的示例代码
2021/03/24 HTML / CSS
年度考核自我鉴定
2013/11/09 职场文书
大学生个人事迹材料
2014/01/21 职场文书
创建无烟单位实施方案
2014/03/29 职场文书
法定代表人身份证明书(含说明)
2014/10/02 职场文书
2014年电话客服工作总结
2014/12/09 职场文书
聊聊Lombok中的@Builder注解使用教程
2021/11/17 Java/Android