内存泄漏问题:处置或不处置托管资源

本文关键字:资源 泄漏 内存 问题 | 更新日期: 2023-09-27 18:04:18

我在计算昂贵的基于内容的图像检索(CBIR) .NET应用程序中遇到奇怪的内存泄漏

这个概念是,有一个带有线程循环的服务类,它从某些源捕获图像,然后将它们传递给图像标记线程进行注释。

服务类以指定的时间间隔从存储库查询图像标签,并将其存储在内存缓存(字典)中,以避免频繁的db命中。

项目中的类有:

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; }
}

IABW worker从某些图像源捕获jpeg图像并将其传递给IABW worker进行注释。如果时间到了,IABW worker首先尝试更新缓存,然后通过一些算法创建图像对象并为其附加标签,然后将其存储到注释库中。

IABW worker中的服务缓存更新片段为:

IEnumerable<Tag> tags = repository.FindAll();
Cache.Clear();
tags.ForEach(t => Cache.Add(t.Id, t));

IABW在一秒钟内被调用多次,并且是相当广泛的处理器。

在运行了几天后,我发现任务管理器的内存增加了。使用Perfmon来观察所有堆中的进程/私有字节和。net内存/字节,我发现它们都随着时间的推移而增加。

试验应用程序,我发现缓存更新是问题所在。如果没有更新,则memm增加没有问题。但是如果缓存更新的频率是1-5分钟一次,那么应用程序就会很快从内存中退出。

内存泄漏的原因是什么?图像对象通常包含对缓存中标签对象的引用。我认为当缓存字典被创建时,这些引用在将来不会以某种方式被垃圾收集。

是否需要显式null托管byte[]对象来避免内存泄漏,例如通过实现Tag, Image作为IDisposable ?

编辑:2001年8月4日,增加了导致内存快速泄漏的错误代码片段。

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());
}

内存泄漏问题:处置或不处置托管资源

不,你不需要设置任何东西为null或处置任何东西,如果它只是内存,正如你所示。

我建议你找一个好的分析器来找出漏洞在哪里。你是否有任何非内存相关的东西,你可能无法处理,例如加载GDI+图像来获得字节?