public class Program
{
public static void Main(string[] args)
{
WeakReference wfRaiser;
WeakReference wfSubscriber;
var before = GC.GetTotalMemory(true);
//region 1: scope of raiser
{
var raiser = new Raiser();
wfRaiser = new WeakReference(raiser);
//region 2: scope of subscriber
{
var subscriber = new Subscriber();
wfSubscriber = new WeakReference(subscriber);
raiser.ValueChanged += subscriber.HandlingMethod;
}
raiser.SetValue(153);
}
Thread.Sleep(2000);
GC.Collect();
Thread.Sleep(2000);
var after = GC.GetTotalMemory(true);
Console.WriteLine($"Is raiser alive? {wfRaiser.IsAlive}\n" +
$"Is subscriber alive? {wfSubscriber.IsAlive}\n" +
"Memory size (bytes):\n" +
$"- before : {before} \n" +
$"- after : {after}\n" +
$"=> Leak: {after - before}");
}
}
public class Raiser
{
public event EventHandler<int> ValueChanged;
public void SetValue(int value) => ValueChanged?.Invoke(this, value);
}
public class Subscriber
{
private readonly double[] MyData = new double[9999];
public void HandlingMethod(object sender, int value) => Console.WriteLine($"Value changed: {value}");
}
我不明白此代码中内存泄漏的原因。 我知道提升者仍然引用订阅者(通过事件处理程序),即使是在区域2之外。但是,我认为它们都应该在区域1之后释放 raiser 。
正如弱参考文献所示,提升者和订阅者都被释放。那为什么还有内存泄漏?
我在Release模式下构建代码并启用了代码优化。 这是我的输出:
Value changed: 153
Is raiser alive? False
Is subscriber alive? False
Memory size (bytes):
- before : 21960
- after : 29520
=> Leak: 7560
答案 0 :(得分:1)
您的值只是在后台创建的其他内容的固定成本开销,如果您在程序中运行两次测试,则第二次和任何其他后续运行将更改0字节。
在代码上运行内存探查器,发现7590的大部分内容是string
的内部char[]
和TextWriter
缓冲区Console.WriteLine()
内部使用。
以下是您的测试的调整版本,在我的机器上进行第二次测试时返回0。
using System;
using System.Threading;
public class Program
{
public static void Main(string[] args)
{
RunTest();
RunTest();
}
private static void RunTest()
{
//WeakReference wfRaiser;
//WeakReference wfSubscriber;
var before = GC.GetTotalMemory(true);
//region 1: scope of raiser
{
var raiser = new Raiser();
//wfRaiser = new WeakReference(raiser);
//region 2: scope of subscriber
{
var subscriber = new Subscriber();
//wfSubscriber = new WeakReference(subscriber);
raiser.ValueChanged += subscriber.HandlingMethod;
}
raiser.SetValue(153);
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
var after = GC.GetTotalMemory(true);
Console.WriteLine(//$"Is raiser alive? {wfRaiser.IsAlive}\n" +
//$"Is subscriber alive? {wfSubscriber.IsAlive}\n" +
"Memory size (bytes):\n" +
$"- before : {before} \n" +
$"- after : {after}\n" +
$"=> Leak: {after - before}");
}
}
public class Raiser
{
public event EventHandler<int> ValueChanged;
public void SetValue(int value) => ValueChanged?.Invoke(this, value);
}
public class Subscriber
{
private readonly double[] MyData = new double[9999];
public void HandlingMethod(object sender, int value) => Console.WriteLine($"Value changed: {value}");
}