none
RAM steigt in die Höhe trotz Dispose (Image/Bitmap) RRS feed

  • Frage

  • Hallo,

    ich habe eine Klasse erstellt, welche für das abspielen von MP3-Dateien zuständig ist. In dieser Klasse ist auch eine PictureBox zum darstellen der Wellenform und der Aktuellen Position der MP3 (blaue Linie, ein Pixel breit).

    Gerade letzteres macht mir zu schaffen, denn das Zeichnen dieser blauen Linie lässt den RAM-Speicher in die Höhe treiben. Nach ca. 11 Stunden, sind mal eben 1,5 Gigabyte "belegt".

    Ich weiß, dass es irgendwas mit dem freigeben der Resourcen zu tun hat, aber ich weiß einfach nicht wo, wie, wann?! :-(

    Hier mal die eine Sub-Routine, welche für das Zeichnen der Linie zuständig ist, sobald sich die Position der MP3 geändert hat.

    Public Sub DrawWaveposition(ByVal Img As PictureBox)
                If IsNothing(Img) Or Img.Width = 0 Or _ShowWaveform = False Then
                    ' Es gibt kein Image, wo die aktuelle Position eingezeichnet
                    ' werden kann. ABBRUCH!
                    Exit Sub
                End If
    
                ' Position in der Wellenform berechnen
                Dim bitmap As Bitmap = New Bitmap(Img.Width, Img.Height)
                'Dim g As Graphics = Graphics.FromImage(bitmap)
                Dim p As Pen = New Pen(_WaveformPositionColor)
                Dim bpp As Double = SoundLength / CType(Img.Width, Double) ' bytes per pixel
                Dim x As Integer = CType(Math.Round(SoundPosition / bpp), Integer)
    
                Using g As Graphics = Graphics.FromImage(bitmap)
    
                    ' Senkrechte Linie einzeichnen
                    If _WaveformPositionWidth = 1 Then
                        ' Nur eine schmale Linie
                        g.DrawLine(p, x, 0, x, Img.Height - 1)
                    Else
                        ' Breiter als 1 Pixel ist eine Box
                        Dim rectBox As New Rectangle(x, 0, _WaveformPositionWidth, Img.Height - 1)
                        g.DrawRectangle(p, rectBox)
                    End If
                    g.Dispose()
                End Using
    
                Img.Dispose()
    
                Img.Image = bitmap.Clone
                _WaveformSoundPosition = x
                p.Dispose()
                bitmap.Dispose()
                p = Nothing
                bitmap = Nothing
    
            End Sub

    Ich habe es mit Dispose probiert, mit Using probiert, aber dennoch steigt der Arbeitsspeicherverbauch mit jedem Aufruf weiter an.

    Hat bitte jemand einen Tipp für mich? Danke!

    Gruß
    Andy

    Montag, 3. Dezember 2012 07:14

Antworten

  • Hallo Andy,

    viel hilft nicht unbedingt viel ;)

    Durch das zusätzliche Clone() verbrätst Du nur weiteren Speicher.
    Freigeben solltest Du das vorherige Image der PictureBox.
    Aber auch Pens und andere Grafikobjekte sollten freigegeben werden.
    Using führt ein Dispose aus, das braucht man also nicht nochmal.

    Im übrigen sollte man OrElse verwenden, denn Deine Prüfung am Anfang würde derzeit auf die Nase fallen, wenn es kein Image geben würde.

    Und bitte verzichte auf diese IsNothing Funktion, Visual Basic kennt Is Nothing und IsNot Nothing und setzt die in direkte Befehle um. Eine Funktion "verbessert" da gar nichts.

    Mal eine mögliche Implementation (mit einigen Annahmen bezüglich der verwendeten Variablen):

        Public Sub DrawWaveposition(ByVal pb As PictureBox)
            If _ShowWaveform = False _
                OrElse pb Is Nothing _
                OrElse pb.ClientSize.Width = 0 _
                OrElse pb.ClientSize.Height = 0 Then
                Exit Sub
            End If
    
            Dim imgSize = pb.ClientSize    ' Die Innereien gelten als Zeichenbereich
    
            ' Position in der Wellenform berechnen
            Dim bitmap As New Bitmap(imgSize.Width, imgSize.Height)
            Using g = Graphics.FromImage(bitmap)
                Using p As New Pen(_WaveformPositionColor)
                    Dim bpp As Double = SoundLength / CDbl(imgSize.Width) ' bytes per pixel
                    Dim x As Integer = CInt(Math.Round(SoundPosition / bpp))
    
                    ' Senkrechte Linie einzeichnen
                    If _WaveformPositionWidth = 1 Then
                        ' Nur eine schmale Linie
                        g.DrawLine(p, x, 0, x, imgSize.Height - 1)
                    Else
                        ' Breiter als 1 Pixel ist eine Box
                        Dim rectBox As New Rectangle(x, 0, _WaveformPositionWidth, imgSize.Height - 1)
                        g.DrawRectangle(p, rectBox)
                    End If
    
                    _WaveformSoundPosition = x
                End Using
            End Using
    
            ' Alte Grafik freigeben
            If pb.Image IsNot Nothing Then
                pb.Image.Dispose()
            End If
    
            pb.Image = bitmap
    
        End Sub

    (Den Namen des Parameters der Methode habe ich im übrigen nur geändert damit nichts durchschlüpft).

    Probiere mal damit aus, ob der Speicherverbrauch immer noch ansteigt. Wenn ja, gibt es vermutlich weitere Problemstellen.

    Die Bitmap könnte im übrigen komplett entfallen, wenn man die PictureBox beerdigt; anstatt dessen ein Panel nimmt und die Linie(n) direkt darauf zeichnet.

    Gruß Elmar


    Montag, 3. Dezember 2012 08:23
    Beantworter

