.net 当前加载的类不断增加-内存泄漏

.net 当前加载的类不断增加-内存泄漏,.net,debugging,windbg,.net,Debugging,Windbg,我有一个服务,有一个非常缓慢的内存泄漏。如果我分析.NET CLR加载计数器,我会看到当前加载的类计数器不断增加,并且始终与加载的类总数计数器相匹配。这给我的印象是内存泄漏与未释放的资源有关(这只是一个猜测) 该服务每次执行任务时都会创建新的appDomains(插件体系结构) 我需要找出类名,以便缩小泄漏的原因。我对WinDbg不是很精通,但我想知道是否有人能教我如何枚举这些加载的类 我有源代码,所以我可以获得符号文件,如果必要的话。提前感谢您的帮助 我建议使用一个合适的内存分析器-比如“.N

我有一个服务,有一个非常缓慢的内存泄漏。如果我分析.NET CLR加载计数器,我会看到当前加载的类计数器不断增加,并且始终与加载的类总数计数器相匹配。这给我的印象是内存泄漏与未释放的资源有关(这只是一个猜测)

该服务每次执行任务时都会创建新的appDomains(插件体系结构)

我需要找出类名,以便缩小泄漏的原因。我对WinDbg不是很精通,但我想知道是否有人能教我如何枚举这些加载的类


我有源代码,所以我可以获得符号文件,如果必要的话。提前感谢您的帮助

我建议使用一个合适的内存分析器-比如“.NET内存分析器”-您当然可以在评估时试用它,看看它是否是一种有用的工具


这将使您比核心WinDBG/SoS更容易查看所有活动对象。

编辑:在阅读了一些其他文章后,在使用更好的探查器和解决appDomain问题后,应该考虑这些内容

您可能希望将性能计数器添加到服务中,以便至少可以跟踪您创建的对象。这将帮助您确定加载的类是您的类还是CLR类

另外,在显式创建和销毁对象时添加一些调试日志可能会有所帮助


作为最后一种手段,您可以尝试将GC.Collect()放入其中,以查看调用它是否可以纠正您的问题。这不是解决方案,但测试它会让您知道这是一个选项

这是.net 2.0还是更高版本?如果是这样,您可能不会卸载
AppDomain
(正如Jon Skeet在评论中所说)

如果是1.1或更低版本,则
AppDomain
unload功能中存在错误。即,
AppDomain
被“卸载”时,它不会释放内存或释放资源


(这在.net 2.0中已经修复)

正如另一个答案所说,应该使用AppDomain.Unload()

但是,您需要小心不要在多个位置加载程序集,尤其是主appdomain

请查看AppDomain.Load(AssemblyName)的MSDN文档,了解上述操作的解释


同样在同一行中,您确定使用了正确的远程类吗?如果没有,上述情况肯定会发生。

AppDomains已卸载,但leppie的响应让我怀疑插件程序集是否同时加载到主AppDomain和次AppDomain中。当我查看性能计数器时,当前AppDomain计数不会不断增加

应用程序应该创建一个辅助appDomain,然后加载一个单独的插件程序集。也许一些代码会有帮助:

从主AppDomain创建辅助AppDomain:

AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationName = "RemoteAgentLib";
ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
ads.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
ads.ShadowCopyDirectories = AppDomain.CurrentDomain.BaseDirectory;
ads.ShadowCopyFiles = "true";

m_domain = AppDomain.CreateDomain("RemoteTaskRunner", null, ads);
RemoteTaskRunner taskRunner = m_domain.CreateInstanceAndUnwrap(
                    Assembly.GetExecutingAssembly().FullName,
                    typeof (RemoteTaskRunner).FullName) as RemoteTaskRunner;
taskRunner.LoadTask(taskInfo.Assembly, taskInfo.Type);
[Serializable]
internal class RemoteTaskRunner : MarshalByRefObject
{
    private ITask m_task;

    public RemoteTaskRunner()
    {
    }

    internal void LoadTask(string assembly, string type)
    {
        // This assembly should load in the secondary appDomain.
        Assembly taskAssembly = AppDomain.CurrentDomain.Load(assembly);
        m_task = taskAssembly.CreateInstance(type) as ITask;
    }

    internal void RunTask(string taskConfig)
    {
        // This method should run in the secondary appDomain.
        m_task.RunTask(taskConfig, m_logger);
    }
...
...
taskRunner.RunTask(taskInfo.TaskConfig);
使用RemoteTaskRunner在辅助appDomain中加载插件:

AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationName = "RemoteAgentLib";
ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
ads.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
ads.ShadowCopyDirectories = AppDomain.CurrentDomain.BaseDirectory;
ads.ShadowCopyFiles = "true";

m_domain = AppDomain.CreateDomain("RemoteTaskRunner", null, ads);
RemoteTaskRunner taskRunner = m_domain.CreateInstanceAndUnwrap(
                    Assembly.GetExecutingAssembly().FullName,
                    typeof (RemoteTaskRunner).FullName) as RemoteTaskRunner;
taskRunner.LoadTask(taskInfo.Assembly, taskInfo.Type);
[Serializable]
internal class RemoteTaskRunner : MarshalByRefObject
{
    private ITask m_task;

    public RemoteTaskRunner()
    {
    }

    internal void LoadTask(string assembly, string type)
    {
        // This assembly should load in the secondary appDomain.
        Assembly taskAssembly = AppDomain.CurrentDomain.Load(assembly);
        m_task = taskAssembly.CreateInstance(type) as ITask;
    }

