ObjectListView TreeListView缓存持有引用致内存泄漏,如何处理?
TreeListView节点对象无法被GC回收问题分析
在NuGet包ObjectListView.Official 2.9.1搭配**.NET Framework 4.7.2**的环境中,出现了TreeListView组件释放后,其加载的Node对象无法被GC回收的问题——即便已释放组件且无其他外部引用,这些Node对象甚至未进入GC终结器队列。
可复现该问题的测试代码如下:
public FormTreeList() { InitializeComponent(); List<Node> nodes = new List<Node>(); for (int i = 0; i < 500; i++) { nodes.Add(new Node("Node" + i.ToString())); } olvColumnValue.AspectGetter = delegate (object x) { return ((Node)x).Value; }; this.treeListView1.SetObjects(nodes); } protected override void Dispose(bool disposing) { if (disposing && (components != null)) { //Disposing of the references treeListView1.ClearCachedInfo(); treeListView1.CanExpandGetter = null; treeListView1.ChildrenGetter = null; treeListView1.ParentGetter = null; treeListView1.Columns.Clear(); olvColumnValue.AspectGetter = null; olvColumnValue = null; treeListView1.Roots = null; treeListView1 = null; components.Dispose(); } base.Dispose(disposing); } public class Node { public string Value { get; set; } public Node(string value) { Value = value; } }
通过RedGate ANTS工具生成的GC图可见,FormTreeList释放后,私有成员cachedLayoutEvent仍持有Node对象的引用,但因该事件为私有,无法手动取消订阅。
这属于ObjectListView的Bug。组件内部维护的cachedLayoutEvent私有事件在Dispose流程中未被清理,导致关联的委托(包含Node对象的上下文引用)无法被GC回收,进而持续持有Node实例,阻止了GC对这些对象的回收。
临时解决办法可通过反射手动清理该私有事件:
// 在Dispose方法中添加以下逻辑(需放在treeListView1置空之前) if (treeListView1 != null) { var field = typeof(TreeListView).GetField("cachedLayoutEvent", BindingFlags.NonPublic | BindingFlags.Instance); if (field != null) { var eventHandler = field.GetValue(treeListView1) as Delegate; if (eventHandler != null) { foreach (var handler in eventHandler.GetInvocationList()) { field.SetValue(treeListView1, Delegate.Remove(eventHandler, handler)); } } } }
更稳妥的方式是升级到ObjectListView的最新版本,后续版本大概率已修复这类资源泄漏问题。
内容的提问来源于stack exchange,提问作者Ninotter
相关产品推荐
相关产品推荐

