解决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 两个列表的差集、并集和交集实现代码
Sep 21 Python
教你学会使用Python正则表达式
Sep 07 Python
pytorch + visdom 处理简单分类问题的示例
Jun 04 Python
Python小游戏之300行代码实现俄罗斯方块
Jan 04 Python
Python设计模式之组合模式原理与用法实例分析
Jan 11 Python
使用python3构建文件传输的方法
Feb 13 Python
python自带tkinter库实现棋盘覆盖图形界面
Jul 17 Python
有关Tensorflow梯度下降常用的优化方法分享
Feb 04 Python
TensorFlow实现checkpoint文件转换为pb文件
Feb 10 Python
python爬虫用scrapy获取影片的实例分析
Nov 23 Python
Python Pandas pandas.read_sql函数实例用法
Jun 21 Python
Python实现制作销售数据可视化看板详解
Nov 27 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中file_exists与is_file,is_dir的区别介绍
2012/09/12 PHP
php获取指定(访客)IP所有信息(地址、邮政编码、国家、经纬度等)的方法
2015/07/06 PHP
PHP常用设计模式之委托设计模式
2016/02/13 PHP
ThinkPHP设置禁止百度等搜索引擎转码(简单实用)
2016/02/15 PHP
PHP 中使用explode()函数切割字符串为数组的示例
2017/05/06 PHP
jQuery Ajax文件上传(php)
2009/06/16 Javascript
面向对象继承实例(a如何继承b问题)(自写)
2013/07/01 Javascript
Jquery遍历checkbox获取选中项value值的方法
2014/02/13 Javascript
jquery制作select列表双向选择示例代码
2014/09/02 Javascript
node.js中的fs.readlinkSync方法使用说明
2014/12/17 Javascript
JS实现的4种数字千位符格式化方法分享
2015/03/02 Javascript
详解JavaScript对象和数组
2015/12/03 Javascript
jQuery内存泄露解决办法
2016/12/13 Javascript
Chrome浏览器的alert弹窗禁止再次弹出后恢复的方法
2016/12/30 Javascript
vue2.0 父组件给子组件传递数据的方法
2018/01/15 Javascript
微信小程序实现全局搜索代码高亮的示例
2018/03/30 Javascript
基于Vue实现的多条件筛选功能的详解(类似京东和淘宝功能)
2019/05/07 Javascript
Vue+Element-UI实现上传图片并压缩
2019/11/26 Javascript
一篇文章带你使用Typescript封装一个Vue组件(简单易懂)
2020/06/05 Javascript
js实现ajax的用户简单登入功能
2020/06/18 Javascript
Python对两个有序列表进行合并和排序的例子
2014/06/13 Python
实例讲解Python的函数闭包使用中应注意的问题
2016/06/20 Python
django 2.0更新的10条注意事项总结
2018/01/05 Python
python抖音表白程序源代码
2019/04/07 Python
Django 使用easy_thumbnails压缩上传的图片方法
2019/07/26 Python
基于python实现坦克大战游戏
2020/10/27 Python
DBA的职责都有哪些
2012/05/16 面试题
《凡卡》教学反思
2014/04/09 职场文书
关于保护环境的建议书
2014/05/13 职场文书
征兵宣传标语
2014/06/20 职场文书
超市开店计划书
2014/09/15 职场文书
党员批评与自我批评
2014/10/15 职场文书
基层工作经历证明
2015/06/19 职场文书
关于元旦的广播稿2016
2015/12/17 职场文书
学习党史心得体会2016
2016/01/23 职场文书
MySQL数据库如何使用Shell进行连接
2022/04/12 MySQL