解决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的几种开发工具介绍
Mar 07 Python
Python 创建空的list,以及append用法讲解
May 04 Python
查看TensorFlow checkpoint文件中的变量名和对应值方法
Jun 14 Python
Python+pandas计算数据相关系数的实例
Jul 03 Python
python操作excel的方法
Aug 16 Python
Python实现分段线性插值
Dec 17 Python
Python实现操纵控制windows注册表的方法分析
May 24 Python
Python使用正则表达式分割字符串的实现方法
Jul 16 Python
python实现代码统计器
Sep 19 Python
Python如何将字符串转换为日期
Jul 31 Python
python自动打开浏览器下载zip并提取内容写入excel
Jan 04 Python
python实现图片九宫格分割的示例
Apr 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
php mssql 时间格式问题
2009/01/13 PHP
php表单敏感字符过滤类
2014/12/08 PHP
PHP rsa加密解密使用方法
2015/04/27 PHP
php实现将Session写入数据库
2015/07/26 PHP
php 使用expat方式解析xml文件操作示例
2019/11/26 PHP
JavaScript网页制作特殊效果用随机数
2007/05/22 Javascript
Jquery+JSon 无刷新分页实现代码
2010/04/01 Javascript
javascript 文章截取部分无损html显示实现代码
2010/05/04 Javascript
JS将数字转换成三位逗号分隔的样式(示例代码)
2014/02/19 Javascript
Vue 父子组件、组件间通信
2017/03/08 Javascript
JS判断一个数是否是水仙花数
2017/06/11 Javascript
vue中实现在外部调用methods的方法(推荐)
2018/02/08 Javascript
基于Vue中点击组件外关闭组件的实现方法
2018/03/06 Javascript
JavaScript实现创建自定义对象的常用方式总结
2018/07/09 Javascript
JS实现将二维数组转为json格式字符串操作示例
2018/07/12 Javascript
详解处理bootstrap4不支持远程静态框问题
2018/07/20 Javascript
vue根据值给予不同class的实例
2018/09/29 Javascript
原生js实现淘宝放大镜效果
2020/10/28 Javascript
微信小程序实现点击图片放大预览
2019/10/21 Javascript
jQuery实现移动端扭蛋机抽奖
2020/11/08 jQuery
[18:20]DOTA2 HEROS教学视频教你分分钟做大人-昆卡
2014/06/11 DOTA
Python中第三方库Requests库的高级用法详解
2017/03/12 Python
python爬虫之百度API调用方法
2017/06/11 Python
python分治法求二维数组局部峰值方法
2018/04/03 Python
python2与python3中关于对NaN类型数据的判断和转换方法
2018/10/30 Python
Python实现12306火车票抢票系统
2019/07/04 Python
Python命令行参数argv和argparse该如何使用
2021/02/08 Python
Python LMDB库的使用示例
2021/02/14 Python
丝芙兰法国官网:SEPHORA法国
2016/09/01 全球购物
乌克兰在线药房:Аптека24
2019/10/30 全球购物
小学毕业家长寄语
2014/01/19 职场文书
总经理任命书
2014/03/29 职场文书
个人授权委托书模板
2014/09/14 职场文书
献爱心大型公益活动策划方案
2014/09/15 职场文书
寒假生活随笔
2015/08/15 职场文书
ORM模型框架操作mysql数据库的方法
2021/07/25 MySQL