传递给已卸载 AppDomain 的服务的内存泄漏
Posted
技术标签:
【中文标题】传递给已卸载 AppDomain 的服务的内存泄漏【英文标题】:Memory leaking for service passed to unloaded AppDomain 【发布时间】:2014-06-14 17:47:00 【问题描述】:我在AppDomains
的上下文中遇到内存泄漏。我将其简化为以下内容:
我有 3 个项目、两个库项目和一个控制台项目:Shared、DynamicallyLoadable
和 RemotingTimeoutPrototype
(控制台程序)。 Shared 包含DynamicallyLoadable
和RemotingTimeoutPrototype
使用的接口。两者都在编译时引用 Shared。任何项目之间都没有其他编译时引用。
共享包含以下内容:
public interface IHostService
string GetStuff();
public interface IRemoteClass
IHostService Alpha get; set;
string CallHostServices();
DynamicallyLoaded 只包含一个类型:
public class RemoteClass : MarshalByRefObject, IRemoteClass
public IHostService Alpha get; set;
public string CallHostServices()
Console.WriteLine("Domain 0, RemoteClass.CallHostServices():", AppDomain.CurrentDomain.Id);
return Alpha.GetStuff();
控制台项目包含IHostService
的实现:
public class Alpha : MarshalByRefObject, IHostService
readonly byte[] mBuffer = new byte[100*1024*1024];
public Alpha()
for (var i = 0; i < mBuffer.Length; ++i)
mBuffer[i] = (byte) (i%256);
public string GetStuff()
return "Alpha";
Program.Main 包含以下内容:
while (true)
var otherDomain = AppDomain.CreateDomain("OtherDomain");
var proxy =
(IRemoteClass)
otherDomain.CreateInstanceFromAndUnwrap("../../../DynamicallyLoadable/bin/debug/DynamicallyLoadable.dll",
"DynamicallyLoadable.RemoteClass");
var alpha = new Alpha();
proxy.Alpha = alpha;
Console.WriteLine(proxy.CallHostServices());
Thread.Sleep(TimeSpan.FromSeconds(2));
AppDomain.Unload(otherDomain);
RemotingServices.Disconnect(alpha); // this was just an attempt, doesn't change a thing whether it's there or not
GC.Collect(); // same here, this shouldn't really be necessary, I just tried it out of despair
现在,当我让它运行一段时间后,内存消耗不断增加,最终我遇到了 OutOfMemoryException。
我希望如果持有代理的域被卸载,那么代理也被卸载,并且不再有对具体 Alpha 的引用,所以这将被收集。但显然不是。
请注意,我还通过引用 mscoree 并使用以下代码枚举加载的域来检查域是否真的被卸载:
var runtimeHost = new CorRuntimeHost();
runtimeHost.EnumDomains(out handle);
// etc.
另外,如果我将一个处理程序附加到 otherDomain.DomainUnload(),这个处理程序就可以正常调用。
请问有人能解释一下吗?
【问题讨论】:
【参考方案1】:这可能是阻塞终结线程的典型问题(因为您可能在 STA 线程中有 while-loop,并且永远不会显式地将控制权交给其他线程)。即使您卸载/处置域,它也可能在内存中,因为它尚未最终确定。在此处阅读更多内容:http://alexatnet.com/articles/memory-leaks-in-net-applications - 特别是“阻塞的终结线程”部分(我是作者)
【讨论】:
我刚刚阅读了您的文章。明天我将根据您的描述使用 WinDbg(现在我住的地方几乎是凌晨 2 点;)检查我是否在“准备完成”对象中看到任何堆积。然而,我想知道在我的情况下会有什么解决方法。也许相关:while 循环是我的 Main 的组成部分,仅此而已。 我只是尝试了一种更简单的方法来检查代理的 RemoteClass 实例是否被破坏,方法是向 RemoteClass 添加一个析构函数,如下所示: ~RemoteClass() Console.WriteLine("Being destroy"); - 我看到控制台消息,因此代理实例正在被破坏。哪个会打败终结器理论,不是吗? 我猜这个线程中应该销毁/终结的不是 RemoteClass,而是代理。无论如何,您是否尝试在 GC.Collect 之后添加 WaitForPendingFinalizers?以上是关于传递给已卸载 AppDomain 的服务的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章
卸载 appdomain 不会清除 C++ COM 对象静态成员