Laden...

AForge MJPEGStream - Anwendung frisst mehr und mehr Speicher

Erstellt von Majestic1987 vor 13 Jahren Letzter Beitrag vor 13 Jahren 6.069 Views
M
Majestic1987 Themenstarter:in
20 Beiträge seit 2010
vor 13 Jahren
AForge MJPEGStream - Anwendung frisst mehr und mehr Speicher

Hallo Leute,

nachdem ich's jetzt hinbekommen habe, mein Image-Control (WPF) mit dem Bild aus ner IP-Cam (über AForge.Video.MJPEGStream) zu versorgen und mich schon (fast) gefreut habe ist mir etwas abscheuliches aufgefallen:

Das Programm belegt immer mehr Hauptspeicher. Wenn ich nur den Stream laufen habe und im "NewFrame"-Event garnichts tu belegt mein Programm 38MB (im Schnitt).

Sobald ich jetzt etwas mit den Bildern mache werden es immer mehr. Erst 100...120....150...170...210....Tendenz steigend. Wie kann ich das vermeiden oder: Woran kann das liegen? Hier mal mein Code:


private void video_NewFrame(object sender, AForge.Video.NewFrameEventArgs eventArgs)
            {
                System.Drawing.Bitmap bmp = ((System.Drawing.Bitmap)eventArgs.Frame);
                
                System.Windows.Media.Imaging.BitmapSource b = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap (((System.Drawing.Bitmap)eventArgs.Frame).GetHbitmap(), IntPtr.Zero, System.Windows.Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromWidthAndHeight (((System.Drawing.Bitmap)eventArgs.Frame).Width, ((System.Drawing.Bitmap)eventArgs.Frame).Height));

                b.Freeze();

                this.imgVideo.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
                                             new Action(delegate()
                                             {
                                                 this.imgVideo.Source = b;
                                             }));
            }

Ich habe auch schonmal versucht, keine neuen Objekte zu erstellen, sondern quasi direkt mit dem Event-Parameter zu arbeiten, das brachte aber keine Besserung.
Es geht eigentlich nur darum, ein paar Überwachungskameras anzeigen zu können und das mausert sich jetzt zu einer Hürde.

Danke schonmal im Voraus.

2.760 Beiträge seit 2006
vor 13 Jahren

Keine Ahnung was mit dem Bitmap aus den EventArgs noch gemacht wird aber die können ganz schön schwer im Speicher hängen. Einfach mal Dispose() aufrufen um Resourcen wieder freizugeben.

M
Majestic1987 Themenstarter:in
20 Beiträge seit 2010
vor 13 Jahren

Aaalso meine erste Vermutung war, dass die Konvertierung von Bitmap in BitmapSource irgendwas tut, was permanent immer mehr Speicher belegt, der nicht mehr freigegeben wird. Also habe ich das ganze mal in WinForms nachgebaut...ohne alles drum und dran. Nur Video anzeigen.

Und siehe da: Selbes Problem. Der Fehler scheint also nicht in der Konvertierung zu liegen.

Ich hab mal auf das EventArg ein Dispose aufgerufen, aber auch das ändert nix an der Speicherfresserei.

Auch wenn ich den GC zwinge, aufzuräumen, ändert das nichts. IRGENDWO, und ich vermute innerhalb der Aforge-Bib hängt da was schief.....

742 Beiträge seit 2005
vor 13 Jahren

Lade dir doch mal die Trial Version des Memory Profilers von RedGate runter:

ANTS Performance Profiler

M
Majestic1987 Themenstarter:in
20 Beiträge seit 2010
vor 13 Jahren

Hab ich mir runtergeladen und mal gestartet.

Leider hilft mir das nicht wirklich weiter. Ich sehe jetzt zwar wunderbar, dass der Speicherplatzbedarf stetig ansteigt.

Das einzige, was mich jetzt wundert, ist der Unterschied im Profil zwischen der WPF- und der Forms-Applikation:

Unter WinForms hat der Speicherplatzbedarf einen Sägezahnförmigen Verlauf. Sieht stark nach nem Puffer innerhalb des MJPEG-Stream-Objekts aus, der von Zeit zu Zeit geleert wird. Dort liegt dann der maximale Speicherplatzbedarf bei 67MB (was ich aber auch ne Hausnummer finge, dafür, dass ausser ner Picture-Box nix drauf is auf dem Form)

Bei der WPF-Anwendung gibt es auch so einen Effekt ABER dort steigt der Bedarf an Speicher an, fällt dann auch wieder ab, jedoch nur um etwa 10% des Spitzenwertes, um anschließend weiter zu steigen.

Ich weiß nun aber noch immer nicht, woher das Problem stammt...

6.862 Beiträge seit 2003
vor 13 Jahren

Hallo,

mit dem Memory Profiler kannst du doch Snapshots machen und diese vergleichen. Dann brauchst du nur schaun welche Objekte in mehreren Snapshops vorkommen, sprich nicht durch den GC irgendwie abgeräumt werden. Bei manchen ist es natürlich normal das die die ganze Programmlaufzeit überleben, bei anderen nicht. Der Memory Profiler von Red Gate erlaubt doch auch nach bestimmten Kriterien die Objekte zu filtern. Da gibts auch nen Abschnitt Common Leak Indicators oder irgendwie so ähnlich. Da muss man besondern mal nen Blick drauf werfen. Durch den Retention Graph bekommt man dann auch ganz gut die Stellen raus wo es zwickt. Auf der Red Gate Seite gibts ja verschiedene Walktroughs die das ganze Vorgehen ein wenig erklären.

