Answer the question
In order to leave comments, you need to log in
Do I need to unsubscribe from an event contained in an object that will be collected by the GC?
Good day, I have a small question.
There is a class, for example a certain Task. It contains the TaskComplete event that someone subscribed to. And there is a certain Ship class containing the CurrentTask property of the Task type (well, or a field, it doesn’t matter). Suppose the task is completed and CurrentTask is assigned to another instance of Task or null. It is logical that the old instance will be collected by the GC after some time.
It seems to be obvious that everything will be collected and there will be no problems from the fact that someone subscribed to that event (unlike the reverse operation, when the subscriber is deleted, of course). But just in case, I’ll clarify - do I understand correctly that it makes no sense to unsubscribe from an event that will soon be collected by the GC and this will not lead to a memory leak?
Answer the question
In order to leave comments, you need to log in
I tried to simulate your situation and it turned out that not unsubscribing does not lead to a memory leak. I used a finalizer to track the garbage collection. At the end of the test, approximately 14% of the objects are not destroyed, but calling GC.Collect() causes them to be destroyed. I interpret this to mean that the GC recognizes these objects as garbage, it just hasn't gotten to them yet. This is also due to the use of a custom finalizer: the garbage collector needs more time to destroy such objects. More about events https://habrahabr.ru/post/89529/
class Program
{
private static bool Unsubscribe = false;
static void Main(string[] args)
{
var ship = new Ship();
Enumerable.Range(0, 100000).ToList().ForEach(i =>
{
Console.Write($"\r{i + 1}\t");
ship.MyTask = new MyTask(i);
ship.MyTask.TaskCompleted += OnTaskCompleted;
ship.MyTask.DoAndRaise();
});
ship.MyTask = null;
Console.WriteLine();
Console.WriteLine("Raised: " + MyTask.EventsRaised);
Console.WriteLine("Collected: " + MyTask.Collected);
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Collected: " + MyTask.Collected);
}
static void OnTaskCompleted(object semder, int taskId)
{
if (Unsubscribe)
{
var mt = semder as MyTask;
mt.TaskCompleted -= OnTaskCompleted;
}
}
}
public class MyTask
{
public static int Collected, EventsRaised;
public MyTask(int id)
{
TaskId = id;
}
public int TaskId { get; }
public event EventHandler<int> TaskCompleted;
public void DoAndRaise()
{
TaskCompleted?.Invoke(this, TaskId);
Interlocked.Increment(ref EventsRaised);
}
~MyTask()
{
Interlocked.Increment(ref Collected);
}
}
public class Ship
{
public MyTask MyTask { get; set; }
}
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question