C#的lambda表达式的好用就不多说了,中午吃饭的时候突然想到一个以前(有年头了,难道屌丝上岁数了就回忆这个么。。。)和同事争执的坑。。
列个demo吧。。
先是一个类,这个类的对象就是为了吃堆内存用的,,
public class MemoryModel { public MemoryModel(int id) { Data = new byte[1024 * 1024]; Id = id; } public byte[] Data { set; get; } public int Id { set; get; } }
然后再来个类存放个事件
public class EventClass { public void Show(int m) { if (ShowMe != null) ShowMe(m); } public event Action<int> ShowMe; }
测试代码如下;
EventClass e = new EventClass(); int i = 0; while (true) { List<MemoryModel> list = new List<MemoryModel>(); i++; for (int j = 0; j < 100; j++) { var mod = new MemoryModel(i); e.ShowMe += c => { string s = mod.Id + "----" + mod.Data.Length; Console.Write(c); }; list.Add(mod); } Thread.Sleep(50); list.Clear(); }
估计乍一看,不知所云。。不明所以。其实主要想说的是事件注册的这个lambda表达式,这块存在比较大的问题,很多人为了方便会在lambda表达式内直接使用作用域范围外的对象,比如说这个mod对象,委托也就是方法的引用,也可以说是方法的指针,运行时在执行方法时会打开栈帧,并将方法使用到的参数或参数的引用存储到该栈帧的局部变量区中,此处我定义的事件目测只有一个int类型的参数,但由于这种随意的编码就导致了我附加了两个参数,也就是mod对象。也就是说这个mod对象会被局部变量区持续引用也就发生了垃圾回收器无法对该对象进行回收了,直到EventClass对象释放为止(当然此方法内是不会的。。)。结果也就内存泄漏了。。
当然场景不只是在这个while场景下,委托内使用作用域范围外的对象都会增加那个对象的生命周期。。
对于这种情况发生的场景。。还是有一些的。。。