解决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实现带声音的摩斯码翻译实现方法
May 20 Python
详解Python中的type()方法的使用
May 21 Python
横向对比分析Python解析XML的四种方式
Mar 30 Python
python pandas 对时间序列文件处理的实例
Jun 22 Python
pyqt5 使用label控件实时显示时间的实例
Jun 14 Python
python实现在函数图像上添加文字和标注的方法
Jul 08 Python
Python中字典与恒等运算符的用法分析
Aug 22 Python
关于numpy中eye和identity的区别详解
Nov 29 Python
Python进阶之迭代器与迭代器切片教程
Jan 29 Python
Python中的X[:,0]、X[:,1]、X[:,:,0]、X[:,:,1]、X[:,m:n]和X[:,:,m:n]
Feb 13 Python
jupyter notebook 使用过程中python莫名崩溃的原因及解决方式
Apr 10 Python
Pycharm 跳转回之前所在页面的操作
Feb 05 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
攻克CakePHP系列一 连接MySQL数据库
2008/10/22 PHP
用C/C++扩展你的PHP 为你的php增加功能
2012/09/06 PHP
PHP is_subclass_of函数的一个BUG和解决方法
2014/06/01 PHP
thinkphp3.2.2实现生成多张缩略图的方法
2014/12/19 PHP
php 把数字转换成汉字的代码
2015/07/21 PHP
Yii2-GridView 中让关联字段带搜索和排序功能示例
2017/01/21 PHP
php+js实现的拖动滑块验证码验证表单操作示例【附源码下载】
2020/05/27 PHP
javascript中的对象和数组的应用技巧
2007/01/07 Javascript
javascript 写类方式之十
2009/07/05 Javascript
jQuery 重复加载错误以及修复方法
2014/12/16 Javascript
JavaScript中的数值范围介绍
2014/12/29 Javascript
JavaScript中的对象序列化介绍
2014/12/30 Javascript
js实现类似新浪微博首页内容渐显效果的方法
2015/04/10 Javascript
Javascript非构造函数的继承
2015/04/27 Javascript
javascript实现起伏的水波背景效果
2016/05/16 Javascript
node.js基于mongodb的搜索分页示例
2017/01/22 Javascript
原生js封装添加class,删除class的实例
2017/11/06 Javascript
Vue.js 2.0和Cordova开发webApp环境搭建方法
2018/02/26 Javascript
微信小程序scroll-view实现字幕滚动
2018/07/14 Javascript
vue拖拽组件使用方法详解
2018/12/01 Javascript
jQuery实现当拉动滚动条到底部加载数据的方法分析
2019/01/24 jQuery
使用express获取微信小程序二维码小记
2019/05/21 Javascript
如何构建 vue-ssr 项目的方法步骤
2020/08/04 Javascript
python贪婪匹配以及多行匹配的实例讲解
2018/04/19 Python
Python3.6使用tesseract-ocr的正确方法
2018/10/17 Python
Python 3.3实现计算两个日期间隔秒数/天数的方法示例
2019/01/07 Python
详解Python中Pyyaml模块的使用
2020/10/08 Python
python中altair可视化库实例用法
2021/01/26 Python
韩国家庭购物网上商店:Nsmall
2017/05/07 全球购物
Roxy荷兰官方网站:冲浪、滑雪板、服装和配件
2019/10/22 全球购物
《美丽的小路》教学反思
2014/02/26 职场文书
MySQL的Query Cache图文详解
2021/07/01 MySQL
javascript条件式访问属性和箭头函数介绍
2021/11/17 Javascript
关于mysql中时间日期类型和字符串类型的选择
2021/11/27 MySQL
MySQL下载安装配置详细教程 附下载资源
2022/09/23 MySQL