解决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使用chardet判断字符串编码的方法
Mar 13 Python
python基础知识小结之集合
Nov 25 Python
Python中的Descriptor描述符学习教程
Jun 02 Python
python+matplotlib绘制旋转椭圆实例代码
Jan 12 Python
python实现聊天小程序
Mar 13 Python
Python实现网站表单提交和模板
Jan 15 Python
Tensorflow实现酸奶销量预测分析
Jul 19 Python
numpy.meshgrid()理解(小结)
Aug 01 Python
Django 大文件下载实现过程解析
Aug 01 Python
Python实现变声器功能(萝莉音御姐音)
Dec 05 Python
python将下载到本地m3u8视频合成MP4的代码详解
Nov 24 Python
python中pivot()函数基础知识点
Jan 03 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
咖啡风味 世界咖啡主要分布分布 咖啡的生长要求
2021/03/06 新手入门
支持oicq头像的留言簿(二)
2006/10/09 PHP
php strftime函数获取日期时间(switch用法)
2018/05/16 PHP
php 提交表单 关闭layer弹窗iframe的实例讲解
2018/08/20 PHP
Yii2.0框架behaviors方法使用实例分析
2019/09/30 PHP
jQueryPad 实用的jQuery测试工具(支持IE,chrome,FF)
2010/05/22 Javascript
JS中动态添加事件(绑定事件)的代码
2011/01/09 Javascript
js遍历td tr等html元素
2012/12/13 Javascript
小结Node.js中非阻塞IO和事件循环
2014/09/18 Javascript
JQuery CheckBox(复选框)操作方法汇总
2015/04/15 Javascript
详解iframe与frame的区别
2016/01/13 Javascript
Vue.js第四天学习笔记
2016/12/02 Javascript
详解ES6中的let命令
2020/04/05 Javascript
深入理解Angularjs中的$resource服务
2016/12/31 Javascript
使用Angular缓存父页面数据的方法
2017/01/03 Javascript
微信小程序 常见问题总结(4058,40013)及解决办法
2017/01/11 Javascript
layDate插件设置开始和结束时间
2018/11/15 Javascript
mpvue性能优化实战技巧(小结)
2019/04/17 Javascript
Vue使用NProgress进度条的方法
2019/09/21 Javascript
vue router 传参获取不到的解决方式
2019/11/13 Javascript
[01:03:41]完美世界DOTA2联赛PWL S3 DLG vs Phoenix 第一场 12.17
2020/12/19 DOTA
python文件比较示例分享
2014/01/10 Python
python中引用与复制用法实例分析
2015/06/04 Python
利用Tkinter和matplotlib两种方式画饼状图的实例
2017/11/06 Python
Python 3 判断2个字典相同
2019/08/06 Python
python如何实现复制目录到指定目录
2020/02/13 Python
Swisse官方海外旗舰店:澳大利亚销量领先,自然健康品牌
2017/12/15 全球购物
英国定做窗帘和纺织品面料一站式商店:Dekoria
2018/08/29 全球购物
八项规定对照检查材料
2014/08/31 职场文书
安全生产工作汇报材料
2014/10/28 职场文书
明确岗位职责
2015/02/14 职场文书
见习期个人总结
2015/03/05 职场文书
2016年质量月活动总结报告
2016/04/05 职场文书
python中的装饰器该如何使用
2021/06/18 Python
Django中session进行权限管理的使用
2021/07/09 Python
使用CSS自定义属性实现骨架屏效果
2022/06/21 HTML / CSS