解决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 struct.unpack
Sep 06 Python
python中xrange和range的区别
May 13 Python
Pthon批量处理将pdb文件生成dssp文件
Jun 21 Python
Python设计模式编程中解释器模式的简单程序示例分享
Mar 02 Python
Python2.7读取PDF文件的方法示例
Jul 13 Python
python MysqlDb模块安装及其使用详解
Feb 23 Python
Python图片转换成矩阵,矩阵数据转换成图片的实例
Jul 02 Python
tensorflow实现图像的裁剪和填充方法
Jul 27 Python
PyQt QListWidget修改列表项item的行高方法
Jun 20 Python
pyhton中__pycache__文件夹的产生与作用详解
Nov 24 Python
python爬虫scrapy基于CrawlSpider类的全站数据爬取示例解析
Feb 20 Python
python爬取企查查企业信息之selenium自动模拟登录企查查
Apr 08 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
解析php 版获取重定向后的地址(代码)
2013/06/26 PHP
PHP JSON出错:Cannot use object of type stdClass as array解决方法
2014/08/16 PHP
PHP导出Excel实例讲解
2016/01/24 PHP
PHP基于新浪IP库获取IP详细地址的方法
2017/05/04 PHP
PHP迭代与递归实现无限级分类
2017/08/28 PHP
PHP设计模式之观察者模式定义与用法示例
2018/08/04 PHP
PHP 图片合成、仿微信群头像的方法示例
2019/10/25 PHP
yii框架结合charjs实现统计30天数据的方法
2020/04/04 PHP
js局部刷新页面时间具体实现
2013/07/04 Javascript
用队列模拟jquery的动画算法实例
2015/01/20 Javascript
纯javascript实现图片延时加载方法
2015/08/21 Javascript
jQuery实现的AJAX简单弹出层效果代码
2015/11/26 Javascript
javascript获取网页各种高宽及位置的方法总结
2016/07/27 Javascript
从零开始学习Node.js系列教程二:文本提交与显示方法
2017/04/13 Javascript
在vue中获取dom元素内容的方法
2017/07/10 Javascript
Angular实现模版驱动表单的自定义校验功能(密码确认为例)
2018/05/17 Javascript
浅谈webpack性能榨汁机(打包速度优化)
2019/01/09 Javascript
微信小程序缓存支持二次开发封装实现解析
2019/12/16 Javascript
javascript设计模式 ? 职责链模式原理与用法实例分析
2020/04/16 Javascript
解决echarts图表使用v-show控制图表显示不全的问题
2020/07/19 Javascript
用Python实现一个简单的多线程TCP服务器的教程
2015/05/05 Python
python3爬取淘宝信息代码分析
2018/02/10 Python
python web基础之加载静态文件实例
2018/03/20 Python
Python 字符串与数字输出方法
2018/07/16 Python
python中reader的next用法
2018/07/24 Python
Python反射和内置方法重写操作详解
2018/08/27 Python
对Python3.x版本print函数左右对齐详解
2018/12/22 Python
python修改txt文件中的某一项方法
2018/12/29 Python
在tensorflow中实现去除不足一个batch的数据
2020/01/20 Python
Python drop方法删除列之inplace参数实例
2020/06/27 Python
万代美国官网:PREMIUM BANDAI USA
2020/09/11 全球购物
请说出以下代码输出什么
2013/08/30 面试题
2013年高中生自我评价
2013/10/23 职场文书
小学校园活动策划
2014/01/30 职场文书
2015年计算机教学工作总结
2015/07/22 职场文书
解决pycharm下载库时出现Failed to install package的问题
2021/09/04 Python