创建多个字节数组时出现OutOfMemoryException

时间:2012-03-27 12:54:38

标签: c# .net out-of-memory pdfsharp

我经常在创建和处理某些字节数组的方法中遇到OutOfMemoryException。代码如下所示:

  1. 创建MemoryStream以获取一些数据(约60MB)。
  2. 创建字节数组(与MemoryStream大小相同,大约60MB)
  3. 使用内存流中的字节填充数组
  4. 关闭MemoryStream
  5. 来自字节数组的处理数据
  6. 离开方法
  7. 当调用此方法20-30次时,我会在分配字节数组的位置获得OutOfMemoryException。但我不认为这是系统内存问题。应用程序内存使用量约为500MB(专用工作集),测试机器为64位,内存为4GB。

    在方法完成后,字节数组或MemoryStream使用的内存是否可能未释放?但是,由于私有工作集只有500MB左右,因此看起来这个内存不会分配给该进程。

    在物理内存不足的情况下创建大字节数组(60MB)时可能导致OutOfMemoryException的原因是什么?

    [已编辑添加代码示例] 来自PdfSharp lib

    在行byte[] imageBits = new byte[streamLength];抛出异常它确实看起来像LOH碎片问题。

    /// <summary>
    /// Reads images that are returned from GDI+ without color palette.
    /// </summary>
    /// <param name="components">4 (32bpp RGB), 3 (24bpp RGB, 32bpp ARGB)</param>
    /// <param name="bits">8</param>
    /// <param name="hasAlpha">true (ARGB), false (RGB)</param>
    private void ReadTrueColorMemoryBitmap(int components, int bits, bool hasAlpha)
    {
      int pdfVersion = Owner.Version;
      MemoryStream memory = new MemoryStream();
      image.gdiImage.Save(memory, ImageFormat.Bmp);
      int streamLength = (int)memory.Length;
    
      if (streamLength > 0)
      {
        byte[] imageBits = new byte[streamLength];
        memory.Seek(0, SeekOrigin.Begin);
        memory.Read(imageBits, 0, streamLength);
        memory.Close();
    
        int height = image.PixelHeight;
        int width = image.PixelWidth;
    
        if (ReadWord(imageBits, 0) != 0x4d42 || // "BM"
            ReadDWord(imageBits, 2) != streamLength ||
            ReadDWord(imageBits, 14) != 40 || // sizeof BITMAPINFOHEADER
            ReadDWord(imageBits, 18) != width ||
            ReadDWord(imageBits, 22) != height)
        {
          throw new NotImplementedException("ReadTrueColorMemoryBitmap: unsupported format");
        }
        if (ReadWord(imageBits, 26) != 1 ||
          (!hasAlpha && ReadWord(imageBits, 28) != components * bits ||
           hasAlpha && ReadWord(imageBits, 28) != (components + 1) * bits) ||
          ReadDWord(imageBits, 30) != 0)
        {
          throw new NotImplementedException("ReadTrueColorMemoryBitmap: unsupported format #2");
        }
    
        int nFileOffset = ReadDWord(imageBits, 10);
        int logicalComponents = components;
        if (components == 4)
          logicalComponents = 3;
    
        byte[] imageData = new byte[components * width * height];
    
        bool hasMask = false;
        bool hasAlphaMask = false;
        byte[] alphaMask = hasAlpha ? new byte[width * height] : null;
        MonochromeMask mask = hasAlpha ?
          new MonochromeMask(width, height) : null;
    
        int nOffsetRead = 0;
        if (logicalComponents == 3)
        {
          for (int y = 0; y < height; ++y)
          {
            int nOffsetWrite = 3 * (height - 1 - y) * width;
            int nOffsetWriteAlpha = 0;
            if (hasAlpha)
            {
              mask.StartLine(y);
              nOffsetWriteAlpha = (height - 1 - y) * width;
            }
    
            for (int x = 0; x < width; ++x)
            {
              imageData[nOffsetWrite] = imageBits[nFileOffset + nOffsetRead + 2];
              imageData[nOffsetWrite + 1] = imageBits[nFileOffset + nOffsetRead + 1];
              imageData[nOffsetWrite + 2] = imageBits[nFileOffset + nOffsetRead];
              if (hasAlpha)
              {
                mask.AddPel(imageBits[nFileOffset + nOffsetRead + 3]);
                alphaMask[nOffsetWriteAlpha] = imageBits[nFileOffset + nOffsetRead + 3];
                if (!hasMask || !hasAlphaMask)
                {
                  if (imageBits[nFileOffset + nOffsetRead + 3] != 255)
                  {
                    hasMask = true;
                    if (imageBits[nFileOffset + nOffsetRead + 3] != 0)
                      hasAlphaMask = true;
                  }
                }
                ++nOffsetWriteAlpha;
              }
              nOffsetRead += hasAlpha ? 4 : components;
              nOffsetWrite += 3;
            }
            nOffsetRead = 4 * ((nOffsetRead + 3) / 4); // Align to 32 bit boundary
          }
        }
        else if (components == 1)
        {
          // Grayscale
          throw new NotImplementedException("Image format not supported (grayscales).");
        }
    
        FlateDecode fd = new FlateDecode();
        if (hasMask)
        {
          // monochrome mask is either sufficient or
          // provided for compatibility with older reader versions
          byte[] maskDataCompressed = fd.Encode(mask.MaskData);
          PdfDictionary pdfMask = new PdfDictionary(document);
          pdfMask.Elements.SetName(Keys.Type, "/XObject");
          pdfMask.Elements.SetName(Keys.Subtype, "/Image");
    
          Owner.irefTable.Add(pdfMask);
          pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask);
          pdfMask.Elements[Keys.Length] = new PdfInteger(maskDataCompressed.Length);
          pdfMask.Elements[Keys.Filter] = new PdfName("/FlateDecode");
          pdfMask.Elements[Keys.Width] = new PdfInteger(width);
          pdfMask.Elements[Keys.Height] = new PdfInteger(height);
          pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1);
          pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true);
          Elements[Keys.Mask] = pdfMask.Reference;
        }
        if (hasMask && hasAlphaMask && pdfVersion >= 14)
        {
          // The image provides an alpha mask (requires Arcrobat 5.0 or higher)
          byte[] alphaMaskCompressed = fd.Encode(alphaMask);
          PdfDictionary smask = new PdfDictionary(document);
          smask.Elements.SetName(Keys.Type, "/XObject");
          smask.Elements.SetName(Keys.Subtype, "/Image");
    
          Owner.irefTable.Add(smask);
          smask.Stream = new PdfStream(alphaMaskCompressed, smask);
          smask.Elements[Keys.Length] = new PdfInteger(alphaMaskCompressed.Length);
          smask.Elements[Keys.Filter] = new PdfName("/FlateDecode");
          smask.Elements[Keys.Width] = new PdfInteger(width);
          smask.Elements[Keys.Height] = new PdfInteger(height);
          smask.Elements[Keys.BitsPerComponent] = new PdfInteger(8);
          smask.Elements[Keys.ColorSpace] = new PdfName("/DeviceGray");
          Elements[Keys.SMask] = smask.Reference;
        }
    
        byte[] imageDataCompressed = fd.Encode(imageData);
    
        Stream = new PdfStream(imageDataCompressed, this);
        Elements[Keys.Length] = new PdfInteger(imageDataCompressed.Length);
        Elements[Keys.Filter] = new PdfName("/FlateDecode");
        Elements[Keys.Width] = new PdfInteger(width);
        Elements[Keys.Height] = new PdfInteger(height);
        Elements[Keys.BitsPerComponent] = new PdfInteger(8);
        // TODO: CMYK
        Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB");
        if (image.Interpolate)
          Elements[Keys.Interpolate] = PdfBoolean.True;
      }
    }
    

