【问题标题】:How to free the memory after the BitmapImage is no longer needed?不再需要 BitmapImage 后如何释放内存?
【发布时间】:2011-12-02 06:58:29
【问题描述】:

首先,我将BitmapImage 加载到Image 控件中,该控件位于Window 上。 然后我使用Image 控件,然后关闭Window

我在一分钟内执行 2-3 次,我的内存很快就会填满,因为关闭窗口时由于某种原因图像没有从内存中卸载。

那么如何手动从Image.Source 中卸载BitmapImage 控件以释放RAM?

【问题讨论】:

    标签: wpf image memory bitmapimage


    【解决方案1】:

    我相信您正在寻找的解决方案是http://www.ridgesolutions.ie/index.php/2012/02/03/net-wpf-bitmapimage-file-locking/。就我而言,我试图找到一种在创建文件后删除文件的方法,但这似乎是解决这两个问题的方法。

    不释放内存:

    var bitmap = new BitmapImage(new Uri(imageFilePath));
    

    释放内存,并允许删除文件:

    var bitmap = new BitmapImage(); 
    var stream = File.OpenRead(imageFilePath);
    
    bitmap.BeginInit();
    bitmap.CacheOption = BitmapCacheOption.OnLoad;
    bitmap.StreamSource = stream;
    bitmap.EndInit();
    stream.Close();
    stream.Dispose();
    

    也可选择冻结 BitmapImage:

    bitmap.Freeze();
    

    【讨论】:

    • 为我工作,更改 bitmap.CacheOption 进一步减少提交的内存。顺便说一句,stream.Close 和 stream.Dispose 是重复的。
    • 请注意:我记得最初尝试将流放在 using 语句中,但内存继续快速增加。使用 stream.close 和 stream.dispose 效果更好。
    【解决方案2】:

    在我的情况下,位图缓存似乎是问题所在。我以前是这样加载位图的:

    Bitmap bitmap = new Bitmap();
    
    using(var stream = new FileStream(...))
    {
        bitmap.BeginInit();
        bitmap.CacheOption = BitmapCacheOption.OnLoad;
        bitmap.StreamSource = stream;
        bitmap.EndInit();
    }
    
    bitmap.Freeze();
    image.Source = bitmap;
    

    不断替换image.Source 就像刚刚建立内存一样,手动强制垃圾收集并没有真正的帮助。

    相反,禁用缓存并让它使用流(需要在显示图像之前保持流打开)与手动垃圾收集相结合,为我消除了内存积累。

    Stream mediaStream;
    
    void DisposeMediaStream()
    {
        if (mediaStream != null)
        {
            mediaStream.Close();
            mediaStream.Dispose();
            mediaStream = null;
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
        }
    }
    
    void Update()
    {
        DisposeMediaStream();
    
        var bitmap = new BitmapImage();
        mediaStream = new FileStream(...);
    
        bitmap.BeginInit();
        bitmap.CacheOption = BitmapCacheOption.None;
        bitmap.StreamSource = mediaStream;
        bitmap.EndInit();
    
        bitmap.Freeze();
        ControlImage.Source = bitmap;
    }
    

    这样我可以循环浏览大量图像(如 Windows 照片查看器)并且内存保持在低位。请注意,图像实际渲染后,流不必保持打开状态。

    【讨论】:

    • 我尝试了上面的代码,但必须将 BitmapCacheOption 设置为 OnLoad 否则我无法运行 Update() 以循环添加多个图像
    • 在设置 BitmapImage 的 StreamSource 时,始终同时设置 BitmapCacheOption.OnLoad 并在 EndInit 之后立即关闭流,例如 using (var fs = new FileStream(...)) { ... bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.StreamSource = fs; bitmap.EndInit(); }
    【解决方案3】:

    您可以将对象设置为null,这样就不再引用BitmapImage 对象。 在这种情况下,GC 应该负责释放资源。 您可以致电GC.Collect,但如果使用过于频繁,可能会影响性能。

    【讨论】:

    • GC.Collect() 不会释放资源,即使我对 BitmapImage 的引用无效。
    • 您的参考链看起来如何?是不是像Image->BitmapImage->MemoryStream->filestream->Actual bytes in memory 这样你就必须空引用正确的对象,否则你仍然会使用内存。
    • 看起来像这样:Image.Source = new BitmapImage(new Uri(link));
    • 似乎我需要使用 Image.BeginInit 和 .EndInit。
    【解决方案4】:

    您可以在窗口的Closed 事件中对图像调用Dispose()。我认为也可以使用不同的缓存选项来优化您的内存占用。

    编辑:

    您不能调用 Dispose(),而是可以考虑使用 BitmapCacheOption.None。图像将直接从磁盘读取,而不是缓存在内存中。

    【讨论】:

    • 我尝试使用new BitmapImage(new Uri(link), new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore));,但没有区别。内存仍在使用中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-30
    • 2013-05-15
    • 2012-10-10
    • 2012-06-11
    • 1970-01-01
    • 2012-01-25
    • 2014-08-05
    相关资源
    最近更新 更多