我寫了2個線程的小WPF應用程序 - 主線程是GUI線程和另一個線程是工人。
應用程序有一個WPF窗體和一些控件。有一個按鈕,允許選擇目錄。 選擇目錄後,應用程序掃描該目錄中的.jpg文件並檢查其縮略圖是否在散列表中。如果是的話,它什麼都不做。否則它會將他們的完整文件名添加到工作隊列中。
工作人員正在從這個隊列中取文件名,加載JPEG圖像(使用WPF的JpegBitmapDecoder和BitmapFrame),製作它們的縮略圖(使用WPF的TransformedBitmap)並將它們添加到散列表。
一切工作正常,除了這個應用程序在製作大圖像(如5000x5000像素)的縮略圖時消耗的內存。我在表單上添加了文本框來顯示內存消耗(GC.GetTotalMemory()和Process.GetCurrentProcess()。PrivateMemorySize64),並且非常驚訝,cuz GC.GetTotalMemory()保持接近1-2 Mbytes,而私有內存大小不斷增長,特別是在加載新圖像時(每幅圖像〜100Mb)。
即使加載完所有圖像後,製作它們的縮略圖並釋放原始圖像,私有內存大小仍保留在〜700-800M字節。我的VirtualBox限於512Mb的物理內存,而VirtualBox中的Windows開始交換很多來處理這種巨大的內存消耗。我想我做錯了什麼,但我不知道如何調查這個問題,因爲GC根據GC分配的內存大小非常低。C#WPF應用程序使用太多的內存,而GC.GetTotalMemory()是低
附加縮略圖加載類的代碼:
class ThumbnailLoader
{
Hashtable thumbnails;
Queue<string> 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<string>();
stop = false;
locker = new object();
width = height = 64;
processed = added = 0;
workercount = Environment.ProcessorCount;
workers=new Thread[workercount];
for (i = 0; i < workercount; i++) {
workers[i] = new Thread(Worker);
workers[i].IsBackground = true;
workers[i].Priority = ThreadPriority.Highest;
workers[i].Start();
}
}
public void SetThumbnailSize(int twidth, int theight)
{
width = twidth;
height = theight;
if (thumbnails.Count!=0) AddTask("#resethash");
}
public void GetProgress(out int Added, out int Processed)
{
Added = added;
Processed = processed;
}
private void AddTask(string filename)
{
lock(locker) {
taskqueue.Enqueue(filename);
wh.Set();
added++;
}
}
private string NextTask()
{
lock(locker) {
if (taskqueue.Count == 0) return null;
else {
processed++;
return taskqueue.Dequeue();
}
}
}
public static string FileNameToHash(string s)
{
return FormsAuthentication.HashPasswordForStoringInConfigFile(s, "MD5");
}
public bool GetThumbnail(string filename,out BitmapFrame thumbnail)
{
string hash;
hash = FileNameToHash(filename);
if (thumbnails.ContainsKey(hash)) {
thumbnail=(BitmapFrame)thumbnails[hash];
return true;
}
AddTask(filename);
thumbnail = null;
return false;
}
private BitmapFrame LoadThumbnail(string filename)
{
FileStream fs;
JpegBitmapDecoder bd;
BitmapFrame oldbf, bf;
TransformedBitmap tb;
double scale, dx, dy;
fs = new FileStream(filename, FileMode.Open);
bd = new JpegBitmapDecoder(fs, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
oldbf = bd.Frames[0];
dx = (double)oldbf.Width/width;
dy = (double)oldbf.Height/height;
if (dx > 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;
}
}
}
}
}
}
聽起來像大型對象堆中正在發生的事情...當你處理圖像時,你能隨着時間的推移獲得性能指標嗎? LOH沒有采用與標準內存相同的方式,並且(如果我沒有記錯的話)更容易發生碎片... – LorenVS 2010-03-31 23:56:47
這可能是WPF中的一個錯誤,它存在一些非託管內存的已知問題。我建議你在Connect(http://connect.microsoft.com/)上報告。如果內存泄漏對您的應用程序有問題,並且您無法修復,則可以將代碼的關鍵部分移至另一個進程或AppDomain。 – 2010-03-31 23:59:40