解决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 相关文章推荐
解读Django框架中的低层次缓存API
Jul 24 Python
Python脚本实现虾米网签到功能
Apr 12 Python
Python序列化基础知识(json/pickle)
Oct 19 Python
深入理解Python单元测试unittest的使用示例
Nov 18 Python
Python常用的json标准库
Feb 19 Python
图文详解python安装Scrapy框架步骤
May 20 Python
对Python3之方法的覆盖与super函数详解
Jun 26 Python
python实现鸢尾花三种聚类算法(K-means,AGNES,DBScan)
Jun 27 Python
Python Pandas对缺失值的处理方法
Sep 27 Python
浅析python 定时拆分备份 nginx 日志的方法
Apr 27 Python
Python绘制动态水球图过程详解
Jun 03 Python
python 带时区的日期格式化操作
Oct 23 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
《APMServ 5.1.2》使用图解
2006/10/23 PHP
php 数组动态添加实现代码(最土团购系统的价格排序)
2011/12/30 PHP
php 模拟post_验证页面的返回状态(实例讲解)
2013/10/28 PHP
thinkphp实现把数据库中的列的值存到下拉框中的方法
2017/01/20 PHP
JS获取地址栏参数的小例子
2013/08/23 Javascript
如何解决Jquery库及其他库之间的$命名冲突
2013/09/15 Javascript
jquery的each方法使用示例分享
2014/03/25 Javascript
JS实现根据当前文字选择返回被选中的文字
2014/05/21 Javascript
js控制鼠标事件移动及移出效果显示
2014/10/19 Javascript
深入理解JavaScript系列(44):设计模式之桥接模式详解
2015/03/04 Javascript
jQuery 1.9.1源码分析系列(十三)之位置大小操作
2015/12/02 Javascript
自己动手制作基于jQuery的Web页面加载进度条插件
2016/06/03 Javascript
使用React实现轮播效果组件示例代码
2016/09/05 Javascript
BootStrap中关于Select下拉框选择触发事件及扩展
2016/11/22 Javascript
Javascript基于jQuery UI实现选中区域拖拽效果
2016/11/25 Javascript
vue基础之事件v-onclick="函数"用法示例
2019/03/11 Javascript
antd-mobile ListView长列表的数据更新遇到的坑
2020/04/08 Javascript
Python使用pandas对数据进行差分运算的方法
2018/12/22 Python
python实现网页自动签到功能
2019/01/21 Python
解决Python3 被PHP程序调用执行返回乱码的问题
2019/02/16 Python
Python从函数参数类型引出元组实例分析
2019/05/28 Python
使用python list 查找所有匹配元素的位置实例
2019/06/11 Python
如何使用Python实现斐波那契数列
2019/07/02 Python
Python class的继承方法代码实例
2020/02/14 Python
基于Canvas+Vue的弹幕组件的实现
2019/07/23 HTML / CSS
使用HTML5 Geolocation实现一个距离追踪器
2018/04/09 HTML / CSS
世界上最大的折扣香水店:FragranceNet.com
2016/10/26 全球购物
秘书专业自荐信范文
2013/12/26 职场文书
党员创先争优公开承诺书
2014/03/28 职场文书
外贸业务员求职信
2014/06/16 职场文书
单位租房协议书范本
2014/12/04 职场文书
承德避暑山庄导游词
2015/02/03 职场文书
工程部文员岗位职责
2015/02/04 职场文书
实践论读书笔记
2015/06/29 职场文书
如何让2019年上半年的工作总结更出色!
2019/07/01 职场文书
Python+Selenium实现抖音、快手、B站、小红书、微视、百度好看视频、西瓜视频、微信视频号、搜狐视频、一点号、大风号、趣头条等短视频自动发布
2022/04/13 Python