Утечки памяти в .NET

Я — «Папочка Разработки»: пишу о карьере .NET/C#, собесах и том, как продавать свои навыки дороже. Без воды: разборы утечек памяти, практические гайды по интервью, резюме с цифрами и честные кейсы с рынка. Плюс немного про ИИ — как делегировать ему рутину и ускорять работу. Заходи, если нужен понятный план действий, а не мотивационные лозунги.

утечки памяти.netсборщик мусора

Слышал различные мнения на этот счёт и то, что это всё не утечки и их в дотнете не существует, но давайте посмотрим на определение из вики:

Уте́чка па́мяти (англ. memory leak) — процесс неконтролируемого уменьшения объёма свободной оперативной или виртуальной памяти компьютера, связанный с ошибками в работающих программах, вовремя не освобождающих память от ненужных данных

Иначе говоря простыми словами, утечка памяти — это сценарий, когда память не освободилась, а должна была. Давайте прикинем как такое возможно в дотнете на примере трёх не самых очевидных вариантов🥰

1. Забытые подписки на события

Пока у события есть подписчики оно не будет очищено сборщиком мусора.

public class EventPublisher
{
    public event EventHandler SomeEvent;
    
    public void RaiseEvent()
    {
        SomeEvent?.Invoke();
    }
}

public class Subscriber
{
    public Subscriber(EventPublisher publisher)
    {
        // Подписываемся на событие
        publisher.SomeEvent += OnSomeEvent;
        // Но никогда не отписываемся!
    }
    
    private void OnSomeEvent(object sender, EventArgs e)
    {
        Console.WriteLine("Event received");
    }
}

2. Статические поля и коллекции

Статические поля живут на протяжении всего времени работы приложения. Если они содержат ссылки на большие объекты или продолжают расти, это может привести к утечкам:

public static class Cache
{
    // Эта коллекция будет только расти
    private static readonly Dictionary<string, object> _items = new Dictionary<string, object>();
    
    public static void Add(string key, object value)
    {
        _items[key] = value;
    }
    
    public static object Get(string key)
    {
        return _items.TryGetValue(key, out var value) ? value : null;
    }
}

3. Замыкания и захват переменных

Анонимные методы и лямбда-выражения могут захватывать переменные из внешней области видимости, что иногда приводит к неожиданным утечкам:

public class LambdaExample
{
    public Action CreateLongLivingAction()
    {
        // Этот массив будет жить до тех пор, пока жив возвращаемый делегат
        var largeArray = new byte[1000000];
        
        return () => 
        {
            Console.WriteLine(largeArray.Length);
        };
    }
}

Лайк, шер, ретвит и, может быть, когда-нибудь техника полностью меня одолеет 🌟

Дискуссия

Андрей
Еще стоит отнести неуправляемые ресурсы, у которых не вызван Dispose, т.к. новые объекты продолжат создаваться, а вот старые GC сам не уберет.
Aleksandr Alekseev
Андрей
Еще стоит отнести неуправляемые ресурсы, у которых не вызван Dispose, т.к. новые объекты продолжат создаваться, а вот старые GC сам не уберет.
Да, тоже вариант
Присоединиться к обсуждению →

Читайте так же