Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# BitmapImage被冻结后的处理_C#_Wpf_Image_Bitmapimage - Fatal编程技术网

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()。如果你叫它一次,它不会释放它,但下一轮得到它。我想这就是为什么我没有在我的应用程序中看到内存泄漏的原因;内存不会立即被清除,但下一个出现的内存会被清除。