【问题标题】:OutOfMemoryException occurs on WriteableBitmap在 WriteableBitmap 上发生 OutOfMemoryException
【发布时间】:2019-02-02 10:05:20
【问题描述】:

我正在开发一个 Windows Phone 应用程序,该应用程序可用于将图像上传到 Web 服务器。我正在从我的设备中选择所有图像到一个 列表对象。我正在将所有位图图像一一转换为字节 []。

我的代码

public byte[] ConvertToBytes(BitmapImage bitmapImage)
    {
        byte[] data = null;
        WriteableBitmap wBitmap = null;

        using (MemoryStream stream = new MemoryStream())
        {
            wBitmap = new WriteableBitmap(bitmapImage);
            wBitmap.SaveJpeg(stream, wBitmap.PixelWidth, wBitmap.PixelHeight, 0, 100);
            stream.Seek(0, SeekOrigin.Begin);
            //data = stream.GetBuffer();
            data = stream.ToArray();
            DisposeImage(bitmapImage);
            return data;
        }

    }
    public void DisposeImage(BitmapImage image)
    {
        if (image != null)
        {
            try
            {
                using (MemoryStream ms = new MemoryStream(new byte[] { 0x0 }))
                {
                    image.SetSource(ms);
                }
            }
            catch (Exception ex)
            {
            }
        }
    }

