解决Python中由于logging模块误用导致的内存泄露


Posted in Python onApril 23, 2015

首先介绍下怎么发现的吧, 线上的项目日志是通过 logging 模块打到 syslog 里, 跑了一段时间后发现 syslog 的 UDP 连接超过了 8W, 没错是 8 W. 主要是 logging 模块用的不对

我们之前有这么一个需求, 就是针对每一个连接日志输出当前连接的信息, 所以每一个 连接就创建了一个日志实例, 并分配一个 Formatter, 创建日志实例为了区分其他连接 所以我就简单粗暴的用了当前对象的 id 来作为日志名称:

import logging


class Connection(object):
  def __init__(self):
    self._logger_name = "Connection.{}".format(id(self))
    self.logger = logging.getLogger(self._logger_name)

当然测试环境是开 DEBUG, 开 DEBUG 就不会往 syslog 里打, 所以不会出现 UDP 连接数 过多, 也就不会知道有内存泄露的, 我们来看看这样为什么会导致内存泄露, 首先看看 getLogger 的代码:

def getLogger(name=None):
  """
  Return a logger with the specified name, creating it if necessary.

  If no name is specified, return the root logger.
  """
  if name:
    return Logger.manager.getLogger(name)
  else:
    return root

主要调用了 Logger.manager.getLogger, 这个函数有下面一段代码片段

if name in self.loggerDict:
        rv = self.loggerDict[name]
        if isinstance(rv, PlaceHolder):
          ph = rv
          rv = (self.loggerClass or _loggerClass)(name)
          rv.manager = self
          self.loggerDict[name] = rv
          self._fixupChildren(ph, rv)
          self._fixupParents(rv)
      else:
        rv = (self.loggerClass or _loggerClass)(name)
        rv.manager = self
        self.loggerDict[name] = rv
        self._fixupParents(rv)

logging 模块为了保证同一个名称引用同一个日志实例,所以就把所有的日志实例全部存 在了一个 loggerDict 的字典里, 所以除非程序退出, 创建的日志实例引用是不会释放的, 所以日志实例里的 handlers 也不会释放. 之前我又用的对象的 id 来作为日志名称 的一部分, 所以 SyslogHandler 创建的 UDP 连接就一直被占用导致了过多的 UDP 连接.

为了解决这个问题我在连接关闭的时候加入了如下代码:

logging.Logger.manager.loggerDict.pop(self._logger_name)
self.logger.manager = None
self.logger.handlers = []

按说只加上上面第一行的代码就应该释放了, 但是没有, 所以又有了第三行代码, SyslogHandler 才最终释放, 这个问题暂时还不知道为什么, 还需要再查查.

2015-03-30 更新 如果日志名称是以 . 分隔, logging 模块则会将最后一部分作为日志名, 并往上去寻找 父 Logger, 如果找不到则创建 PlaceHolder 对象作为父, 并引用 Logger.

比如创建的 Logger 名称为 a.b.c, 那么实际的名称则为 c, 并将 b 作为 c 的父, a 作为 b 的 父, 如果没有该名称的 Logger 则创建 PlaceHolder 对象作为代替, PlaceHolder 会创建对当前 Logger 的引用. 所以需要被回收的日志对象名称里不应包含 .

Python 相关文章推荐
phpsir 开发 一个检测百度关键字网站排名的python 程序
Sep 17 Python
python模拟登陆阿里妈妈生成商品推广链接
Apr 03 Python
python的re模块应用实例
Sep 26 Python
浅谈numpy库的常用基本操作方法
Jan 09 Python
Python求出0~100以内的所有素数
Jan 23 Python
python读取和保存视频文件
Apr 16 Python
基于DataFrame改变列类型的方法
Jul 25 Python
python 实现A*算法的示例代码
Aug 13 Python
Python实现FTP文件传输的实例
Jul 07 Python
Python爬取知乎图片代码实现解析
Sep 17 Python
python中设置超时跳过,超时退出的方式
Dec 13 Python
Python面向对象编程基础实例分析
Jan 17 Python
粗略分析Python中的内存泄漏
Apr 23 #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
You might like
模仿OSO的论坛(一)
2006/10/09 PHP
以文件形式缓存php变量的方法
2015/06/26 PHP
如何利用http协议发布博客园博文评论
2015/08/03 PHP
PHP的Yii框架中过滤器相关的使用总结
2016/03/29 PHP
无需数据库在线投票调查php代码
2016/07/20 PHP
PHP中的使用curl发送请求(GET请求和POST请求)
2017/02/08 PHP
PHP memcache在微信公众平台的应用方法示例
2017/09/13 PHP
使用新的消息弹出框blackbirdjs
2008/10/16 Javascript
javascript脚本编程解决考试分数统计问题
2008/10/18 Javascript
JavaScript动态插入script的基本思路及实现函数
2013/11/11 Javascript
javascript 按键事件(兼容各浏览器)
2013/12/20 Javascript
js对象的复制继承实例
2015/01/10 Javascript
JQuery实现的图文自动轮播效果插件
2015/06/19 Javascript
ExtJs动态生成treepanel的Json格式
2015/07/19 Javascript
javascript实现base64 md5 sha1 密码加密
2015/09/09 Javascript
深入探究AngularJS框架中Scope对象的超级教程
2016/01/04 Javascript
关于动态生成dom绑定事件失效的原因及解决方法
2016/08/06 Javascript
数组Array的一些方法(总结)
2017/02/17 Javascript
Vue.js 2.0 移动端拍照压缩图片预览及上传实例
2017/04/27 Javascript
jQuery remove()过滤被删除的元素(推荐)
2017/07/18 jQuery
js实现轮播图的两种方式(构造函数、面向对象)
2017/09/30 Javascript
vue请求数据的三种方式
2020/03/04 Javascript
Python def函数的定义、使用及参数传递实现代码
2014/08/10 Python
使用Python标准库中的wave模块绘制乐谱的简单教程
2015/03/30 Python
Python数据分析matplotlib设置多个子图的间距方法
2018/08/03 Python
pandas去除重复列的实现方法
2019/01/29 Python
Python2和Python3的共存和切换使用
2019/04/12 Python
解决Python spyder显示不全df列和行的问题
2020/04/20 Python
opencv 图像轮廓的实现示例
2020/07/08 Python
用CSS3来实现社交分享按钮
2014/11/11 HTML / CSS
饿了么订餐官网:外卖、网上订餐
2019/06/28 全球购物
政协调研汇报材料
2014/08/15 职场文书
邓小平文选读书笔记
2015/06/29 职场文书
经典励志格言:每日一句,让你每天充满能量
2019/08/16 职场文书
sql查询结果列拼接成逗号分隔的字符串方法
2021/05/25 SQL Server
SpringBoot实现异步事件驱动的方法
2021/06/28 Java/Android