Baka wa shinanakya naoranai.

Mein XING Profil.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Majestic1987,

Ich weiß nun aber noch immer nicht, woher das Problem stammt...

und ich weiß immer noch nicht, ob du überhaupt ein Problem hast. Der GC gibt den Speicher normalerweise erst frei, wenn er anderweitig gebraucht wird. Solange das nicht der Fall ist, ist steigender Speicherverbrauch (oder sagen wir besser steigender Speichergebrauch) allenfalls ein sehr schwaches Indiz, aber in keinster Weise ein Beweis, dass irgendwas nicht stimmt. Wir können weiterreden, wenn irgendwelche Anderen Anwendungen negativ durch dein Programm beeinflusst werden. Solange das nicht der Fall ist, solltest du deine Zeit sinnvoller einsetzen.

herbivore

M
Majestic1987 Themenstarter:in
20 Beiträge seit 2010
vor 13 Jahren

Naja irgendwas KANN ja nicht stimmen, wenn die selbe Anwendung unter WindowsForms schön zwischen 40 und 60 MB pendelt, während bei der WPF-Version die Speichernutzung bis an die Grenzen des physikalisch machbaren geht. Ist ja auch nicht so, dass die Anwendung nachher auf nem Server mit 128GB RAM laufen soll 😉

Du stimmst mir sicher zu, dass es doch irgendwie seltsam ist, dass der Speicher auch dann nicht frei wird, wenn ich den GC zwinge, aufzuräumen.

Ich habe das Problem jetzt (eher unelegant) gelöst, indem ich statt dem ImageControl einen WindowsFormsHost samt PictureBox eingefügt habe. Seitdem pendelt die Speichernutzung auch bei der WPF-Version zwischen 40 und 70MB, was ich mal als vertretbar einstufe.

4.221 Beiträge seit 2005
vor 13 Jahren
  
this.imgVideo.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,  
new Action(delegate()  
{  
   // ---schnipp---  
   if (this.imgVideo.Source!=null)  
   {  
      this.imgVideo.Source.Dispose();  
   }  
   // ---schnapp---  
   this.imgVideo.Source = b;  
}));  
            }  
  

Nur so aus lauter Neugier... mach mal markierten Zeilen rein... ev. bringts ja was... ansonsten hängt das vorher angezeigt Bild noch in der Gegend rum.

Gruss
Programmierhans

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

M
Majestic1987 Themenstarter:in
20 Beiträge seit 2010
vor 13 Jahren

Nope,

geht nicht. imgVideo.Source ist Member von imgVideo, wobei es sich wiederum um ein Image-Control handelt. Auf die Eigenschaft "Source" kann man kein Dispose() aufrufen, und wenn ich das Image-Control dispose seh ich natürlich gar kein Bild mehr 😉

4.221 Beiträge seit 2005
vor 13 Jahren

Kommst Du sonst irgendwie an das letzte angezeigte Bild um dieses zu disposen ?

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

M
Majestic1987 Themenstarter:in
20 Beiträge seit 2010
vor 13 Jahren

Nope. Auf das EventArg rufe ich bereits ein Dispose auf und auch GC.Collect() habe ich bereits probiert.

Und auch auf das temporär erstellte Bitmap hab ich bereits ein Dispose aufgerufen. Wobei das eigentlich überflüssig sein sollte, denn das Objekt wird ja eh nach Beenden der Methode freigegeben.

Das Problem tritt ja auch, wie erwähnt, in einer WinForms-Umgebung NICHT auf, auch unter WPF -> WinFormsHost -> PictureBox ist der Fehler nicht zu beobachten.

Scheinbar ist die Windows.Interop.... Methode nicht dafür geeignet, viele Bilder pro Sekunde von Bitmap in ImageSource zu konvertieren...denn sobald ich diese Methode nicht verwende ist der Fehler auch weg. Ich habe nämlich auch schon probiert einfach nur IRGENDWAS im Event-Handler zu tun...auch da war kein Speicherleck zu finden.

Naja. Wie erwähnt ist das Problem nun durch den kleinen WindowsForms-Workaround gelöst.

S
248 Beiträge seit 2008
vor 13 Jahren

Hallo,

ich habe den von dir verwendeten Code zur Konvertierung von Bitmap nach BitmapSource angeschaut. Dabei verwendest du den Zwischenschritt über ein hBitmap. Dieses hBitmap ist jedoch ein Handle, das nicht vom GC freigeben wird, siehe MSDN.

Dieses kleine Programm sollte das Problem und dessen Lösung erläutern:

using System;
using System.Drawing;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Bitmap bitmap = new Bitmap(@"c:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg");
            int i = 0;
            while (true)
            {
                IntPtr hBitmap = bitmap.GetHbitmap();
                var options = System.Windows.Media.Imaging.BitmapSizeOptions.FromWidthAndHeight(bitmap.Width, bitmap.Height);
                BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, new Int32Rect(0, 0, bitmap.Width, bitmap.Height), options);
                //DeleteObject(hBitmap);
                if (++i == 10)
                {
                    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
                    GC.WaitForPendingFinalizers();
                    Console.WriteLine("Memory used: {0}", Environment.WorkingSet);
                    i = 0;
                }
            }
        }

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
    }
}

Durch das Freigeben des Handles wird das Memoryleak geschlossen.

spooky