解决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 相关文章推荐
Python解析xml中dom元素的方法
Mar 12 Python
Python+Turtle动态绘制一棵树实例分享
Jan 16 Python
Python简单定义与使用二叉树示例
May 11 Python
python五子棋游戏的设计与实现
Jun 18 Python
Python3.7 pyodbc完美配置访问access数据库
Oct 03 Python
python3 assert 断言的使用详解 (区别于python2)
Nov 27 Python
Pycharm debug调试时带参数过程解析
Feb 03 Python
使用sklearn的cross_val_score进行交叉验证实例
Feb 28 Python
python安装后的目录在哪里
Jun 21 Python
在tensorflow实现直接读取网络的参数(weight and bias)的值
Jun 24 Python
python pip如何手动安装二进制包
Sep 30 Python
如何用Python徒手写线性回归
Jan 25 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
mysql5详细安装教程
2007/01/15 PHP
基于preg_match_all采集后数据处理的一点心得笔记(编码转换和正则匹配)
2014/01/31 PHP
PHP中date与gmdate的区别及默认时区设置
2014/05/12 PHP
微信支付开发交易通知实例
2016/07/12 PHP
PHP中session跨子域的三种实现方法
2016/07/25 PHP
php 如何禁用eval() 函数实例详解
2016/12/01 PHP
innerHTML 和 getElementsByName 在IE下面的bug 的解决
2010/04/09 Javascript
dotopAlert 提示用户需安装播放器的代码
2012/09/17 Javascript
一个基于jquery的文本框记数器
2012/09/19 Javascript
JavaSript中变量的作用域闭包的深入理解
2014/05/12 Javascript
一个JavaScript去除字符串末尾的空白实例代码
2014/09/22 Javascript
angularjs中的$eval方法详解
2017/04/24 Javascript
详解AngularJS ng-class样式切换
2017/06/27 Javascript
深入浅析JSONAPI在PHP中的应用
2017/12/24 Javascript
详解Angular6学习笔记之主从组件
2018/09/05 Javascript
vue实现类似淘宝商品评价页面星级评价及上传多张图片功能
2018/10/29 Javascript
你不知道的Vue技巧之--开发一个可以通过方法调用的组件(推荐)
2019/04/15 Javascript
原生JavaScript实现刮刮乐
2020/09/29 Javascript
Python Socket编程入门教程
2014/07/11 Python
Python如何为图片添加水印
2016/11/25 Python
python机器学习之贝叶斯分类
2018/03/26 Python
使用Python通过win32 COM实现Word文档的写入与保存方法
2018/05/08 Python
pandas表连接 索引上的合并方法
2018/06/08 Python
python+POP3实现批量下载邮件附件
2018/06/19 Python
python利用7z批量解压rar的实现
2019/08/07 Python
python创建学生管理系统
2019/11/22 Python
使用 PyTorch 实现 MLP 并在 MNIST 数据集上验证方式
2020/01/08 Python
对python中 math模块下 atan 和 atan2的区别详解
2020/01/17 Python
使用sklearn的cross_val_score进行交叉验证实例
2020/02/28 Python
vscode写python时的代码错误提醒和自动格式化的方法
2020/05/07 Python
python如何遍历指定路径下所有文件(按按照时间区间检索)
2020/09/14 Python
英国领先的狗和宠物美容专家:Christies Direct
2017/04/03 全球购物
澳大利亚儿童精品仓库:Goo & Co.
2019/06/20 全球购物
预备党员群众意见
2015/06/01 职场文书
Vue.js 带下拉选项的输入框(Textbox with Dropdown)组件
2021/04/17 Vue.js
仅用几行Python代码就能复制她的U盘文件?
2021/06/26 Python