    internal void RunTask(string taskConfig)
    {
        // This method should run in the secondary appDomain.
        m_task.RunTask(taskConfig, m_logger);
    }
...
...
taskRunner.RunTask(taskInfo.TaskConfig);
使用RemoteTaskRunner在辅助appDomain中执行任务:

AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationName = "RemoteAgentLib";
ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
ads.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
ads.ShadowCopyDirectories = AppDomain.CurrentDomain.BaseDirectory;
ads.ShadowCopyFiles = "true";

m_domain = AppDomain.CreateDomain("RemoteTaskRunner", null, ads);
RemoteTaskRunner taskRunner = m_domain.CreateInstanceAndUnwrap(
                    Assembly.GetExecutingAssembly().FullName,
                    typeof (RemoteTaskRunner).FullName) as RemoteTaskRunner;
taskRunner.LoadTask(taskInfo.Assembly, taskInfo.Type);
[Serializable]
internal class RemoteTaskRunner : MarshalByRefObject
{
    private ITask m_task;

    public RemoteTaskRunner()
    {
    }

    internal void LoadTask(string assembly, string type)
    {
        // This assembly should load in the secondary appDomain.
        Assembly taskAssembly = AppDomain.CurrentDomain.Load(assembly);
        m_task = taskAssembly.CreateInstance(type) as ITask;
    }

    internal void RunTask(string taskConfig)
    {
        // This method should run in the secondary appDomain.
        m_task.RunTask(taskConfig, m_logger);
    }
...
...
taskRunner.RunTask(taskInfo.TaskConfig);
要执行插件任务,在主appDomain中使用以下代码行:

AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationName = "RemoteAgentLib";
ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
ads.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
ads.ShadowCopyDirectories = AppDomain.CurrentDomain.BaseDirectory;
ads.ShadowCopyFiles = "true";

m_domain = AppDomain.CreateDomain("RemoteTaskRunner", null, ads);
RemoteTaskRunner taskRunner = m_domain.CreateInstanceAndUnwrap(
                    Assembly.GetExecutingAssembly().FullName,
                    typeof (RemoteTaskRunner).FullName) as RemoteTaskRunner;
taskRunner.LoadTask(taskInfo.Assembly, taskInfo.Type);
[Serializable]
internal class RemoteTaskRunner : MarshalByRefObject
{
    private ITask m_task;

    public RemoteTaskRunner()
    {
    }

    internal void LoadTask(string assembly, string type)
    {
        // This assembly should load in the secondary appDomain.
        Assembly taskAssembly = AppDomain.CurrentDomain.Load(assembly);
        m_task = taskAssembly.CreateInstance(type) as ITask;
    }

    internal void RunTask(string taskConfig)
    {
        // This method should run in the secondary appDomain.
        m_task.RunTask(taskConfig, m_logger);
    }
...
...
taskRunner.RunTask(taskInfo.TaskConfig);
任务完成后,将卸载appDomain:

AppDomain.Unload(m_domain);

我刚在苏珊娜·库克的博客上读到这篇文章

一定不要通过任何考试 类型/组件/等实例(除 您的MarshalByRefObject类型)返回到 原始appdomain。如果你这样做了,它会 将导致这些程序集被删除 已加载到原始appdomain。如果 appdomain设置不同 在两个AppDomain之间 程序集可能无法在那里加载。 此外,即使他们成功了 加载后,程序集将保持不变 在目标后加载并锁定 appdomain已卸载,即使 原始appdomain从不使用它们


当她说任何类型/组件等时,她会说什么类型?我之所以问这个问题,是因为我的MarshalByRefObject(RemoteTaskRunner)在任务运行后确实返回了DateTime对象。这是否会导致插件程序集加载到我的主appDomain(并最终导致内存泄漏)?

您可以随时检查appDomain中加载了哪些程序集:

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
      Console.WriteLine(assembly.FullName);
}
因此,如果您意外地将程序集加载到错误的域中,也不难看到

编辑:


如果要使用WinDgb SOS,请提供支持的命令。您可能最感兴趣的是:DumpDomain、DumpClass、DumpAssembly、EEHeap…

我认为问题实际上是由一系列未处理的FileSystemWatcher实例引起的,这些实例嵌套在RemoteTaskRunner MBRO中。我仍然不确定我是否已经完全解决了内存泄漏问题,但我可以肯定地看出其中的区别

这似乎不是文件系统观察者第一次给我带来问题


谢谢大家(特别是莱皮)帮我做这件事

每当有人报告内存泄漏时,我总是把它扔出去,因为它让我忙了几个星期。不要在调试模式下运行应用程序。如果您在.Net 2.0+中以调试模式运行应用程序(在.Net 1.1中没有),并且您实例化了一个包含事件的类,即使您没有引发该事件,它也将只保留一小块内存。这会极大地影响长时间运行的应用程序,如服务和web应用程序,因为随着时间的推移,实例化对象后消耗的少量内存会增加很多。

每次使用新的AppDomain后是否都会销毁它?是的,AppDomain.Unload被调用。此外,AppDomains的性能计数器不会不断增加。我同意,应该卸载它们。您可以转储加载到主域中的所有程序集,看看它们是否加载错误。这可能非常棘手。顺便说一句,您的MarshalByRefObject应该是可序列化的吗?你能相信吗