C# 图片加载引发的内存溢出异常
在c#中,使用下面代码将图片读取到内存,发现内存暴涨。
public static System.Windows.Media.Imaging.BitmapImage ByteArrayToBitmapImage(this byte[] array)
{
using (var ms = new System.IO.MemoryStream(array))
{
try
{
var image = new System.Windows.Media.Imaging.BitmapImage();
image.BeginInit();
image.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad; // here
image.StreamSource = ms;
image.EndInit();
image.Freeze();
return image;
}
catch (Exception ex)
{
LogHelper.Error(ex);
}
return null;
}
}
使用托管堆内存查看工具,发现托管堆增长的大小和图片物理空间基本一致;那么暴涨出来的那部分大小在哪儿呢?大概率是开辟了非托管内存!
图片加载到内存会占用多少内存?
答案:不一定,因为需要看你的处理方式。如上面的代码,并未进行额外处理,系统会进行下面的处理:
- 将300kb的数据读入内存
- 以原始分辨率进行解码,解码后会变成未压缩的位图,公式为:宽度×高度×每像素字节数 。举个例子:
假如你的图片分辨率为 1920*1080 ;那么解码后占用内存可能为 1920*1080*4 ≈ 7.9MB;在bitmapiamge内部开辟的是非托管内存。故用托管内存分析工具还看不到这段内存。 - 如果这个图片用于WPF的Image控件展示,还会有额外的内存开支。
如何正确处理图片以应对内存占用?
设想在这些场景中:展示头像,展示在列表行中...,图片真的要用非压缩的形式展示给用户么?
答案是:不需要,而是要设置解码宽度,拿WPF的Image控件来说,解码宽度设置为你的Image的真实宽度--ActualWidth。
// 在你的方法中添加信息输出
public static BitmapImage ByteArrayToBitmapImage(this byte[] array, int iPicWid = 0 )
{
using (var ms = new MemoryStream(array))
{
try
{
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = ms;
// 设置解码宽度 避免以无损方式解码
if (iPicWid > 0) image.DecodePixelWidth = iPicWid ;
image.EndInit();
image.Freeze();
// 调试信息
#if DEBUG
PrintImageInfo(image);
#endif
return image;
}
catch (Exception ex)
{
LogHelper.Error(ex);
return null;
}
}
}
// 在调试时输出详细信息
public static void PrintImageInfo(BitmapImage image)
{
Console.WriteLine("=== 图像信息 ===");
Console.WriteLine($"宽度: {image.PixelWidth} 像素");
Console.WriteLine($"高度: {image.PixelHeight} 像素");
Console.WriteLine($"格式: {image.Format}");
Console.WriteLine($"每像素位数: {image.Format.BitsPerPixel}");
int bytesPerPixel = (image.Format.BitsPerPixel + 7) / 8;
Console.WriteLine($"每像素字节数: {bytesPerPixel}");
long totalBytes = image.PixelWidth * image.PixelHeight * bytesPerPixel;
Console.WriteLine($"总内存占用: {totalBytes} 字节 ({totalBytes / 1024.0 / 1024.0:F2} MB)");
// 如果是文件加载的,还可以显示原始文件大小
if (image.StreamSource != null)
{
Console.WriteLine($"压缩格式: 已压缩");
}
}
我拿了一个300kb大小的图片,不设置解码宽度的情况下,打印了理论内存占用,代码如下:
ByteArrayToBitmapImage(buffer);
输出信息如下:
=== 图像信息 ===
宽度: 2560 像素
高度: 1440 像素
格式: Bgr32
每像素位数: 32
每像素字节数: 4
总内存占用: 14745600 字节 (14.06 MB)
压缩格式: 已压缩
参考
WPF 解决Image控件读取高分辨率图片并缩放占用内存过大
How to: Use a BitmapImage
WPF的BitmapImage的文件无法释放及内存泄露的问题

浙公网安备 33010602011771号