我的服务内存泄漏非常慢。如果我分析.NET CLR加载计数器,我会看到当前加载的类计数器不断增加,并始终匹配加载的总类计数器。这让我觉得内存泄漏与未被释放的资源有关(这只是猜测)。
每次执行任务(插件架构)时,该服务都会创建新的appDomains。
我需要找出类名,这样我才能缩小泄漏的原因。我对WinDbg并不十分熟悉,但我想知道是否有人可以指导我完成枚举这些 Loaded 类的步骤。
我有源代码,所以我可以在必要时获取符号文件。在此先感谢您的帮助!
答案 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);