← Назад к вопросам
Приведи пример утечки памяти в 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 для кэшей и обработчиков