Проблемы утечки памяти: утилизировать или не утилизировать управляемые ресурсы?
Я испытываю странную утечку памяти в дорогостоящем приложении.NET для поиска изображений на основе контента (CBIR).
Концепция заключается в том, что существует класс обслуживания с циклом потока, который захватывает изображения из некоторого источника и затем передает их в поток тегов изображения для аннотации.
Теги изображений запрашиваются из хранилища классом обслуживания через определенные промежутки времени и сохраняются в его кэш-памяти (словаре), чтобы избежать частых обращений в дб.
Классы в проекте:
class Tag
{
public Guid Id { get; set; } // tag id
public string Name { get; set; } // tag name: e.g. 'sky','forest','road',...
public byte[] Jpeg { get; set; } // tag jpeg image patch sample
}
class IRepository
{
public IEnumerable<Tag> FindAll();
}
class Service
{
private IDictionary<Guid, Tag> Cache { get; set; } // to avoid frequent db reads
// image capture background worker (ICBW)
// image annotation background worker (IABW)
}
class Image
{
public byte[] Jpeg { get; set; }
public IEnumerable<Tag> Tags { get; set; }
}
Работник ICBW захватывает изображение в формате JPEG из некоторого источника изображения и передает его работнику IABW для аннотации. Работник IABW сначала пытается обновить Cache, если пришло время, а затем аннотирует изображение с помощью некоторого алгоритма, создавая объект Image и прикрепляя к нему теги, а затем сохраняя его в хранилище аннотаций.
Фрагмент обновления кэша службы в IABW работнике:
IEnumerable<Tag> tags = repository.FindAll();
Cache.Clear();
tags.ForEach(t => Cache.Add(t.Id, t));
IABW вызывается много раз в секунду и довольно обширный процессор.
Во время работы в течение нескольких дней я обнаружил увеличение памяти в диспетчере задач. Используя Perfmon для наблюдения за процессными / приватными байтами и.NET-памятью / байтами во всех кучах, я обнаружил, что они оба увеличиваются с течением времени.
Экспериментируя с приложением, я обнаружил, что проблема заключается в обновлении кэша. Если он не обновляется, проблем с увеличением памяти нет. Но если обновление Cache происходит так часто, как раз в 1-5 минут, приложение довольно быстро выходит из строя.
Что может быть причиной этой утечки памяти? Объекты изображений создаются довольно часто, содержащие ссылки на объекты Tag в Cache. Я предполагаю, что при создании словаря Cache эти ссылки как-то не будут собираться в будущем.
Нужно ли явно обнулять управляемые объекты byte[], чтобы избежать утечки памяти, например, путем реализации Tag, Image as IDisposable
?
Редактирование: 4 августа 2001, добавление фрагмента кода с ошибкой, вызывающего быструю утечку памяти.
static void Main(string[] args)
{
while (!Console.KeyAvailable)
{
IEnumerable<byte[]> data = CreateEnumeration(100);
PinEntries(data);
Thread.Sleep(900);
Console.Write(String.Format("gc mem: {0}\r", GC.GetTotalMemory(true)));
}
}
static IEnumerable<byte[]> CreateEnumeration(int size)
{
Random random = new Random();
IList<byte[]> data = new List<byte[]>();
for (int i = 0; i < size; i++)
{
byte[] vector = new byte[12345];
random.NextBytes(vector);
data.Add(vector);
}
return data;
}
static void PinEntries(IEnumerable<byte[]> data)
{
var handles = data.Select(d => GCHandle.Alloc(d, GCHandleType.Pinned));
var ptrs = handles.Select(h => h.AddrOfPinnedObject());
IntPtr[] dataPtrs = ptrs.ToArray();
Thread.Sleep(100); // unmanaged function call taking byte** data
handles.ToList().ForEach(h => h.Free());
}
1 ответ
Нет, вам не нужно ничего устанавливать на ноль или распоряжаться чем-либо, если это просто память, как вы показали.
Я предлагаю вам приобрести хороший профилировщик, чтобы выяснить, где утечка. Есть ли у вас что-либо не связанное с памятью, которое вы, возможно, не смогли бы утилизировать, например, загрузка изображения GDI+ для получения байтов?