C# BitmapImage被冻结后的处理
我使用此功能创建了一个图像:C# BitmapImage被冻结后的处理,c#,wpf,image,bitmapimage,C#,Wpf,Image,Bitmapimage,我使用此功能创建了一个图像: private BitmapImage LoadImage(byte[] imageData) { if (imageData == null || imageData.Length == 0) return null; var image = new BitmapImage(); using (var mem = new MemoryStream(imageData)) { mem.Position = 0;
private BitmapImage LoadImage(byte[] imageData)
{
if (imageData == null || imageData.Length == 0) return null;
var image = new BitmapImage();
using (var mem = new MemoryStream(imageData))
{
mem.Position = 0;
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = mem;
image.EndInit();
}
image.Freeze();
return image;
}
当我试图处理它时:
myImage.StreamSource.Close();
myImage.StreamSource.Dispose();
// Throws an exception since its frozen to read only
//myImage.StreamSource = null;
GC.Collect();
它不是由垃圾收集器收集的。可能是因为我无法将其设置为null
如何处置此
位图图像
,使其在内存中的寿命更长?您已经处置了流源
,因为您在using语句中创建了MemoryStream
,该语句在离开代码块时进行处置。BitmapImage
本身仅受管理,不需要处理
你确定垃圾收集器没有把它清理干净吗?我有一个项目,它使用BitmapCacheOption.OnLoad制作了大量的BitmapImage
s,我从未见过内存泄漏
(更新)在WPF中测试:(Update2)需要添加另一轮垃圾收集。出于某种原因,必须调用它两次才能释放数组
private async void Button_Click(object sender, RoutedEventArgs e)
{
WeakReference test = this.TestThing();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Debug.WriteLine(test.IsAlive); // Returns false
}
private WeakReference TestThing()
{
byte[] imageData = File.ReadAllBytes(@"D:\docs\SpaceXLaunch_Shortt_3528.jpg");
var image = new BitmapImage();
using (var mem = new MemoryStream(imageData))
{
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = mem;
image.EndInit();
}
image.Freeze();
return new WeakReference(image);
}
我们可以使用以下代码调查该问题:
public static void Main()
{
var readAllBytes = File.ReadAllBytes(@"SomeBitmap.bmp");
var wr = new WeakReference(readAllBytes);
var result = LoadImage(readAllBytes);
readAllBytes = null;
//result.StreamSource = null;
result = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine($"IsAlive: {wr.IsAlive}");
Console.ReadLine();
}
private static BitmapImage LoadImage(byte[] imageData)
{
if (imageData == null || imageData.Length == 0) return null;
var image = new BitmapImage();
using (var mem = new MemoryStream(imageData))
{
mem.Position = 0;
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = mem;
image.EndInit();
}
image.Freeze();
return image;
}
我尝试了很多不同的缓存设置,我找不到释放字节数组的方法,它看起来像WPF中的一个bug
如您所见,在2次GC收集之后,字节数组被释放
编辑1:通过删除冻结简化BitmapImage
,并且Init
方法释放字节数组:
private static BitmapImage LoadImage(byte[] imageData)
{
if (imageData == null || imageData.Length == 0) return null;
var image = new BitmapImage();
using (var mem = new MemoryStream(imageData))
{
mem.Position = 0;
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.UriSource = null;
image.StreamSource = mem;
}
return image;
}
编辑2:正如其他人所指出的,字节数组是在2轮垃圾收集后释放的,我已经更新了上面的示例。我发现这真的很有用,这表明框架不是一个黑匣子。在我的例子中,我需要在服务器上存储为字节数组的一堆大型平面图之间切换。一旦所有这些都被加载到内存中,并且在它们之间进行了一些切换之后,我坐在3GB的位置上,开始出现加载任何东西的问题。图像本身的大小从600KB到几兆字节不等。当它们被画出来时,它们占据了更多的空间
我发现了一个包装流实现
这里是布拉德利·格兰杰(Bradley Grainger)
这使得我的内存使用率相当低;大部分时间小于2GB
WrapperStream MasterStream{get;set;}
private void ChangeImage()
{
SelectedImage = null;
GC.Collect();
var stream = new MemoryStream(ImageSource);
using (MasterStream = new WrappingStream(stream))
{
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = MasterStream;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
SelectedImage = bitmap;
}
GC.Collect();
}
我可以用这段代码确认字节数组没有被释放,在某个地方仍然有一个引用。您可以通过向其添加WeakReference
并检查IsAlive
属性来测试它。我在发布模式下执行此操作,没有附加调试程序。添加了一些测试代码。我不知道为什么在这种情况下,IsAlive
对你来说是true
。如果GC调用处于相同的函数和调试模式,它将返回true
,但是如果它处于相同的函数和发布模式,它仍然是false
。也许你可以分享你使用的确切代码?@Stuart我在跟踪传递到内存流中的字节数组时也会得到同样的结果。@Stuart我的错,我需要在垃圾收集真正工作之前再添加一轮垃圾收集。奇怪的是,如果我批量进行垃圾收集。它也不会发布,除非我做了另一个大量的图像。我删除了冻结,现在垃圾收集发生在我创建一个新的图像和EndInit()被调用时。谢谢@John,我已经验证了你的发现,并更新了我的答案,包括它=)创建一个全新的图像来强制垃圾收集器并不是一个理想的解决方案。需要弄清楚是什么调用了以前没有发生过的集合@斯图亚特,我知道有什么区别了。测试时,我碰巧在两个地方调用了GC.Collect()。如果你叫它一次,它不会释放它,但下一轮得到它。我想这就是为什么我没有在我的应用程序中看到内存泄漏的原因;内存不会立即被清除,但下一个出现的内存会被清除。