6 个答案:

答案 0 :(得分:6)

我希望您使用的是MemoryStream.GetBuffer(),并且您没有复制到新阵列。

你的主要问题不是直接缺乏记忆而是LOH的碎片化。这可能是一个棘手的问题,主要问题是分配不同大小的大缓冲区。收集LOH上的项目但未压缩。

解决方案可能是:

  • 首先要确保你没有阻止收集任何东西。使用分析器。
  • 尝试重用您的缓冲区。
  • 将分配向上分配到一组固定数字。

最后2个都要求你使用超大数组,可能需要一些工作。

答案 1 :(得分:3)

已建议弃置,但仅适用于MemoryStream,这对您没有任何帮助。此外,还提出了GC.Collect。这可能没有用,因为看起来你的记忆力并没有大幅增长。小心,调用GC.Collect可能是一项昂贵的操作。

碎片

看起来你真的遇到了臭名昭着的大对象堆碎片问题。这可能是由于经常分配和释放60MB内存的块。如果LOH变得支离破碎,它将保持支离破碎。这是长期运行的.NET应用程序的一个主要问题,也是ASP.NET经常被配置为每隔一段时间重新启动的原因。

防止OutOfMemoryException

请参阅上面的CodeProject文章,了解如何执行此操作。诀窍是,使用MemoryFailPoint并抓住InsufficientMemoryException。通过这种方式,您可以优雅地降级,并且您的应用程序不会变得不稳定。

可能的一般解决方案

确保您的大型物体尽可能长时间存在。重用缓冲区。以足够的大小分配一次,并在需要时将缓冲区归零。这样您就不会遇到任何其他内存问题。当你的物体保持在85k以下时,它们通常不会碰到LOH并且不会混乱。

64位计算机不应该出现此问题

编辑:根据this post(变通办法选项卡)和this post(请参见公开评论),此问题不应出现在64位计算机上。既然你说你在64位机器上运行你的代码,也许你编译时配置设置为x86?

答案 2 :(得分:0)

您的堆内存正在抛出此异常,请尝试在最后调用GC.Collect()以释放资源。您还可以MemoryProfiler查找堆内存使用情况,它附带14天试用

答案 3 :(得分:-1)

尝试将其封装在using(MemoryStream x = ...) { }块中,该块将为您配置对象。

虽然Close应该是Dispose对象,但根据.NET指南,它可能在MemoryStream中有所不同。

答案 4 :(得分:-3)

我在下面的代码中遇到同样的问题:

ImageData = (byte[])pDataReader.qDataTable.Rows[0][11];
if (ImageData != null)
{
    ms = new MemoryStream(ImageData);
    button1.BackgroundImage = Image.FromStream(ms);
}
ImageData = null;

ImageData = (byte[])pDataReader.qDataTable.Rows[0][12];
if (ImageData != null)
{
    ms = new MemoryStream(ImageData);
    button1.BackgroundImage = Image.FromStream(ms);
}
ImageData = null;
ms.Close();

删除ms.Close();可以解决问题 我认为问题上升是因为您在MemoryStream memory = new MemoryStream();块之外定义if并在if块中关闭它,与我一样! Exception Debug info

答案 5 :(得分:-4)

首先,不建议您通过TSQL读/写大型FILESTREAM数据。推荐的方法是使用SQL Server提供的Win32 / DOTNET API。我上面发布的代码显示了如何使用SqlFileStream()。net类访问FILESTREAM数据。它还显示了如何以较小的块发送数据。

假设您的总内存足够,您可以通过创建一堆较小的数组并将它们包装在单个IList或其他索引接口中来防止因LOH碎片导致的内存不足异常。