我有一个服务器子类产生线程响应处理程序,处理程序依次启动应用程序线程。一切顺利,除非我使用ObjGraph我看到正确运行的应用程序线程数量(我正在加载测试并限制它以保持35个应用程序实例运行)。
调用objgraph.typestats()可以解释每个对象当前在解释器中的实例数(根据GC)。查看内存泄漏的输出,我找到700个记录器实例 - 这将是服务器生成的响应处理程序的总数。
当应用程序线程退出run()方法时,我调用了logger.removehandler(memoryhandler)和logger.removehandler(filehandler),以确保没有对记录器实例的延迟引用,记录器实例也完全隔离在内应用程序线程(没有外部引用)。作为消除这些记录器实例的最后一步,run()中的最后一个语句是del self.logger
为了在init()中获取记录器,我给它提供了一个适当大的随机数来命名它,因此它对于文件访问是不同的 - 我使用相同的大数字作为日志文件名的一部分来避免应用程序日志冲突。
长期和短期是我跟踪了700个记录器实例,但只有35个活动线程 - 我如何杀死这些记录器?一个更麻烦的工程师解决方案是创建一个记录器池,并在应用程序线程的生命周期中获取一个,但是当GC应该自动处理时,创建更多的代码来维护。
答案 0 :(得分:0)
不要创建可能无限数量的记录器,这不是一种好的做法 - 还有其他方法可以将上下文相关信息记录到日志中,如文档here所述。
您也不需要将记录器作为实例属性:记录器是单例,因此您可以从任何地方按名称获取特定的记录器。建议的做法是使用
在模块级别命名记录器logger = logging.getLogger(__name__)
足以满足大多数情况。
从你的问题我不知道你是否意识到处理程序和记录器是不一样的 - 例如你谈论removeHandler调用(它可能用于释放处理程序实例,因为它们的引用计数为零,但是你不会通过这样做释放任何记录器实例。)
通常,记录器以应用程序的部分命名,这些部分会生成感兴趣的事件。
如果你想要每个线程,例如写入不同的文件,您可以每次创建一个新的文件名,然后在完成后关闭处理程序并且线程即将终止(关闭对于释放处理程序资源很重要)。或者,您可以使用日志输出中包含的线程ID或其他鉴别器将所有内容记录到一个文件中,并对日志文件使用后处理。
答案 1 :(得分:0)
使用logging.Logger()时遇到了相同的内存泄漏,当记录器无用时,您可能会尝试手动关闭处理程序fd,如:
for handler in logger.handlers:
handler.close()