C # memory leak events

When does a memory leak occur with canceled signatures? Should I write a destructor or implement IDisposable to unsubscribe from an event?

+13
c # memory-leaks
Aug 26 '12 at 20:15
source share
2 answers

Let's say that A links B. Also, say you think you are done with B and expect this to be garbage collection.

Now, if A is reachable [1], B will not be garbage collected, despite the fact that "you did with it." This is essentially a memory leak [2]

If B subscribes to an event in A , then we have the same situation: A has a link to B through the delegate of the event handler.

So when is the problem? Only when the link object is available, as described above. In this case, there may be a leak when the Foo instance is no longer used:

class Foo { Bar _bar; public Foo(Bar bar) { _bar = bar; _bar.Changed += BarChanged; } void BarChanged(object sender, EventArgs e) { } } 

The reason there might be a leak is because the Bar instance passed in the constructor may have a longer life than Foo using it. A signed event handler can then save Foo .

In this case, you need to provide a way to unsubscribe from the event so as not to get a memory leak. One way to do this is to let Foo implement IDisposable . The surface of this is that it clearly signals the class consumer that it needs to call Dispose () when it's done. Another way is to have separate Subscribe () and Unsubscribe () methods, but this does not convey type expectations - they are too optional to call and introduce a temporary connection.

My recommendation:

 class sealed Foo : IDisposable { readonly Bar _bar; bool _disposed; ... public void Dispose() { if (!_disposed) { _disposed = true; _bar.Changed -= BarChanged; } } ... } 

Or alternatively:

 class sealed Foo : IDisposable { Bar _bar; ... public void Dispose() { if (_bar != null) { _bar.Changed -= BarChanged; _bar = null; } } ... } 

On the other hand, if the referenced object is not available , then the leak cannot :

 class sealed Foo { Bar _bar; public Foo() { _bar = new Bar(); _bar.Changed += BarChanged; } void BarChanged(object sender, EventArgs e) { } } 

In this case, any instance of Foo will always outlive its configured instance of Bar . If Foo is not available, then there will be a Bar . A signed event handler cannot contain Foo here. The disadvantage of this is that if Bar is a dependency that needs to be mocked by unit test scripts, it cannot (in any pure way) be explicitly installed by the consumer, but must be introduced.

[1] http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

[2] http://en.wikipedia.org/wiki/Memory_leak

+31
Aug 26 2018-12-12T00:
source share

The event handler contains a strong reference to the object declaring the handler (in the delegate Target property).

Until the event handler is deleted (or until the object that owns the event is no longer referenced anywhere), the object containing the handler will not be assembled.

You can fix this by deleting the handler when you no longer need it (possibly in Dispose() ).
The finalizer cannot help, because the finalizer will only work after it has been assembled.

+6
Aug 26 '12 at 20:19
source share



All Articles