解决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 相关文章推荐
在win和Linux系统中python命令行运行的不同
Jul 03 Python
使用Python脚本和ADB命令实现卸载App
Feb 10 Python
Python3 适合初学者学习的银行账户登录系统实例
Aug 08 Python
Python for循环中的陷阱详解
Jul 13 Python
python将控制台输出保存至文件的方法
Jan 07 Python
Python redis操作实例分析【连接、管道、发布和订阅等】
May 16 Python
Django 静态文件配置过程详解
Jul 23 Python
使用python实现unix2dos和dos2unix命令的例子
Aug 13 Python
简单易懂Pytorch实战实例VGG深度网络
Aug 27 Python
python 实现从高分辨图像上抠取图像块
Jan 02 Python
Jupyter notebook 启动闪退问题的解决
Apr 13 Python
python 自动识别并连接串口的实现
Jan 19 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
杏林同学录(五)
2006/10/09 PHP
PHP 替换模板变量实现步骤
2009/08/24 PHP
使用php来实现网络服务
2009/09/15 PHP
一份老外写的XMLHttpRequest代码多浏览器支持兼容性
2007/01/11 Javascript
jQuery maxlength文本字数限制插件
2010/04/16 Javascript
屏蔽网页右键复制和ctrl+c复制的js代码
2013/01/04 Javascript
基于mouseout和mouseover等类似事件的冒泡问题解决方法
2013/11/18 Javascript
利用了jquery的ajax实现二级联互动菜单
2013/12/02 Javascript
js监听鼠标点击和键盘点击事件并自动跳转页面
2014/09/24 Javascript
详解JavaScript语言的基本语法要求
2015/11/20 Javascript
JS实现六位字符密码输入器功能
2016/08/19 Javascript
JQuery和HTML5 Canvas实现弹幕效果
2017/01/04 Javascript
JavaScript评论点赞功能的实现方法
2017/03/13 Javascript
基于webpack-hot-middleware热加载相关错误的解决方法
2018/02/22 Javascript
vue项目中使用Hbuilder打包app 设置沉浸式状态栏的方法
2018/10/22 Javascript
JS实现简单的文字无缝上下滚动功能示例
2019/06/22 Javascript
JavaScript鼠标拖拽事件详解
2020/04/03 Javascript
[02:28]DOTA2 2017国际邀请赛小组赛回顾
2017/08/09 DOTA
利用Python+Java调用Shell脚本时的死锁陷阱详解
2018/01/24 Python
python2.7实现爬虫网页数据
2018/05/25 Python
pytorch多GPU并行运算的实现
2019/09/27 Python
python logging日志模块原理及操作解析
2019/10/12 Python
TensorFlow内存管理bfc算法实例
2020/02/03 Python
django实现后台显示媒体文件
2020/04/07 Python
详解Pycharm第三方库的安装及使用方法
2020/12/29 Python
有关HTML5 Video对象的ontimeupdate事件(Chrome上无效)的问题
2013/07/19 HTML / CSS
MVMT手表官方网站:时尚又实惠的高品质手表
2016/12/04 全球购物
JS原生实现轮播图的几种方法
2021/03/23 Javascript
大学生的自我鉴定范文
2014/01/21 职场文书
家长会邀请书
2014/01/25 职场文书
管理建议书范文
2014/05/13 职场文书
旷课检讨书范文
2014/10/30 职场文书
银行求职信范文怎么写
2015/03/20 职场文书
推广普通话的宣传语
2015/07/13 职场文书
高中语文教材(文学文化常识大全一)
2019/08/13 职场文书
golang连接MySQl使用sqlx库
2022/04/14 Golang