You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python logging MemoryHandler内存泄漏问题排查求助

内存泄漏排查:MemoryHandler导致RES内存持续增长

代码片段

main.py

import time

from v1 import log, ServiceLogger
from logging.handlers import MemoryHandler


class Service:
    h = MemoryHandler(1000)
    id = None
    log = None

    def __init__(self, id: int):
        self.id = id
        self.log = ServiceLogger(log)
        log.addHandler(self.h)

    def do(self):
        print("Do some job per service!")


def service_exec():
    service = Service(id=4)
    service.do()


if __name__ == '__main__':
    while True:
        for i in range(10):
            service_exec()
            time.sleep(1)

v1.py

import logging

log = logging.getLogger('.'.join(__name__.split('.')[:-1]))


class ServiceLogger(logging.LoggerAdapter):
    def log(self, level, msg, *args, **kwargs):
        print("Custom logger hello!")

问题描述

程序运行15-30分钟后,top命令中的RES内存指标持续无上限增长。移除log.addHandler(self.h)语句后,内存增长问题消失。

更新:将MemoryHandler移至全局变量空间并添加到日志器后,内存仍持续增长。

问题原因

  1. 重复添加Handler:日志器log是全局单例对象,原代码中每次创建Service实例都会调用log.addHandler(self.h),导致日志器中累积了大量MemoryHandler实例,每个实例都会占用内存缓存日志。
  2. MemoryHandler未配置目标Handler:MemoryHandler的核心逻辑是缓存日志记录,达到容量阈值后转发给目标Handler并清空缓存。但你的代码中未指定target参数,缓存的日志永远不会被转发或清理,只会不断累积占用内存。
  3. 全局Handler的缓存累积:即使将MemoryHandler移到全局,只要没有正确配置目标Handler或触发刷新逻辑,缓存的日志记录会持续堆积,最终导致内存泄漏。

解决方法

1. 为MemoryHandler指定目标Handler

创建MemoryHandler时必须传入target参数,让它在达到缓存阈值时自动转发日志并清空缓存。示例如下:

# 全局初始化目标Handler(比如文件Handler)
import logging
from logging.handlers import MemoryHandler

file_handler = logging.FileHandler('service.log')
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

# 初始化MemoryHandler并绑定目标
global_memory_handler = MemoryHandler(capacity=1000, target=file_handler)
log.addHandler(global_memory_handler)

2. 避免重复添加Handler

确保MemoryHandler仅被添加到日志器一次,删除Service构造函数中的log.addHandler(self.h)语句,改为在全局初始化时完成Handler的添加。

3. 手动触发缓存刷新(可选)

如果需要在特定时机清理缓存,可以调用MemoryHandler.flush()方法,比如在每次任务执行完成后:

def service_exec():
    service = Service(id=4)
    service.do()
    global_memory_handler.flush()  # 手动刷新缓存,立即转发日志

4. 程序退出时清理资源

在程序终止前调用close()方法,确保缓存中剩余的日志被转发,同时释放Handler占用的资源:

if __name__ == '__main__':
    try:
        while True:
            for i in range(10):
                service_exec()
                time.sleep(1)
    except KeyboardInterrupt:
        global_memory_handler.close()
        file_handler.close()

内容的提问来源于stack exchange,提问作者Denis Prot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 13:25:00