場景:大量圖片依序載入形成動畫(如工業檢測、醫療影像)。若不當處理 BitmapImage,會導致非託管記憶體累積、最終 OutOfMemoryException。
var src = new BitmapImage(new Uri(path, UriKind.Absolute));
image.Source = src;
簡單但無法控制快取與解碼時機,大量循環易爆記憶體。
var data = File.ReadAllBytes(path);
var ms = new MemoryStream(data);
var src = new BitmapImage();
src.BeginInit();
src.CacheOption = BitmapCacheOption.OnLoad; // 立即解碼,Stream 可立即關閉
src.StreamSource = ms;
src.EndInit();
src.Freeze(); // 讓 BitmapImage 可跨執行緒、不可變,利於 GC
ms.Close();
return src;
關鍵點:OnLoad 強制立即解碼像素資料到託管記憶體,Freeze() 釋放非託管資源參考。
適用於取得 byte[] 或 IntPtr 的高效能攝影機 SDK:
[DllImport("gdi32.dll", SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);
private BitmapSource ToBitmapSource(System.Drawing.Bitmap bmp)
{
try
{
var hBitmap = bmp.GetHbitmap();
var src = Imaging.CreateBitmapSourceFromHBitmap(
hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
DeleteObject(hBitmap); // 關鍵:釋放 GDI 物件
return src;
}
catch { return null; }
}
// 使用
var ms = new MemoryStream(data);
var gdiBmp = new System.Drawing.Bitmap(ms);
var src = ToBitmapSource(gdiBmp);
ms.Close();
gdiBmp.Dispose(); // 釋放 Managed Bitmap
return src;
| 方案 | 適用場景 | 記憶體控制 | 跨執行緒 |
|---|---|---|---|
| Uri 直接 | 少量、靜態圖片 | 弱 | 否 |
| Stream + OnLoad + Freeze | 大量循環、檔案載入 | 強 | 是 |
| GDI+ 中轉 + DeleteObject | 高解析度、攝影機 SDK、byte[] 來源 | 最強 | 是 |
GC.Collect() 只作最後手段,優先靠 Freeze、Dispose、DeleteObject 釋放非託管資源。WriteableBitmap 重複使用像素緩衝區。BitmapCacheOption.OnDemand 並手動 DecodePixelWidth/Height 限制解碼尺寸。