C#WPF应用程序使用太多内存而GC.GetTotalMemory()为低

我写了两个线程的WPF应用程序 – 主线程是GUI线程,另一个线程是worker。
应用程序有一个带有一些控件的WPF表单。 有一个按钮,允许选择目录。 选择目录后,应用程序将扫描该目录中的.jpg文件,并检查其缩略图是否在哈希表中。 如果它们是,它什么都不做。 否则它将他们的完整文件名添加到工作队列。
Worker正在从这个队列中获取文件名,加载JPEG图像(使用WPF的JpegBitmapDecoder和BitmapFrame),制作它们的缩略图(使用WPF的TransformedBitmap)并将它们添加到哈希表中。
一切正常,除了这个应用程序在为大图像(如5000×5000像素)制作缩略图时的内存消耗。 我在我的表单上添加了文本框以显示内存消耗(GC.GetTotalMemory()和Process.GetCurrentProcess()。PrivateMemorySize64)并且非常惊讶,因为GC.GetTotalMemory()保持接近1-2 MB,而私有内存大小不断增长,特别是在加载新图像时(每张图像约+ 100Mb)。
即使在加载所有图像,制作它们的缩略图并释放原始图像之后,私有内存大小仍保持在~700-800Mbytes。 我的VirtualBox仅限于512Mb的物理内存,VirtualBox中的Windows开始交换很多来处理这种巨大的内存消耗。 我想我做错了什么,但我不知道如何调查这个问题,根据GC的说法,分配的内存大小非常低。

附加缩略图加载器类的代码:

class ThumbnailLoader { Hashtable thumbnails; Queue taskqueue; EventWaitHandle wh; Thread[] workers; bool stop; object locker; int width, height, processed, added; public ThumbnailLoader() { int workercount,i; wh = new AutoResetEvent(false); thumbnails = new Hashtable(); taskqueue = new Queue(); stop = false; locker = new object(); width = height = 64; processed = added = 0; workercount = Environment.ProcessorCount; workers=new Thread[workercount]; for (i = 0; i  dy) scale = 1 / dx; else scale = 1 / dy; tb = new TransformedBitmap(oldbf, new ScaleTransform(scale, scale)); bf = BitmapFrame.Create(tb); fs.Close(); oldbf = null; bd = null; GC.Collect(); return bf; } public void Dispose() { lock(locker) { stop = true; } AddTask(null); foreach (Thread worker in workers) { worker.Join(); } wh.Close(); } private void Worker() { string curtask,hash; while (!stop) { curtask = NextTask(); if (curtask == null) wh.WaitOne(); else { if (curtask == "#resethash") thumbnails.Clear(); else { hash = FileNameToHash(curtask); try { thumbnails[hash] = LoadThumbnail(curtask); } catch { thumbnails[hash] = null; } } } } } } 

我怀疑BitmapCacheOption.OnLoad正在将图像添加到Framework的内存占用空间,但由于您不拥有图像缓存中的对象,因此它们不会出现在GC方法调用的结果中。 尝试使用BitmapCacheOption.None ,看看是否能解决您的内存问题。 注意:这样做会对性能产生巨大影响。

解决了这个问题。
只需用BitmapCacheOption.None替换BitmapCacheOption.OnLoad 🙂

我认为它与图像有关 – Image类的底层对象是非托管的,因为它们消耗的内存不包含在GC计数器中。

他们还需要特别注意你如何处理它们 – 它们的托管内存消耗非常低,因此GC并没有真正关注,但是无人管理的内存 – 你可以看到它。

底线 – 仅仅让它们超出范围是不够的,你必须在完成时明确地调用它们。