首页 > 解决方案 > Log4Net LogImpl 的实例太多

问题描述

我们正在使用 log4net 进行日志记录,我正在分析内存转储。我们将 log4net 实现包装在我们自己的名为“TextLogger”的类中,其中堆上有 6601 个实例,而 LogImpl 有 325882 个实例。

这对我来说似乎很奇怪。我们创建的记录器只能存活很短一段时间,所以我猜它们可能只是在等待被垃圾收集,但我仍然发现差异太大而无法用未清理的垃圾来解释。

从 windbg 复制,其中第一列是实例数,第二列是以字节为单位的总大小我们的包装类:6601 211232 Logging.TextLogger

log4net 类:

325882 10428224 log4net.Repository.Hierarchy.LoggerKey

325882 20856448 log4net.Core.LogImpl

325903 20857792 log4net.Repository.LoggerRepositoryConfigurationChangedEventHandler

325882 23463504 log4net.Repository.Hierarchy.DefaultLoggerFactory+LoggerImpl

任何有 log4net 经验的人都会说这是正常的,还是我们没有以某种方式正确处理我们的记录器实现?

我想我们也有可能以某种方式持有对记录器的引用,以防止它们被垃圾收集,但我们没有接近相同数量的可能引用记录器的其他类的实例。

编辑:TextLogger 实现:

public class TextLogger
{
    private ILog m_Logger;
    private string m_UniqueId;

    static TextLogger()
    {
        var stream = File.OpenRead(@"log4net.config");
        log4net.Config.XmlConfigurator.Configure(stream);
    }

    public TextLogger(string name, object owner)
    {
        m_UniqueId = Guid.NewGuid().ToString("n").Substring(0, 6);
        if (false && owner != null)
        {
            m_Logger = log4net.LogManager.GetLogger(string.Format("{0}-{1}", m_UniqueId, name), owner.GetType());
        }
        else
        {
            m_Logger = log4net.LogManager.GetLogger(string.Format("{0}-{1}", m_UniqueId, name));
        }
    }

    public void Debug(string msg)
    {
        m_Logger.Debug(msg);
    }

    public void Error(string msg)
    {
        m_Logger.Error(msg);
    }

    public void Error(string msg, Exception exception)
    {
        m_Logger.Error($"{msg}, {exception.ToString().Replace(Environment.NewLine, "<br>")}");
    }
    public void Warn(string msg)
    {
        m_Logger.Warn(msg);
    }
}

标签: c#memory-leakslog4netwindbgheap-memory

解决方案


我看到对于 TextLogger 的每个实例,都会实例化一个具有唯一基于 Guid 的名称的新 log4net.ILog:

m_Logger = log4net.LogManager.GetLogger(string.Format("{0}-{1}", m_UniqueId, name));

log4net.LogManager 重新调整的 ILog 在您的应用程序期间保持“活动”。这使得以后可以通过其名称再次检索相同的 ILog 实例。

因为您的所有实例都有唯一的名称,所以它们在这里计数。

使用 log4net 时,典型设置是为每个组件/类使用静态记录器,如下所示。尝试将您的代码重构为类似的东西。

class Foo
{
    private static ILog _logger = LogManager.Instance.GetLogger(typeof(Foo));

    public void DoSomething()
    {
        _logger.Info("Doing something"); 
    }
}

这个概念在log4net 网站上得到了很好的解释。


推荐阅读