持有存活单例引用的无引用父对象能否被GC回收?
如果一个对象已经没有外部引用,但它持有一个仍存活的单例服务的引用(比如这个单例还被其他ViewModel引用),这个对象会不会被GC判定为不可回收?我个人倾向于这类对象可以被回收,因为它并非“拥有”这个服务,不过没找到GC对此场景的明确说明,结合以下场景具体探讨:
场景1
代码示例
public class MainClass { public SingletonService SingletonService{ get; } public void DoSomething() { using (ParentViewModel parentVM = new ParentViewModel(this.SingletonService)) { // 业务逻辑 } } }
public class ParentViewModel : INotifyPropertyChanged, IDisposable { private readonly SingletonService _service; public ParentViewModel(SingletonService singleton) { this._service = singleton; } public void Dispose(bool disposing) { // 持有存活单例的readonly引用会阻止ParentViewModel被GC回收吗? } }
疑问1:对存活单例的readonly引用是否会阻止ParentViewModel被GC回收?
不会阻止。GC判断对象能否回收,看的是有没有从GC根对象到这个对象的引用链,不是看对象本身持有什么存活对象的引用。ParentViewModel如果已经没有外部引用(比如using块结束后,parentVM变量超出作用域),哪怕它拿着单例的引用,GC也会认定它是不可达的,直接回收。单例活不活不影响它,因为引用是单向的——单例没引用ParentViewModel,不存在反向的引用链。
场景2
代码示例
public class ParentViewModel : INotifyPropertyChanged, IDisposable { public SingletonService SingletonService{ get; } public void Dispose(bool disposing) { // 需要把SingletonService属性置null才能让ParentViewModel被回收吗? // 如果SingletonService和视图有绑定,该怎么处理? } public bool SomeValue { get => this.SingletonService.OtherValue } }
疑问2:是否需要将SingletonService属性置null才能让ParentViewModel被GC回收?
不需要。和场景1逻辑一样,只要ParentViewModel本身没外部引用,哪怕它的属性还挂着单例,GC照样回收它。手动置null纯属多余,除非你有别的业务需求(比如切断事件订阅),但单纯为了GC回收完全没必要。
疑问3:若SingletonService与视图存在绑定,该如何处理?
首先,单例和视图的绑定不影响ParentViewModel的回收,因为绑定是视图和单例之间的关系,跟ParentViewModel没关系。但如果ParentViewModel通过单例订阅了事件(比如单例的PropertyChanged事件),那得在Dispose里取消订阅——这不是为了让ParentViewModel被回收,而是避免内存泄漏:单例会一直拿着ParentViewModel的引用,导致它没法被回收。如果只是像SomeValue那样单纯读取单例属性,啥都不用做。
疑问4:是否需要同时将依赖服务的SomeValue置null?
不需要。SomeValue只是个计算属性,get的时候才去读单例的OtherValue,它本身不持有任何引用。ParentViewModel被回收后,这个属性自然就没了,完全没必要手动置null。
内容的提问来源于stack exchange,提问作者rektsuki

