Ответы пользователя по тегу Mono
  • Нужно ли выполнять отписку от события, содержащемся в объекте, который будет собран GC?

    @rare
    Я попытался смоделировать вашу ситуацию и получилось, что не отписка не приводит к утечке памяти. Я использовал финализатор, чтобы отследить сборку мусора. В конце теста примерно 14% объектов не уничтожено, но вызов GC.Collect() приводит к их уничтожению. Я это интерпретирую так, что GC распознает эти объекты как мусор, просто еще не добрался до них. Это связано и с использованием кастомного финализатора: для уничтожения таких объектов сборщику мусора нужно больше времени. Подробнее про события 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; }
    }
    Ответ написан
    Комментировать