我经常在创建和处理某些字节数组的方法中遇到OutOfMemoryException
。代码如下所示:
当调用此方法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;
}
}
答案 0 :(得分:6)
我希望您使用的是MemoryStream.GetBuffer()
,并且您没有复制到新阵列。
你的主要问题不是直接缺乏记忆而是LOH的碎片化。这可能是一个棘手的问题,主要问题是分配不同大小的大缓冲区。收集LOH上的项目但未压缩。
解决方案可能是:
最后2个都要求你使用超大数组,可能需要一些工作。
答案 1 :(得分:3)
已建议弃置,但仅适用于MemoryStream
,这对您没有任何帮助。此外,还提出了GC.Collect
。这可能没有用,因为看起来你的记忆力并没有大幅增长。小心,调用GC.Collect可能是一项昂贵的操作。
看起来你真的遇到了臭名昭着的大对象堆碎片问题。这可能是由于经常分配和释放60MB内存的块。如果LOH变得支离破碎,它将保持支离破碎。这是长期运行的.NET应用程序的一个主要问题,也是ASP.NET经常被配置为每隔一段时间重新启动的原因。
请参阅上面的CodeProject文章,了解如何执行此操作。诀窍是,使用MemoryFailPoint
并抓住InsufficientMemoryException
。通过这种方式,您可以优雅地降级,并且您的应用程序不会变得不稳定。
确保您的大型物体尽可能长时间存在。重用缓冲区。以足够的大小分配一次,并在需要时将缓冲区归零。这样您就不会遇到任何其他内存问题。当你的物体保持在85k以下时,它们通常不会碰到LOH并且不会混乱。
编辑:根据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
块中关闭它,与我一样!
答案 5 :(得分:-4)
首先,不建议您通过TSQL读/写大型FILESTREAM数据。推荐的方法是使用SQL Server提供的Win32 / DOTNET API。我上面发布的代码显示了如何使用SqlFileStream()。net类访问FILESTREAM数据。它还显示了如何以较小的块发送数据。
假设您的总内存足够,您可以通过创建一堆较小的数组并将它们包装在单个IList或其他索引接口中来防止因LOH碎片导致的内存不足异常。