OutOfMemoryException, wenn Sie mehrere byte-arrays
Ich bin ständig schlagen einen OutOfMemoryException
innerhalb einer Methode erzeugt und verarbeitet einige byte-arrays. Der code sieht so aus:
- Erzeugen MemoryStream, um einige Daten (über 60MB).
- Erstellen byte-array (gleiche Größe wie MemoryStream, über 60MB)
- Füllen Sie das array mit den bytes aus dem Speicher-stream
- Schließen MemoryStream
- Prozess Daten aus dem byte-array
- Lassen Methode
Wenn diese Methode aufgerufen wird, wie die 20-30 mal, die ich bekommen OutOfMemoryException
Recht, wo das byte-array zugeordnet ist. Aber ich glaube nicht, dass es die system-Speicher-Problem. Anwendung Speichernutzung ist rund 500MB (private working set) und dem test-Maschine ist 64-bit mit 4GB RAM.
Ist es möglich, dass der Speicher verwendet werden, indem das byte-array oder MemoryStream
wird nicht freigegeben, nachdem die Methode beendet wird? Aber dann, es sieht nicht aus wie dieser Speicher reserviert für den Prozess als private working set ist nur 500MB oder so.
Was kann die Ursache des OutOfMemoryException
wenn große byte-array (60MB) neben physikalischen Speicher Mangel?
[Bearbeitet, um hinzufügen code-Beispiel]
Quelle stammt aus PdfSharp lib
Ausnahme wird ausgelöst bei Zeile byte[] imageBits = new byte[streamLength];
Es in der Tat aussieht wie LOH Fragmentierung Problem.
///<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;
}
}
- #1 Frage, sind Sie ordnungsgemäß zu entsorgen Ihre streams? Auch, möchten Sie vielleicht, um etwas von Ihrem code.
- es gibt nichts zu Entsorgen ist, neben MemoryStream. Die wird geschlossen. Und als eine Angelegenheit von der Tat MemoryStream.Close() ist nur ein Aufruf an MemoryStream.Dispose(true), gefolgt von GC.SuppressFinalize(this). Byte-arrays sind nicht Einweg, so gibt es nichts mehr das ich tun kann. Wie für den code... es ist sehr Komplex und kommt von der PdfSharp lib. Es ist nicht mein code, aber ich versuche zu verstehen und die Probleme beheben, die es macht.
- Ich war Lesung auf einige der Fragmentierung. Es scheint, dass auf 64-bit-windows, die zugrunde liegende Speicher-manager, sollten Sie nicht setzen dieses Verhalten. Ist es möglich, dass Ihr Prozess läuft im WOW64 -, d.h., dass Sie nur für x86 kompiliert? Ich aktualisiert meine Antwort mit mehr Informationen über die Fragmentierung Problem.
- Ganz wenige Exemplare und Helfer-arrays, die Fliegen hier Rum. Dies könnte eine seltene Gelegenheit, aufrufen von GC.Collect() könnte helfen, am besten nach oder am Ende der
ReadTrueColorMemoryBitmap()
. - Wir sind uns alle einig, dass das problem mit dem GC Versagen, sich zu erholen, oder fragmentiert, der verwaltete heap-Speicher. Dann, eine gute alternative sein könnte, mit nicht verwaltetem Speicher:
System.IO.UnmanagedMemoryStream
das problem ist Das man vorher wissen müssen der Platzbedarf. Oder zumindest eine Obergrenze. Die Dokumentation sagt ganz klar, dass dieser stream nicht reserviert Speicher auf dem heap. Ein weiteres problem, dein Programm braucht, Sicherheits-Einstellungen, die erlaubt, dies zu tun.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich hoffe, Sie sind mit
MemoryStream.GetBuffer()
sind und dass Sie nicht das kopieren in ein neues array.Ihr Hauptproblem ist nicht ein direkter Mangel an Speicher, aber die Fragmentierung des LOH. Das kann ein heikles problem, das Hauptproblem ist die Zuteilung der große Puffer von unterschiedlicher Größe. Elemente auf dem LOH sind erhoben, aber nicht verdichtet.
Lösungen könnten sein:
Den letzten 2 beide verlangen, dass Sie mit zu großen arrays, kann einige Arbeit.
Entsorgung ist vorgeschlagen worden, aber nur für die
MemoryStream
dies tun Sie nichts. AuchGC.Collect
vorgeschlagen worden. Dies kann nicht helfen, wie es scheint, Ihr Gedächtnis ist nicht wesentlich wächst. Seien Sie vorsichtig, aufrufen von GC.Sammeln kann eine teure operation sein.Fragmentierung
Es sieht wirklich so aus, wie Sie schlagen die berüchtigten Large Object Heap-Fragmentierung-Problem. Dies kann auch verursacht werden, indem Sie oft allozieren und freigeben von Blöcken von 60MB Speicher. Wenn die LOH wird fragmentiert, es bleibt fragmentiert. Dies ist ein wichtiges Thema mit langer Laufzeit .NET-Anwendungen und ein Grund, warum ASP.NET ist oft so konfiguriert, starten in regelmäßigen Abständen.
Verhindern OutOfMemoryException
Siehe oben CodeProject-Artikel auf, wie dies zu tun. Der trick ist, verwenden Sie
MemoryFailPoint
und fangen dieInsufficientMemoryException
. Auf diese Weise können Sie die anmutig degradieren und Ihre Anwendung wird nicht instabil.Möglich Allgemeine Lösung
Stellen Sie sicher, dass Ihre große Objekte Leben so lange wie möglich. Die Wiederverwendung der buffer. Weisen Sie ihn einmal mit ausreichender Größe und null-Puffer, wenn du es wieder brauchst. Auf diese Weise werden Sie nicht in eine andere Speicher-Probleme. Wenn Sie Ihre Objekte bleiben unter 85k in der Größe, die Sie in der Regel nicht in die LOH und nicht Durcheinander.
64-bit-Maschinen sollten nicht dieses Problem haben
EDIT: Laut dieser Beitrag (workaround tab) und dieser Beitrag (siehe open-Kommentar), sollte dieses Thema nicht auf 64-bit-Maschinen. Da Sie sagen, Sie führen Sie den code auf 64-bit-Maschinen, vielleicht haben Sie zusammengestellt, mit der Konfiguration zu x86?
Ihre Heap-Speicher ist das werfen dieser Ausnahme, versuchen, aufrufen von GC.Erfassen() am Ende, um Ressourcen freizusetzen. Sie können auch MemoryProfiler, um herauszufinden, die heap-Speicher-Auslastung, es kommt mit 14 Tage trial
Versuchen, umschließt es in einem
using(MemoryStream x = ...) { }
- block, wird über das Objekt für Sie.Obwohl
Close
sollDispose
das Objekt, nach .NET-Richtlinien, vielleicht ist es anders inMemoryStream
.GC.Collect()
auch nicht zu Häufig. Aber die using-block Garantien, dass der stream freigegeben wird, auch wenn etwas schief geht (d.h. eine Ausnahme wird geworfen, ro, vergessen Sie zu schließen/entsorgen das Objekt).using{..}
wenn Objekte sind Einweg, aber in einigen Fällen, ein Objekt ist nurIDisposable
da die Vererbung Kette erfordert. In diesem FallDispose()
ist praktisch ein no-op und nicht Ressourcen frei. Prüfen Sie den Reflektor, wenn Sie nicht glauben, meine Ansprüche ;). Hinweis: es verletzt nicht, um zu verwendenusing
hier, aber es tut nichts entweder.for
Körper oder was auch immer). Das könnte nützlich bei der viele streams erstellt wird und nicht aus dem Rahmen. Bitte, korrigieren Sie mich, wenn ich falsch bin.Gleiche problem habe ich im code wie folgt:
Entfernen
ms.Close();
löst das problem.Ich denke, das problem steigen, weil Sie definieren
MemoryStream memory = new MemoryStream();
außerhalb derif
block und schließen Sie es in derif
block, gleichen wie ich!Zuerst von allen, ist es nicht empfohlen, dass Sie Lesen/schreiben großer FILESTREAM-Daten über TSQL. Die empfohlenen Ansatz ist durch die Verwendung von Win32/DOTNET-APIs von SQL server bereitgestellt. Der code, den ich oben gepostet zeigt, wie der Zugriff auf die FILESTREAM-Daten mit den SqlFileStream() .net-Klasse. Es zeigt außerdem, wie zum senden der Daten in kleinere Segmente.
Vorausgesetzt, dass Ihr gesamter Arbeitsspeicher ist ausreichend, Sie können verhindern, dass Out-of-memory-Ausnahmen ergeben sich aus LOH Fragmentierung durch die Schaffung einer Reihe von kleineren arrays, und wickelte Sie in ein einzelnes IList, oder einige andere indizierte Schnittstelle.