2

我有一个生成线程响应处理程序的服务器子类,这些处理程序依次启动应用程序线程。一切都很顺利,除非当我使用ObjGraph时,我看到正确数量的应用程序线程正在运行(我正在进行负载测试并对其进行限制以保持 35 个应用程序实例运行)。

调用objgraph.typestats()可以细分解释器中当前存在的每个对象的实例数量(根据 GC)。查看内存泄漏的输出,我发现 700 个记录器实例——这将是服务器产生的响应处理程序的总数。

当应用程序线程退出 run() 方法时,我调用了 logger.removehandler(memoryhandler) 和 logger.removehandler(filehandler) 以确保没有对记录器实例的延迟引用,而且记录器实例在应用程序线程中完全隔离(没有外部引用它)。作为消除这些记录器实例的最后一步,run() 中的最后一条语句是 del self.logger

为了在 init() 中获取记录器,我为它提供了一个适当大的随机数来命名它,这样它对于文件访问将是不同的——我使用相同的大数字作为日志文件名的一部分以避免应用程序日志冲突。

总而言之,我有 700 个被 GC 跟踪的记录器实例,但只有 35 个活动线程 - 我该如何杀死这些记录器?一个更麻烦的工程师解决方案是创建一个记录器池,并在应用程序线程的生命周期内只获取一个记录器,但这会创建更多代码来维护,什么时候 GC 应该简单地自动处理它。

4

2 回答 2

1

不要创建可能无限数量的记录器,这不是一个好习惯 - 还有其他方法可以将上下文相关信息获取到您的日志中,如此所述。

您也不需要将记录器作为实例属性:记录器是单例的,因此您可以从任何地方按名称获取特定的记录器。推荐的做法是使用在模块级别命名记录器

logger = logging.getLogger(__name__)

这足以满足大多数情况。

根据您的问题,我无法判断您是否理解处理程序和记录器不是一回事 - 例如,您谈论 removeHandler 调用(这可能有助于释放处理程序实例,因为它们的引用计数为零,但您不会t 通过这样做释放任何记录器实例)。

通常,记录器以生成感兴趣事件的应用程序部分命名。

如果您希望每个线程例如写入不同的文件,您可以每次创建一个新文件名,然后在完成并且线程即将终止时关闭处理程序(关闭对于释放处理程序资源很重要)。或者,您可以将所有内容记录到一个文件中,其中包含日志输出中包含的线程 ID 或其他鉴别器,并对日志文件使用后处理。

于 2011-06-17T17:15:06.013 回答
0

我在使用 logging.Logger() 时遇到了同样的内存泄漏,您可以尝试在记录器无用时手动关闭处理程序 fd,例如:

for handler in logger.handlers:
    handler.close()
于 2014-04-01T08:12:25.733 回答