我有一个父对象,它具有对子对象的引用,另外父对象有一个事件处理程序,用于侦听子对象的事件。
如果将释放对父对象的所有引用,是否将通过GC释放通过父对象和子进程使用的内存?(假设不再向子对象或父对象引用存在)。
class ParentClass {
ChildClass _childClass;
public ParentClass(ChildClass childClass) {
_childClass = childClass;
childClass.SomeEvent += ChildClass_SomeEvent;
}
void ChildClass_SomeEvent(object sender, SomeEventArgs e) {
}
}
请注意,我知道GC不会立即做出反应。我的问题不是在释放父对象后是否立即释放内存。我的问题是,无论如何都要释放内存。
更新
对我而言,似乎答案很清楚是的,GC能够解决此循环引用。 但对于阅读此帖并且有类似问题的所有人,请注意不要让事件注册开放。这只是一个特殊的例子,注册没有问题。在其他情况下,事件注册可能会导致严重的内存泄漏。
vilx提供了一个涵盖这个问题的非常好的资源: http://www.interact-sw.co.uk/iangblog/2004/07/07/circulareventrefs
答案 0 :(得分:9)
是。 .NET GC处理循环引用没有问题(假设你没有使用非托管资源,或者你实现了IDisposable)。
答案 1 :(得分:3)
好吧,如果没有其他人使用相同的_childClass
实例,是的,它会被收集。但是,我不确定你没有分离事件处理程序会带来什么影响 - 剩下注册的事件处理程序是.NET应用程序中无法使用GC的内存泄漏的来源 - 你的问题很有趣。
更新:结果表明我对内存泄漏源的理解是倒退的。如果A类订阅B类但没有取消订阅,那么只有在收集B时才收集A(因为B的订阅列表保持A存活)。对于长期存在的事件源,这可能是订阅者的问题。
问题出现在所有对象生命周期中,但实际上,如果B与A一样短暂,则不会发现泄漏,通常也不会出现问题。
更新2:在OP的示例中,child是源,父是订阅者 - GC可以处理这种情况 - 只要子节点不在父节点之外引用,意思是孩子不适合收集(这将使父母保持活力)。
答案 2 :(得分:1)
当对堆上的特定对象没有更强的引用时(即引用计数已经变为零),GC确实被安排为该对象释放内存。在何处或如何定义这些引用并不重要;它们由GC跟踪并进行相应处理。
值得注意的是:GC本质上是非常不确定的方式(它在一个单独的线程上运行,一开始),并且可能无法在内存中破坏对象一段时间。 “有些时间”通常几乎是瞬间的,除非在处理器太忙和/或一次释放大量内存的特殊情况下。
答案 3 :(得分:1)
childClass.SomeEvent += ChildClass_SomeEvent;
此代码表示该孩子引用了父母。这是循环引用,GC可以处理这个问题。在另一种情况下,未订阅的事件可能是危险的。假设您有一些独立的实例而不是childClass:
anotherClass.SomeEvent += AnotherClass_SomeEvent;
现在,当您认为可以收集ParentClass时,如果anotherClass仍然存在且未取消事件订阅,则它实际上仍然存活。仅当收集anotherClass时,也可以收集ParentClass。