← Назад к вопросам

Приведи пример утечки памяти в C#

1.0 Junior🔥 161 комментариев
#Память и Garbage Collector

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Утечки памяти в C#

Хотя C# имеет автоматическую сборку мусора (GC), утечки памяти полностью исключены быть не могут. Объект, на который есть сильная ссылка, не будет удален.

Пример 1: Невалидированные обработчики событий

Одна из самых частых причин — подписка на события без отписки:

public class DataProcessor
{
    private List<byte[]> _largeData = new();

    public DataProcessor(EventPublisher publisher)
    {
        // Подписываемся на событие
        publisher.OnDataReceived += HandleData;
        // ПРОБЛЕМА: никогда не отписываемся!
    }

    private void HandleData(byte[] data)
    {
        _largeData.Add(data);
    }
}

// Использование
var publisher = new EventPublisher();
while (true)
{
    var processor = new DataProcessor(publisher);
    // processor выходит из области видимости,
    // но остается в памяти, т.к. publisher удерживает на него ссылку
}

Решение:

public class DataProcessor : IDisposable
{
    private readonly EventPublisher _publisher;

    public DataProcessor(EventPublisher publisher)
    {
        _publisher = publisher;
        _publisher.OnDataReceived += HandleData;
    }

    public void Dispose()
    {
        _publisher.OnDataReceived -= HandleData;
    }

    private void HandleData(byte[] data) { }
}

// Использование
using (var processor = new DataProcessor(publisher))
{
    // работа
} // Здесь вызовется Dispose() и отписка произойдет

Пример 2: Циклические ссылки

public class Node
{
    public Node Parent { get; set; }
    public List<Node> Children { get; set; } = new();
}

var parent = new Node();
var child = new Node { Parent = parent };
parent.Children.Add(child);

// child -> parent -> child (циклическая ссылка)
// Хотя GC может это обнаружить, это неэффективно

Решение — использовать WeakReference:

public class Node
{
    private WeakReference<Node> _parentRef;
    public Node Parent
    {
        get => _parentRef?.TryGetTarget(out var target) == true ? target : null;
        set => _parentRef = new WeakReference<Node>(value);
    }
    public List<Node> Children { get; set; } = new();
}

Пример 3: Неуправляемые ресурсы

public class DatabaseConnection
{
    private IntPtr _nativeHandle; // неуправляемый ресурс

    public DatabaseConnection()
    {
        _nativeHandle = NativeDatabase.Open(); // выделяем ресурс
    }
    
    // ПРОБЛЕМА: забыли освободить ресурс
}

var conn = new DatabaseConnection(); // утечка!

Правильное решение:

public class DatabaseConnection : IDisposable
{
    private IntPtr _nativeHandle;
    private bool _disposed = false;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;
        
        if (disposing)
        {
            // Освобождаем управляемые ресурсы
        }
        
        // Освобождаем неуправляемые ресурсы
        if (_nativeHandle != IntPtr.Zero)
        {
            NativeDatabase.Close(_nativeHandle);
            _nativeHandle = IntPtr.Zero;
        }
        
        _disposed = true;
    }

    ~DatabaseConnection()
    {
        Dispose(false);
    }
}

Лучшие практики

  • Всегда отписывайтесь от событий в Dispose()
  • Используйте using statement для объектов, реализующих IDisposable
  • Минимизируйте жизненный цикл большихобъектов
  • Профилируйте приложение с помощью dotMemory или Visual Studio Profiler
  • Избегайте глобальных ссылок на объекты
  • Используйте WeakReference для кэшей и обработчиков
Приведи пример утечки памяти в C# | PrepBro