代表可以导致内存泄漏? GC.TotalMemory(真)似乎表明这样 [英] Can delegates cause a memory leak? GC.TotalMemory(true) seems to indicate so
问题描述
代码
使用系统;
内部静态类测试
{
私有静态无效的主要()
{
试
{
Console.WriteLine({0, 10}:起点,GC.GetTotalMemory(真));
行动SimpleDelegate的= SimpleDelegate的;
Console.WriteLine({0,10}:简单的委托创作,GC.GetTotalMemory(真));
行动simpleCombinedDelegate = SimpleDelegate的+ SimpleDelegate的+ SimpleDelegate的;
Console.WriteLine({0,10}:简单的联合委托创作,GC.GetTotalMemory(真));
字节[] = bigManagedResource新的字节[亿];
Console.WriteLine({0,10}:大管理资源创建,GC.GetTotalMemory(真));
行动bigManagedResourceDelegate = bigManagedResource.BigManagedResourceDelegate;
Console.WriteLine({0,10}:大托管资源的委托创作,GC.GetTotalMemory(真));
行动bigCombinedDelegate = simpleCombinedDelegate + bigManagedResourceDelegate;
Console.WriteLine({0,10}:大联合委托创作,GC.GetTotalMemory(真));
GC.KeepAlive(bigManagedResource);
bigManagedResource = NULL;
GC.KeepAlive(bigManagedResourceDelegate);
bigManagedResourceDelegate = NULL;
GC.KeepAlive(bigCombinedDelegate);
bigCombinedDelegate = NULL;
Console.WriteLine({0,10}:大托管资源,大管理的资源代表和大联合委托删除,但内存没有释放,GC.GetTotalMemory(真));
GC.KeepAlive(simpleCombinedDelegate);
simpleCombinedDelegate = NULL;
Console.WriteLine({0,10}:简单组合委托删除,内存被释放,最后,GC.GetTotalMemory(真));
GC.KeepAlive(SimpleDelegate的);
SimpleDelegate的= NULL;
Console.WriteLine({0,10}:简单的委托撤销,GC.GetTotalMemory(真));
}
赶上(例外五)
{
Console.WriteLine(E);
}
Console.ReadKey(真);
}
私有静态无效SimpleDelegate的(){}
私有静态无效BigManagedResourceDelegate(这byte []数组){}
}
输出
GC.TotalMemory(真)
105776:起点
191264:简单的委托创建
191328:简单组合委托创建
100191344:大管理资源创建
100191780:大管理资源委派创建
100191812:大联合委托创建
100191780:大托管资源,大管理的资源代表和大联合委托删除,但内存没有释放
191668:简单组合委托拆除,记忆中解脱出来,最后
191636:简单的委托删除
有趣的案例。这里是解决方案:
组合代表为观测上的纯:它看起来像代表是不可改变的外面。但内部,现有的代表正在修改即可。他们分享,在一定条件下,同样的 _invocationList
性能原因(这几个代表们迷上了同样的事件场景优化)。不幸的是, _invocationList
为 simpleCombinedDelegate
引用 bigMgdResDelegate
这导致内存来维持生命。
Code
using System;
internal static class Test
{
private static void Main()
{
try
{
Console.WriteLine("{0,10}: Start point", GC.GetTotalMemory(true));
Action simpleDelegate = SimpleDelegate;
Console.WriteLine("{0,10}: Simple delegate created", GC.GetTotalMemory(true));
Action simpleCombinedDelegate = simpleDelegate + simpleDelegate + simpleDelegate;
Console.WriteLine("{0,10}: Simple combined delegate created", GC.GetTotalMemory(true));
byte[] bigManagedResource = new byte[100000000];
Console.WriteLine("{0,10}: Big managed resource created", GC.GetTotalMemory(true));
Action bigManagedResourceDelegate = bigManagedResource.BigManagedResourceDelegate;
Console.WriteLine("{0,10}: Big managed resource delegate created", GC.GetTotalMemory(true));
Action bigCombinedDelegate = simpleCombinedDelegate + bigManagedResourceDelegate;
Console.WriteLine("{0,10}: Big combined delegate created", GC.GetTotalMemory(true));
GC.KeepAlive(bigManagedResource);
bigManagedResource = null;
GC.KeepAlive(bigManagedResourceDelegate);
bigManagedResourceDelegate = null;
GC.KeepAlive(bigCombinedDelegate);
bigCombinedDelegate = null;
Console.WriteLine("{0,10}: Big managed resource, big managed resource delegate and big combined delegate removed, but memory not freed", GC.GetTotalMemory(true));
GC.KeepAlive(simpleCombinedDelegate);
simpleCombinedDelegate = null;
Console.WriteLine("{0,10}: Simple combined delegate removed, memory freed, at last", GC.GetTotalMemory(true));
GC.KeepAlive(simpleDelegate);
simpleDelegate = null;
Console.WriteLine("{0,10}: Simple delegate removed", GC.GetTotalMemory(true));
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.ReadKey(true);
}
private static void SimpleDelegate() { }
private static void BigManagedResourceDelegate(this byte[] array) { }
}
Output
GC.TotalMemory(true)
105776: Start point
191264: Simple delegate created
191328: Simple combined delegate created
100191344: Big managed resource created
100191780: Big managed resource delegate created
100191812: Big combined delegate created
100191780: Big managed resource, big managed resource delegate and big combined delegate removed, but memory not freed
191668: Simple combined delegate removed, memory freed, at last
191636: Simple delegate removed
Interesting case. Here is the solution:
Combining delegates is observationally pure: It looks like delegates are immutable to the outside. But internally, existing delegates are being modified. They share, under certain conditions, the same _invocationList
for performance reasons (optimizing for the scenario that a few delegates are hooked up to the same event). Unfortunately, the _invocationList
for the simpleCombinedDelegate
references the bigMgdResDelegate
which causes the memory to be kept alive.
这篇关于代表可以导致内存泄漏? GC.TotalMemory(真)似乎表明这样的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!