2009-05-14 9 views
1

Ich habe, was ich vermute, ein Speicherfragmentierungsproblem.WPF-Speicherfragmentierung

Wir haben kürzlich unsere WinForms-Anwendung in eine WPF-Anwendung portiert. Es gibt eine Bildverarbeitung, die diese Anwendung ausführt, und diese Verarbeitung hat immer in der WinForms-Version der App funktioniert. Wir gehen zu WPF, und die Verarbeitung stirbt ab. Das Debugging in die Bibliothek hat den Tod an zufälligen Punkten, aber immer mit einem Array, das null ist, dh die Zuweisung ist fehlgeschlagen.

Die Verarbeitung selbst erfolgt in einer C++ - Bibliothek, die von p/invoke aufgerufen wird und ziemlich speicherintensiv ist; Wenn das gegebene Bild N × M Pixel groß ist, dann ist das Bild N × M × 2 Bytes groß (jedes Pixel ist ein vorzeichenloser Kurzschluss und es ist ein Graustufenbild). Während der Verarbeitung werden Bildpyramiden erstellt, die sich im Float-Bereich befinden, so dass die gesamte Speicherbelegung N x M x (2 + 2 + 4 + 4 + 4 + 4) ist, wobei die erste 2 die Eingabe, die zweite ist 2 ist der Ausgang, der erste 4 ist der Eingang in Floats, der zweite 4 ist das 0th Level Difference Bild, und die letzten 2 Viere sind der Rest der Pyramide (da sie Pyramiden sind und jedes Level halb so groß ist Richtung, diese 4s sind obere Grenzen). Also, für ein 5000x6000 Bild, das sind 600 mb, was gut in den Speicher passen sollte.

(Es gibt die Möglichkeit, dass das Marshalling die Speicheranforderung durch ein anderes N x M x 4 erhöht, dh die Eingabe- und Ausgabebilder auf der C# -Seite und dann die gleichen auf die C++ - Seite kopierten Arrays - Marshalling Anforderung größer sein?)

Wie fragmentiert ist WPF im Vergleich zu WinForms? Gibt es eine Möglichkeit, Speicher zu konsolidieren, bevor diese Verarbeitung ausgeführt wird? Ich vermute, dass Fragmentierung das Problem ist, aufgrund der zufälligen Natur der Brüche, wenn sie passieren, und dass es immer ein Speicherzuweisungsproblem ist.

Oder sollte ich dieses Problem vollständig vermeiden, indem ich die Verarbeitung als separaten Prozess mit Datentransfer über Sockets oder ähnliches ausführen lasse?

Antwort

3

Wenn ich dies richtig gelesen habe, passiert der Speicherzuordnungsfehler auf der nicht verwalteten Seite, nicht auf der verwalteten Seite. Es erscheint dann seltsam, WPF zu beschuldigen. Ich erkenne, dass Sie Ihre Schlussfolgerung basierend auf der Tatsache ziehen, dass "es in WinForms funktioniert", aber es gibt wahrscheinlich mehr Änderungen als nur das. Sie können ein Tool wie die .NET Memory Profiler verwenden, um die Unterschiede zwischen der Verarbeitung von Speicher durch die WPF-Anwendung und die WinForms-Anwendung zu sehen. Möglicherweise stellen Sie fest, dass Ihre Anwendung etwas tut, was Sie nicht erwarten. :)

Pro Kommentar: Yup, ich verstehe. Wenn Sie sicher sind, dass Sie Dinge wie Umgebungsänderungen ausgeschlossen haben, denke ich, dass Sie sich eine Kopie von BoundsChecker und Memory Profiler (oder DevPartner Studio) holen und sich ansehen müssen, was Ihre Speicherzuweisung verpfuscht.

+0

ich über diesen Verarbeitungscode oft gegangen sind. Wenn es leckt oder sich schlecht benimmt, sehe ich es einfach nicht; aber die Pausen begannen definitiv, als wir von Winforms zu WPF gingen. Der Code ist ansonsten identisch. – mmr

1

Ich vermute, dass der GC Ihr Gedächtnis bewegt. Versuchen Sie, den Speicher in nicht verwaltetem Land zu fixieren, solange Sie einen rohen Zeiger auf das Array haben, und lösen Sie ihn so schnell wie möglich ab. Es ist möglich, dass WPF den GC häufiger ausführt, was erklären würde, warum es öfter passiert und wenn es der GC ist, dann würde das erklären, warum es an zufälligen Stellen in Ihrem Code passiert.

Edit: Aus Neugier, könnten Sie auch all Ihren Speicher im Voraus reservieren (ich sehe den Code nicht, so weiß nicht, ob das möglich ist), und stellen Sie sicher, dass alle Ihre Zeiger sind Nicht-null, so dass Sie überprüfen können, dass es tatsächlich in der Speicherzuordnung statt eines anderen Problems geschieht?

+0

fixieren? Die DLL deklariert all ihren Arbeitsspeicher in sich selbst. Wenn der GC überall herumwandert, dann würde das wirklich bedeuten, in einen anderen Prozess zu gehen, nur um so ein wackeliges Verhalten zu vermeiden. – mmr

+0

Nun, sind die Zeiger auf * gcnew * Objekte oder * neue * Objekte? Ich nahm an, dass Sie ein Int * zu den rohen Pixeldaten aus einer Bitmap-Klasse nehmen. Der GC könnte das Bitmap aufnehmen und es bewegen, während Sie laufen. – FryGuy

+0

Ja, das mache ich nicht. Ich verwende DICOM-Bilder, die keine Bitmaps sind. Die Visualisierung erfolgt als OpenGL-Texturen, und die Bilder selbst sind nur Arrays - in der C# -App sind sie Ushorts und in der C++ - DLL werden sie zu Floats, um negative Werte und genügend Dynamikbereich zu haben. Also, sie sind zu "neuen" Objekten. – mmr

0

Es klingt, als ob Sie in Bezug auf Ihr Speichermanagement im Allgemeinen vorsichtiger sein möchten; Das heißt, entweder die Verarbeitungs-Engine in einem separaten Adressraum ausführen, der Speicher sorgfältig verwaltet, oder einen ausreichend großen Chunk vorbelegen, bevor der Speicher zu fragmentiert wird und Bilder nur in diesem Bereich verwaltet werden. Wenn Sie den Adressraum mit der.NET-Laufzeit in einem lang andauernden Prozess, und Sie benötigen große zusammenhängende Bereiche, wird es immer irgendwann möglicherweise fehlschlagen. Nur meine 2c.

+0

Also, im Grunde, gehen Sie einfach zu einem zweiten Prozess, was ich höre. Warum könnte es immer potenziell scheitern? Was macht .NET, das es so instabil macht? – mmr