位图到字节的转换

 using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
        {
            if (!store.DirectoryExists("ImagesZipFolder"))
            {
                //MediaImage mediaImage = new MediaImage();
                //mediaImage.ImageFile = decodeImage(new byte[imgStream[0].Length]);
                //lstImages.Items.Add(mediaImage);

                store.CreateDirectory("ImagesZipFolder");
                for (int i = 0; i < imgname.Count(); i++)
                {
                    using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(@"ImagesZipFolder\" + imgname[i], FileMode.CreateNew,store))
                    //using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(@"ImagesZipFolder\text.txt" , System.IO.FileMode.OpenOrCreate, store))                        
                    {
                       // byte[] bytes = new byte[imgStream[i].Length];
                        byte[] bytes = ConvertToBytes(ImgCollection[i]);
                        stream.Write(bytes, 0, bytes.Length);
                    }
                }
            }
            else {
                directory = true;
            }
          }

我的模拟器中有 91 张图片。当我将所有这些位图图像转换为 byte[] 时,wBitmap = new WriteableBitmap(bitmapImage);

行出现以下错误

System.Windows.ni.dll 中出现“System.OutOfMemoryException”类型的异常,但未在用户代码中处理

我可以做些什么来解决这个错误?

网络服务

如果我们向 Web 服务发送大文件,是否会出现类似

的错误

System.ServiceModel.ni.dll 中出现“System.OutOfMemoryException”类型的异常,但未在用户代码中处理

【问题讨论】:

  • 是否需要同时在内存中拥有所有字节数组?或许可以把图片转成字节,一张一张上传。
  • 是的,我一次想要它。我正在创建一个包含所有图像的 zip 文件
  • 那么您将 91 张图像加载到内存中?毫不奇怪,你用完了它。
  • GetBuffer 更要小心 - msdn:请注意,缓冲区包含可能未使用的已分配字节。例如,如果将字符串“test”写入 MemoryStream 对象,则从 GetBuffer 返回的缓冲区长度为 256,而不是 4,其中 252 个字节未使用。要仅获取缓冲区中的数据,请使用 ToArray 方法;但是,ToArray 会在内存中创建数据的副本。
  • 仅供参考,有些位图无法加载到中端 PC 的内存中。我的意思是单个位图。那就是他们可以消耗多少内存。你在移动设备上加载了一个 91 的数组-_-

标签: c# windows-phone-7 windows-phone-8 bitmapimage


【解决方案1】:

我可以做些什么来解决这个错误?

改变你做事的方式,使用更少的内存。

例如,不是转换每张图片然后上传它们,而是转换一张图片,上传它,转换下一张,等等。

如果你真的想一次处理所有的图片,你可以将字节数组存储在隔离存储中,而不是将它们保存在内存中,并在需要时将它们读回。

基本上,重新考虑您的流程并使用存储在给定时间使用更少的内存。

或者要求微软解除 Windows Phone 的内存限制,但这可能有点棘手。

【讨论】:

  • 感谢您的回复。我和你说的一样。我正在一张一张地传递图像并将其写入隔离存储中。
  • 嘿 KooKiz,我检查了这个链接 stackoverflow.com/questions/13355496/… 并在我的代码中添加了“DisposeImage”方法。我在返回“ConvertToBytes”方法之前调用此方法。但我收到此错误“找不到组件。(来自 HRESULT 的异常:0x88982F50)”
  • 您好,我在“using (MemoryStream ms = new MemoryStream(new byte[] { 0x0 }))”一行收到错误消息
  • @Ajay 然后是设计使然,try/catch 可以吞下这个异常。 BitmapImage 将继续消耗内存,直到加载新图片。此 hack 的目的是加载无效图片以强制 BitmapImage 释放以前使用的内存,但不要为新图片使用内存(因为它无效且无法加载)。附加调试器时(当您直接从 Visual Studio 启动应用程序时),该异常会减慢执行速度,但在没有 Visual Studio 的情况下运行时没有明显影响。
  • 也就是说,在 大多数 的情况下,只需将图像源 (image.Source = null;) 清零就足够了。除了在一些晦涩的情况下,例如在链接的问题中,即使将源归零也不足以强制运行时释放内存。
【解决方案2】:

问题在于 GC 和位图图像如何协同工作,如以下错误报告所述:https://connect.microsoft.com/VisualStudio/feedback/details/679802/catastrophic-failure-exception-thrown-after-loading-too-many-bitmapimage-objects-from-a-stream#details

来自报告:

当 Silverlight 加载图像时,框架会保留一个引用并 缓存解码后的图像,直到流控制返回给 UI 线程调度程序。当你像这样在一个紧密的循环中加载图像时, 即使您的应用程序不保留引用,GC 也不能 释放图像,直到我们释放我们的参考,当流量控制是 返回。

处理完 20 张左右的图像后,您可以停下来排队等待下一张 使用 Dispatcher.BeginInvoke 设置只是为了分解工作 一批处理。这将允许我们释放不属于 由您的应用程序保留。

我了解当前的解码行为并不明显 Silverlight 保留了这些参考,但更改了解码器 设计可能会影响其他领域,所以现在我建议处理 像这样的图像批量。

现在,如果您实际上是在尝试加载 500 张图片并保留它们,那么您 根据图像大小,仍然可能会耗尽内存。如果 您正在处理多页文档,您可能希望改为 在后台按需加载页面并在退出时释放它们 用几页缓冲区查看,这样你就不会超过 合理的纹理内存限制。

修复:

private List<BitmapImage> Images = .....;
private List<BitmapImage>.Enumerator iterator;
private List<byte[]> bytesData = new List<byte[]>();

public void ProcessImages()
{
    if(iterator == null)
        iterator = Images.GetEnumerator();

    if(iterator.MoveNext())
    {
        bytesData.Add(ConvertToBytes(iterator.Current));

        //load next images
        Dispatcher.BeginInvoke(() => ProcessImages());
    }else{
        //all images done
    }
}

public static byte[] ConvertToBytes(BitmapImage bitmapImage)
{
    using (MemoryStream stream = new MemoryStream())
    {
        var wBitmap = new WriteableBitmap(bitmapImage);
        wBitmap.SaveJpeg(stream, wBitmap.PixelWidth, wBitmap.PixelHeight, 0, 100);
        stream.Seek(0, SeekOrigin.Begin);
        return stream.ToArray();
    }
}

【讨论】:

  • 我已经尝试过这个解决方案,但我在同一行上遇到了同样的错误。 :(请检查编辑过的问题
【解决方案3】:

我找到了将位图图像转换为 byte[] 数组的新方法。无需将图像写入内存。 野兔是代码

 public byte[] GetBytes(BitmapImage bi)
    {
        WriteableBitmap wbm = new WriteableBitmap(bi);
        return ToByteArray(wbm);
    }
    public byte[] ToByteArray(WriteableBitmap bmp)
    {
        // Init buffer
        int w = bmp.PixelWidth;
        int h = bmp.PixelHeight;
        int[] p = bmp.Pixels;
        int len = p.Length;
        byte[] result = new byte[4 * w * h];

        // Copy pixels to buffer
        for (int i = 0, j = 0; i < len; i++, j += 4)
        {
            int color = p[i];
            result[j + 0] = (byte)(color >> 24); // A
            result[j + 1] = (byte)(color >> 16); // R
            result[j + 2] = (byte)(color >> 8);  // G
            result[j + 3] = (byte)(color);       // B
        }

        return result;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-20
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多