目前的课程不断增加 - 内存泄漏

时间:2008-12-05 14:19:39

标签: .net debugging windbg

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

每次执行任务(插件架构)时,该服务都会创建新的appDomains。

我需要找出类名,这样我才能缩小泄漏的原因。我对WinDbg并不十分熟悉,但我想知道是否有人可以指导我完成枚举这些 Loaded 类的步骤。

我有源代码,所以我可以在必要时获取符号文件。在此先感谢您的帮助!

9 个答案:

答案 0 :(得分:2)

这是.net 2.0还是更高?如果是这样,您可能无法卸载AppDomain(正如Jon Skeet在评论中所述)。

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

(从.net 2.0开始修复)

答案 1 :(得分:2)

您始终可以检查AppDomain中加载的程序集:

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
      Console.WriteLine(assembly.FullName);
}

因此,如果您不小心将程序集加载到错误的域中,则不会太难看到。

编辑:

如果要使用WinDgb SOS,here是受支持的命令。您最有可能对以下内容感兴趣:DumpDomain,DumpClass,DumpAssembly,EEHeap ......

答案 2 :(得分:2)

我认为这个问题实际上是由一系列未部署的FileSystemWatcher实例引起的,这些实例嵌套在RemoteTaskRunner MBRO中。我仍然不确定我是否完全解决了内存泄漏问题,但我绝对可以说出不同之处。

似乎这不是FileSystemWatchers第一次给我带来问题。 :)

感谢大家(特别是leppie)帮助我!

答案 3 :(得分:1)

我建议使用一个合适的内存分析器 - 比如“.NET Memory Profiler” - http://memprofiler.com/你当然可以在评估中试一试,看看它是否有助于它的工具。

这将使您能够比硬核WinDBG / SoS更容易地看到所有活动对象。

答案 4 :(得分:1)

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

但是,您需要注意不要在多个位置加载程序集,尤其是主应用程序域。

查看AppDomain.Load(AssemblyName)的MSDN文档,了解上述情况的解释。

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

答案 5 :(得分:1)

我刚刚在Suzanne Cook的博客上看过这篇文章。

http://blogs.msdn.com/suzcook/archive/2003/06/12/57169.aspx

  

一定不要通过   类型/组件/等。实例(除了   你的MarshalByRefObject类型)回到   原来的appdomain。如果你这样做的话   将导致这些组件   加载到原始appdomain。如果   appdomain设置不同   两个appdomains之间,那些   组件可能无法在那里装载。   另外,即使他们成功了   加载后,组件将保留   在目标之后加载并锁定   appdomain是卸载的,即使是   原始appdomain从不使用它们。

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

答案 6 :(得分:1)

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

答案 7 :(得分:0)

编辑:在阅读了其他一些帖子之后,在使用更好的分析器之后以及在解决appDomain问题之后,应该考虑这些帖子。

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

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

作为最后的手段,您可以尝试将GC.Collect()放在那里,看看是否可以调用它来纠正您的问题。它不是 修复,但测试它会让你知道它是一个选项。

答案 8 :(得分:0)

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在辅助appDomain中加载插件:

RemoteTaskRunner taskRunner = m_domain.CreateInstanceAndUnwrap(
                    Assembly.GetExecutingAssembly().FullName,
                    typeof (RemoteTaskRunner).FullName) as RemoteTaskRunner;
taskRunner.LoadTask(taskInfo.Assembly, taskInfo.Type);

使用RemoteTaskRunner在辅助appDomain中执行任务:

[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);
    }
...
...

要执行插件任务,在主appDomain中使用以下代码行:

taskRunner.RunTask(taskInfo.TaskConfig);

任务完成后,将卸载appDomain:

AppDomain.Unload(m_domain);