Alle Antworten

  • Hallo Andy,

    viel hilft nicht unbedingt viel ;)

    Durch das zusätzliche Clone() verbrätst Du nur weiteren Speicher.
    Freigeben solltest Du das vorherige Image der PictureBox.
    Aber auch Pens und andere Grafikobjekte sollten freigegeben werden.
    Using führt ein Dispose aus, das braucht man also nicht nochmal.

    Im übrigen sollte man OrElse verwenden, denn Deine Prüfung am Anfang würde derzeit auf die Nase fallen, wenn es kein Image geben würde.

    Und bitte verzichte auf diese IsNothing Funktion, Visual Basic kennt Is Nothing und IsNot Nothing und setzt die in direkte Befehle um. Eine Funktion "verbessert" da gar nichts.

    Mal eine mögliche Implementation (mit einigen Annahmen bezüglich der verwendeten Variablen):

        Public Sub DrawWaveposition(ByVal pb As PictureBox)
            If _ShowWaveform = False _
                OrElse pb Is Nothing _
                OrElse pb.ClientSize.Width = 0 _
                OrElse pb.ClientSize.Height = 0 Then
                Exit Sub
            End If
    
            Dim imgSize = pb.ClientSize    ' Die Innereien gelten als Zeichenbereich
    
            ' Position in der Wellenform berechnen
            Dim bitmap As New Bitmap(imgSize.Width, imgSize.Height)
            Using g = Graphics.FromImage(bitmap)
                Using p As New Pen(_WaveformPositionColor)
                    Dim bpp As Double = SoundLength / CDbl(imgSize.Width) ' bytes per pixel
                    Dim x As Integer = CInt(Math.Round(SoundPosition / bpp))
    
                    ' Senkrechte Linie einzeichnen
                    If _WaveformPositionWidth = 1 Then
                        ' Nur eine schmale Linie
                        g.DrawLine(p, x, 0, x, imgSize.Height - 1)
                    Else
                        ' Breiter als 1 Pixel ist eine Box
                        Dim rectBox As New Rectangle(x, 0, _WaveformPositionWidth, imgSize.Height - 1)
                        g.DrawRectangle(p, rectBox)
                    End If
    
                    _WaveformSoundPosition = x
                End Using
            End Using
    
            ' Alte Grafik freigeben
            If pb.Image IsNot Nothing Then
                pb.Image.Dispose()
            End If
    
            pb.Image = bitmap
    
        End Sub

    (Den Namen des Parameters der Methode habe ich im übrigen nur geändert damit nichts durchschlüpft).

    Probiere mal damit aus, ob der Speicherverbrauch immer noch ansteigt. Wenn ja, gibt es vermutlich weitere Problemstellen.

    Die Bitmap könnte im übrigen komplett entfallen, wenn man die PictureBox beerdigt; anstatt dessen ein Panel nimmt und die Linie(n) direkt darauf zeichnet.

    Gruß Elmar


    Montag, 3. Dezember 2012 08:23
    Beantworter
  • Hallo Elmar,

    danke! Danke danke danke!!!! :-)

    Es funktioniert auf anhieb (Routine gegen deine 1:1 ersetzt!), kein weiterer Speicheranstieg, BIS JETZT! :-)
    Mehr kann ich erst sagen, wenn ich das Programm wieder über mehrere Stunden laufen lasse.
    Aber jetzt steigt der Arbeitsspeicher nicht mehr nach jedem "Sprung" im Song um mehrere MB!!! Es kommt NICHTS mehr hinzu, noch nicht mal ein KB :-)

    Der Anstieg ist zwar noch vorhanden, aber erst wenn der nächste Song geladen wird. Ich gehe mal davon aus, dass in der Routine (erstellen der Wellenform) das "gleiche Problem" ist und das werde ich, dank deiner Vorlage, noch rausfinden :-)

    Vielen Dank nochmal! :-)

    Gruß
    Andy
    PS: Natürlich habe ich meine "alte Routine" behalten um zu sehen/zu vergleichen wo mein Denkfehler ist/war :-)

    Montag, 3. Dezember 2012 08:48