Avoiding memory leaks with events - c#

class Subscriber
{
private Publisher _publisher;
void SomeMethod()
{
_publisher = new Publisher();
_publisher.SomeEvent += HandleEvent;
}
void HandleEvent(object sender, EventArgs e)
{
}
}
class Publisher
{
public event EventHandler SomeEvent;
void FireEvent()
{
SomeEvent?.Invoke(this, EventArgs.Empty);
}
}
Do I need to detach HandleEvent from SomeEvent to avoid memory leak?
Subscriber lives longer than Publisher, so my understanding is that when Publisher gets disposed, it will also clear all event handlers from SomeEvent, so there shouldn't be any references left. Am I right?

Subscriber lives longer than Publisher
That means you are OK, no need to complicate things with an unsubscribe.
The statement _publisher.SomeEvent += HandleEvent; creates a (somewhat hidden) reference from _publisher to its owner. That would prevent the owning Subscriber from being collected, but only when the publisher outlives it.
Since _publisher is private, the cleanup is implicit. You do not have to (should
not) add IDisposable here. That is, not for managing the events.
And since you tagged this WinForms: all those eventhandlers (eg Button1_Click) all create references from a Control to the owning Form, no need for cleanup there either.

Related

Event handler affects in garbage collection in CLR

I'm completely confused about how event handler can affect in garbage collection operation.
For example, why object a1 not collected by garbage collection ( the destructor of a1 is not calling):
Even after unsubscribing timeChange eventHandler the destructor isn't called by garbage collector.
Best regards.
public class B
{
private void button1_Click(object sender, EventArgs e)
{
A a1 = new A();
a1.timeChange += A1_timeChange;
a1.Start();
a1 = null;
GC.Collect();
}
private void A1_timeChange(object sender, EventArgs e)
{
MessageBox.Show(((DateTime)sender).ToString() );
}
}
public class A
{
~A()
{
MessageBox.Show("A Collected");
}
public void Start()
{
if (timeChange != null)
{
Task.Factory.StartNew(() => {
while (true)
{
timeChange(DateTime.Now, null);
System.Threading.Thread.Sleep(3000);
}
});
}
}
public event EventHandler timeChange;
}
In summary
It is not the event itself that is causing this, but rather referencing an instance member of class A from a long running thread using a closure.
The event itself is not the issue, or is it?
This code a1.timeChange += A1_timeChange; causes a delegate inside class A that implements the event public event EventHandler timeChange to reference A1_timeChange inside class B.
So, the reference is the other way around
In your scenario, if you got rid of all references to class B, but did not unsubscribe from the event, then the event handler in A that points to a handler method in B could keep class B reachable, and therefore not GC'ed
What is really happening
Your class A is accessible from one of the GC roots (specifically, actively running threads), it's still reachable and therefore, not collected - read more
You have spawned an eternally running task (well it will stop when the app closes / foreground thread terminates) with this code
Task.Factory.StartNew(() => {
while (true)
{
timeChange(DateTime.Now, null);
System.Threading.Thread.Sleep(3000);
}
The thing is, that you're using a lambda that closes over the timeChange event, and that makes a compiler to generate a class that simply references class A
One more thing, just by having a descructor (which is compiled to a finalizer) you prolong the lifetime of your object by one GC collection - on the first GC, your object will be marked as unreachable, and will be put onto finalization queue. And on the next GC, the finalizer will actually run - read more

Reference lost but still not in a undefined state? [duplicate]

Let's say we have the following setup:
public class ClassA
{
public event EventHandler SomeEvent;
}
public class ClassB : IDisposable
{
public void SomeMethod(ClassA value)
{
value.SomeEvent += (s, e) => { DoSomething(); };
}
void DoSomething() { }
void Dispose() { }
}
public static class Program
{
static void Main()
{
var a = new ClassA();
using (var b = new ClassB())
b.SomeMethod(a);
// POINT OF QUESTION!!
}
}
What happens when the event SomeEvent is raised after the "POINT OF QUESTION"?
It will call method of disposed object. That's why it is important to unsubscribe. It even can lead to memory leaks.
You should use the Dispose() method of ClassB to unsubscribe from the ClassA event. You run the risk of classes not being garbage collected which of course leads to potential memory leaks. You should always unsub from events.
Nothing you have above would unhook your event handler. Since both a and b go out of scope at the same time, you'd be safe. Since a and b can both be collected, then a will not keep b alive.
If you were to raise the ClassA.SomeEvent event after your using statement, then ClassB.DoSomething will be called even though b has been disposed. You would have to explicitly remove the event handler in this case.
If you were to retain a reference to a somewhere else, then b would be keep alive by a. Again, because the event handler has not been removed.
Nothing. Runtime doesn't know about Dispose method and call to this method inside using statement do nothing to memory management.
Garbage collection is all about memory management, but IDisposable (and Finalizer) is about resource management, so you still should think yourself and implement it manually (and propertly, for example unsubscribe in Dispose).

Difference in EventHandler syntax on new event (non generic)

I'm trying to wrap my head around this problem. The below code ultimately merely counts the number of garbage collected objects of type SubscribersClass. When the code runs as shown, I get a SubscribersClass.Count value of 0. When I comment out the first line in EventsClass and uncomment the remainder of that class, the value of SubscribersClass.Count is 10.
The only thing I can come up with is that because there is something wrong with the EventsClass EventHandler (as shown), then no instances of SubscribersClass are actually being created.
Was hoping someone could help me to understand what is happening exactly.
This is an academic and of no practical value. Just tying to figure it out but have thus far only managed to get GoogleBlisters.
namespace understandingEvents
{
public class EventsClass
{
public event EventHandler SomeEvent; // if this is commented out and
// remainder of class is uncommented
// it works fine
/*
public event EventHandler SomeEvent
{
add
{
Console.WriteLine("An event has been added");
}
remove
{
Console.WriteLine("An event has been removed");
}
}
*/
}
public class SubscribersClass
{
static int count = 0;
static public int Count
{
get { return count; }
}
public SubscribersClass (EventsClass eventPublisher)
{
eventPublisher.SomeEvent += new EventHandler(Subscriber_SomeEvent);
}
~SubscribersClass()
{
Interlocked.Increment(ref count);
}
public void Subscriber_SomeEvent(object sender, EventArgs e)
{
Console.WriteLine("This is an event");
}
}
class Program
{
static void Main(string[] args)
{
EventsClass publisher = new EventsClass();
for (int i = 0; i < 10; i++)
{
SubscribersClass subscriber = new SubscribersClass(publisher);
subscriber = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(SubscribersClass.Count.ToString());
}
}
}
When you use a standard, compiler implemented event (public event EventHandler SomeEvent;), subscribing to the event causes a reference to the subscriber to be kept in the event, since the delegate refers to an instance method of the SubscribersClass.
This means that publisher is still holding references to each and every subscriber, so they are never released.
When you write your own add and remove handlers, you're not actually using the delegate, so the delegate is ignored (you'll find that raising the event has no effect and isn't handled by the subscribers in that case), which also means that those references aren't held, so the GC.Collect call can "clean up" the subscribers. This causes the count to increment.
When you subscribe to an event, the publisher will hold a reference to the subscriber. This is what allows the publisher to call all the methods that were subscribed to the event when the event takes place.
To solve your issue, simply remove the subscription before destroying the object. ( You'll need to store a reference to the publisher of the event). You can do this by manually calling some Unsubscribe method or (better) implement IDisposable and unsubscribe in Dispose().
public SubscribersClass (EventsClass eventPublisher)
{
m_eventPublisher = eventPublisher;
eventPublisher.SomeEvent += new EventHandler(Subscriber_SomeEvent);
}
public override void Dispose()
{
m_eventPublisher.SomeEvent -= Subscriber_SomeEvent;
}
You are not removing event susbscribers in your test code, as result the SubscribersClass instances are not getting collected.
Commented out code does not at all add listeners, so all instances of SubscribersClass are ready for GC as soon as they are created.
Note that code (when properly fixed) will also behave differently in DEBUG build due to extended lifetime of all variables. Consider putting all interesting code in a function and triggering GC outside it.

Do I need to unsubscribe from an event in a c# metro application?

I have subscribed various event in OnNavigatedTo like
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Loaded += Screen_Loaded;
}
I haven't unsubscribed this event. Does it cause any memory issue when this page is not needed??
No. In this case you do not need to unsubscribe to avoid memory leaks. The reason is that you subscribe to an event on this. The garbage collector must identify this and free the object.
But, I would for other reasons still unsubscribe. For example having balanced resources makes the code easier to read. Also what if the OnNavigatedTo gets called twice? (Don't actually know if this can even happen) Then you'll have two subscriptions to the same method. Some would argue that unsubscribing in this scenario is redundant code and remove it. Although correct as such I would oppose such arguments.
You can try this short snippet to try it out yourself. Note: Never user finalizers or GC.Collect() except for learning about the GC like in this example.
public class Program
{
private class Foo
{
public event EventHandler FooChanged;
~Foo()
{
Console.WriteLine("Foo was collected");
}
public void Bar()
{
FooChanged += UpdateUI;
}
private void UpdateUI(object sender, EventArgs e)
{
}
}
public static void Main(string[] args)
{
var foo = new Foo();
foo.Bar();
foo = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("End of program");
Console.ReadKey();
}
}
Yes you do have to unsubscribe from some event which might be automatically fired in a metro app
For Example :
Events such as
Window.Current.SizeChanged += Current_SizeChanged;
void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
//your code block contining various objects and logic
}
These events are not controlled by you as they are fire in the background. Any event which might not be related to a particular page (suppose the above event is initilized in the OnNavigatedTo Event) then you have to unsubscribe from it in events like OnNavigatedFrom
For further clarification initialize this event
Window.Current.SizeChanged += Current_SizeChanged;
and just keep a break point and change the size of the window change from landscape to snapped mode and this event will fire unless you have not unsubscribed from the event.
Remember that:
You could accidentally subscribe to event several times (use -= to avoid this).
If event handler is a method from some other object, then that object will not be garbage collected until method is subscribed to an event.

C#: Thread-safe events

Is the implementation below thread-safe? If not what am I missing? Should I have the volatile keywords somewhere? Or a lock somewhere in the OnProcessingCompleted method? If so, where?
public abstract class ProcessBase : IProcess
{
private readonly object completedEventLock = new object();
private event EventHandler<ProcessCompletedEventArgs> ProcessCompleted;
event EventHandler<ProcessCompletedEventArgs> IProcess.ProcessCompleted
{
add
{
lock (completedEventLock)
ProcessCompleted += value;
}
remove
{
lock (completedEventLock)
ProcessCompleted -= value;
}
}
protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
{
EventHandler<ProcessCompletedEventArgs> handler = ProcessCompleted;
if (handler != null)
handler(this, e);
}
}
Note: The reason why I have private event and explicit interface stuff, is because it is an abstract base class. And the classes that inherit from it shouldn't do anything with that event directly. Added the class wrapper so that it is more clear =)
You need to lock when you fetch the handler too, otherwise you may not have the latest value:
protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
{
EventHandler<ProcessCompletedEventArgs> handler;
lock (completedEventLock)
{
handler = ProcessCompleted;
}
if (handler != null)
handler(this, e);
}
Note that this doesn't prevent a race condition where we've decided we're going to execute a set of handlers and then one handler unsubscribed. It will still be called, because we've fetched the multicast delegate containing it into the handler variable.
There's not a lot you can do about this, other than making the handler itself aware that it shouldn't be called any more.
It's arguably better to just not try to make the events thread-safe - specify that the subscription should only change in the thread which will raise the event.
There is no need for the private ProcessCompleted member to be an event - it could just be a field: private EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - inside the class it always goes straight to the field, so the event stuff is lost anyway.
The approach you've shown with an explicit lock object isn't much more thread-safe than just having a field-like event (i.e. public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - the only difference is that you aren't locking "this" (which is a good thing - you should ideally avoid locking on this).. The "handler variable" approach is the right one, but there are still side-effects you should be aware of.

Categories

Resources