locked
OleDbConnection belegt sporadisch Speicher (RAM) und gibt diesen dann nicht wieder frei. RRS feed

  • Frage

  • Hallo,

    bitte nicht erschlagen, wenn das Thema in diesem Forum fehlplatziert ist. Hinweis und ich stelle es richtig ein.

    Unter Visual Studio 2005 (Visual Basic) wurde mein Programm 2009 als ClickOnce Anwendung entwickelt, weiter entwickelt und lief stabil und ohne Macken. Am 11.11.2016 wurden diverse Updates auf alle dienstlichen Rechner (Windows 7 Pro / Office 2007 Pro) aufgespielt und seither tritt sporadisch folgender Fehler auf.

    Ich öffne eine System.Data.OleDb.OleDbConnection zu einer Kennwort geschützten Access 2007 Datenbank, führe einen SQL Befehl aus und schließe die Verbindung zur Datenbank wieder. Es werden im Laufe des Tages mehr als 6000 SQL Befehle ausgeführt. Das Programm lief 24h/Tag/Woche wie bereits erwähnt stabil. Nun steigt der belegte Speicher (RAM) sporadisch an, Mal zwischen den Messpunkten 2 und 3, aber auch zwischen 4 und 5. Belegt werden zwischen 8 und 45 MB und diese werden danach nicht wieder freigegeben.

    Das u.a. Skript ist ein Auszug des Originals, welches seit November viele Messpunkte und Logfile Einträge erhalten hat, um dem Fehler auf die Spur zu kommen. Diese habe ich entfernt. Ergänzend noch der Hinweis, im Programm werden Schleifen durchlaufen, welche z.B. 8 SQL Blöcke je 5 SQL Befehle durchlaufen. Mal steigt der Speicher im Block 4 Befehl 2, dann nach 20 problemlosen Durchläufen im Block 6 Befehl 4. u.s.w. Steigt der belegte Speicher über 105 MB an, wird der Zugriff auf die zuletzt abgefragte Tabelle der Datenbank für weitere Abfragen gesperrt. Steigt der belegte Speicher um weitere 10 MB an funktioniert kein Zugriff mehr auf die Datenbank. Die zurück gegebene Fehlermeldung lautet:

    Es wurde versucht, im geschützten Speicher zu lesen oder zu schreiben. Dies ist
    häufig ein Hinweis darauf, dass anderer Speicher beschädigt ist.

    Beende ich das Programm und starte es neu, dann läuft die Anwendung wieder und die Access DB ist definitiv nicht defekt. Auch die Protokolldatei ist nach schließen der Verbindung nicht mehr vorhanden.

    Laut Protokoll ist die Verbindung nach verlassen der Funktion geschlossen und auch der Dispose Befehl wird korrekt ausgeführt.

    Leider gehen mir die Ideen aus, woran diese ominöse Speicherbelegung liegen kann und bitte um Hilfe.

    Module MSDN_Forum
    
        Private Enum ConBy As Byte
            ConBySQL = 0
            ConByOLE = 1
        End Enum
    
        Private Enum ExecuteCMD As Byte
            vbScalar = 1
            vbNonQuery = 2
            vbReader = 3
            vbInsert = 4
        End Enum
    
        Private Structure Structure_db_Conection_Command_Execute
            Public SQL_Befehl As String
            Public Connection As String
            Public ConnectionBy As ConBy
            Public Execute As ExecuteCMD
            Public StringResultJN As Boolean
            Public Password As String
            Public Funktionsname As String
            Public ErrorJN As Boolean
            Public ErrorMSG As String
            Public ErrorIndex As Integer
            Public StringResult As String
            Public IntegerResult As Integer
            Public IdentityIndex As Integer
        End Structure
    
        Public Function Call_MSDN_Forum() As Boolean
            Try
                Dim fResult As Structure_db_Conection_Command_Execute
                Dim f_Parameter As New Structure_db_Conection_Command_Execute
                fResult = f_cmd(f_Parameter)
            Catch ex As Exception
                MsgBox("Global Error in public Funktion, MSG: " & vbCrLf & ex.Message, MsgBoxStyle.Critical, "MSDN_Forum")
            End Try
        End Function
    
        Private Function f_cmd(ByRef f_dbcce As Structure_db_Conection_Command_Execute) As Structure_db_Conection_Command_Execute
            Dim Messpunkt As Integer = 0
            Dim dbcce As New Structure_db_Conection_Command_Execute
            Dim cn As System.Data.OleDb.OleDbConnection
            Dim cmd As System.Data.OleDb.OleDbCommand
            Messpunkt = 1
            Try
                dbcce = f_dbcce
                With dbcce
                    .Connection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\SERVICE\TEST\Projekt.ACCDB;" & _
                                  "Persist Security Info=False;Jet OLEDB:Database Password=ProJekt#00;"
                    .ConnectionBy = ConBy.ConByOLE
                    '.SQL_Befehl = "SELECT init.dbversion as Version FROM init WHERE init.parametername='DBV'"
                    '.SQL_Befehl = "SELECT COUNT(0) AS AnzahlDS FROM init"
                    .SQL_Befehl = "UPDATE init SET init.dbversion='2.01.01.215' WHERE init.parametername='DBV'"
                    '.StringResultJN = True
                    .StringResultJN = False
                    .Funktionsname = "ProgrammTest"
                    '.Execute = ExecuteCMD.vbScalar
                    .Execute = ExecuteCMD.vbNonQuery
                End With
                Try
                    Messpunkt = 2
                    cn = New System.Data.OleDb.OleDbConnection(dbcce.Connection)
                    cmd = New System.Data.OleDb.OleDbCommand(dbcce.SQL_Befehl, cn)
                    Messpunkt = 3
                    Try
                        cn.Open()
                        Select Case dbcce.Execute
                            Case ExecuteCMD.vbInsert
                            Case ExecuteCMD.vbNonQuery
                                Messpunkt = 4
                                dbcce.IntegerResult = cmd.ExecuteNonQuery()
                                Messpunkt = 5
                            Case ExecuteCMD.vbScalar
                                If dbcce.StringResultJN = True Then
                                    Messpunkt = 4
                                    dbcce.StringResult = cmd.ExecuteScalar().ToString
                                    Messpunkt = 5
                                Else
                                    Messpunkt = 4
                                    dbcce.IntegerResult = cmd.ExecuteScalar()
                                    Messpunkt = 5
                                End If
                            Case Else
                        End Select
                    Catch ex As Exception
                        MsgBox("MP 4 und 5 Error in Funktion (" & Messpunkt.ToString & ")", MsgBoxStyle.Critical, "MSDN_Forum")
                    Finally
                        cmd.Dispose()
                        cmd = Nothing
                        cn.Close()
                        cn = Nothing
                    End Try
                    Messpunkt = 6
                Catch ex As Exception
                    MsgBox("MP 2 und 3 Error in Funktion (" & Messpunkt.ToString & ")", MsgBoxStyle.Critical, "MSDN_Forum")
                End Try
            Catch ex As Exception
                MsgBox("Global Error in private Funktion after MP (" & Messpunkt.ToString & ")", MsgBoxStyle.Critical, "MSDN_Forum")
                f_cmd = New Structure_db_Conection_Command_Execute
                With f_cmd
                    .ErrorIndex = Messpunkt
                    .ErrorJN = True
                    .ErrorMSG = ex.Message
                End With
            End Try
            f_cmd = dbcce
        End Function
    End Module
    

    Anbei noch ein Protokollauszug:

    "20170111_04:02:13";"2310";"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"

    "20170111_04:02:13";"2310";"Memory warning    : Funktion f_cmd_SQL_integer"

    "20170111_04:02:13";"2310";"Call by Function  : nv"

    "20170111_04:02:13";"2310";"Memory Begin/End  : 59,75/102,43"

    "20170111_04:02:13";"2310";"Before/After  Exec: 60,05/102,52"

    "20170111_04:02:13";"2310";"Parameter f_SQL   : INSERT INTO t_VH SELECT t_i_VS13.* FROM t_i_VS13 LEFT JOIN (SELECT SANR FROM t_VH WHERE DC='169') AS tmp ON t_i_VS13.SANR = tmp.SANR WHERE ( ((tmp.SANR) Is Null) )"

    "20170111_04:02:13";"2310";"Parameter f_cn    : Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\XXXXX\AppData\Local\XXXXX\data\VH169DB.accdb;Persist Security Info=True;Jet OLEDB:Database Password=XXXXXXX;"

    "20170111_04:02:13";"2310";"Parameter f_pwd   : "

    "20170111_04:02:13";"2310";"Parameter DbExt   : .accdb"

    "20170111_04:02:13";"2310";"Parameter Execute : vbNonQuery"

    "20170111_04:02:13";"2310";"TimeStamp, before new connection   11.01.2017 04:02:12"

    "20170111_04:02:13";"2310";"TimeStamp, before open connection  11.01.2017 04:02:12"

    "20170111_04:02:13";"2310";"TimeStamp, before execute          11.01.2017 04:02:12"

    "20170111_04:02:13";"2310";"TimeStamp, after execute           11.01.2017 04:02:12"

    "20170111_04:02:13";"2310";"TimeStamp, before close connection 11.01.2017 04:02:13"

    "20170111_04:02:13";"2310";"TimeStamp, after close connection  11.01.2017 04:02:13"

    "20170111_04:02:13";"2310";"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"

    Vielen Dank und Grüße,

    Axel

    Mittwoch, 11. Januar 2017 14:44

Alle Antworten

  • Hi Axel,
    wenn Du ConnectionPooling nicht explizit ausschaltest, dann wird nach dem Close das Connection-Objekt im Pool gepuffert. Schalte das mal aus und prüfe dann.

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Mittwoch, 11. Januar 2017 15:15
  • Hallo Axel,

    ergänzend zu Peters Antwort:

    Bei Ole DB wird das Pooling (und anderes mehr) durch den OleDB Services Parameter in der Verbindungszeichenfolge gesteuert. Durch "-4" kann man das Connection Pooling abschalten - andere Flagwerte täten es auch, bringen aber bei .NET OleDb eher Probleme.

    Wobei man das Connection Pooling bei einer Jet Datenbank eher aktiviert lassen sollte, das ansonsten nicht nur die Datenbank geschlossen sondern auch die Puffer Jet internen Puffer freigegeben werden - sowie die Verbindung zur Systemdatenbank und zur Sperrdatei (.ldb). Was in der Summe bei jedem Öffnen / Schließen einiges an Zeit kosten und sich bei vielen kleinen Aktionen deutlich bemerkbar machen kann.

    Besser ist es da im allgemeinen eine Verbindung offen zu lassen und erst nach einiger Zeit an Inaktivität zu schließen wie es das Pooling tut.

    Damit die Daten auf Platte geschrieben werden und nicht im Speicher rumträumen sollte man bei ggf. eine (OleDb) Transaktion verwenden. (Wobei das bei Deiner gewöhnungsbedürftigen "Zugriffsstruktur" etwas an Redesign erfordern dürfte ;)

    Gruß Elmar

    Mittwoch, 11. Januar 2017 16:33
  • Hallo Peter,

    vielen Dank für Deine schnelle Antwort.

    Hatte schon darüber nachgedacht das Pooling auszuschalten, aber nach den Hinweisen im MSDN hatte ich mich dagegen entschieden. Hauptgrund dafür war aber auch, dass die Anwendung bis November funktionierte. Ich konnte mir keinen Reim darauf machen, warum das Ausschalten des Poolings nun Besserung bringen würde.

    Aber nun getestet und ... Fehler bleibt leider.

    Hier sogar bei Zugriff auf eine nicht mit Passwort geschützte Datenbank.

    Speicherbelegung
    vor Open 24,07 MB
    vor Execute 31,16 MB
    nach Execute 31,93 MB
    nach Close 32,31 MB

    Ich lasse mir derweilen jeden Speicherverbrauch größer 3 MB zwischen (vor Open) und (nach Close) protokollieren, da ich schon vermutet hatte, dass es an einem bestimmten SQL Befehl liegt. Hat sich leider auch als Niete herausgestellt.

    "20170111_19:30:50";"2600";"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    "20170111_19:30:50";"2600";"Memory warning    : Funktion f_cmd_SQL_string"
    "20170111_19:30:50";"2600";"Call by Function  : nv"
    "20170111_19:30:50";"2600";"Memory Begin/End  : 24,07/32,31"
    "20170111_19:30:50";"2600";"Before/After  Exec: 31,16/31,93"
    "20170111_19:30:50";"2600";"Parameter f_SQL   : SELECT SQL_String FROM SQL_Programm WHERE P_Modul='DB_VERS'"
    "20170111_19:30:50";"2600";"Parameter f_cn    : Provider=Microsoft.ACE.OLEDB.12.0;OLE DB Services=-4;Persist Security Info=False;Data Source=C:\Users\XXXXXXX\AppData\Local\XXXXXXXX\init\INS_0164.accdb;"
    "20170111_19:30:50";"2600";"Parameter DbExt   : .accdb"
    "20170111_19:30:50";"2600";"Parameter Execute : vbScalar"
    "20170111_19:30:50";"2600";"TimeStamp, before new connection   11.01.2017 19:30:49"
    "20170111_19:30:50";"2600";"TimeStamp, before open connection  11.01.2017 19:30:49"
    "20170111_19:30:50";"2600";"TimeStamp, before execute          11.01.2017 19:30:49"
    "20170111_19:30:50";"2600";"TimeStamp, after execute           11.01.2017 19:30:49"
    "20170111_19:30:50";"2600";"TimeStamp, before close connection 11.01.2017 19:30:49"
    "20170111_19:30:50";"2600";"TimeStamp, after close connection  11.01.2017 19:30:50"
    "20170111_19:30:50";"2600";"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"

    Bin Dankbar für jede weitere Idee!

    Viele Grüße,

    Axel

    Mittwoch, 11. Januar 2017 18:48
  • Hallo Elmar,

    hatte gerade Peter schon das Resultat vom ausschalten des Pooling mitgeteilt. War auch mein Ansinnen das Pooling aktiviert zu lassen.

    Mein Programm durchläuft immer wieder die gleiche Routinen, 8 Firmeninterne Web-Abfragen, txt Daten importieren (bis zu 80'000 Datensätze), aufbereiten und verarbeiten. Ein Durchlauf dauert ca. 6 Minuten und läuft dann erneut. Wie bereits angegeben bis November brauchte mein Programm selbst nach 3 Wochen ununterbrochener Laufzeit nicht mehr wie 28 MB Speicher. Seit November wird schon beim Start mit der ersten SQL Abfrage mehr Speicher belegt und nicht wieder freigegeben. Komischer Weise kommt der nächste Speicherfresser erst Stunden später und zwischendurch werden zig SQL Befehle ausgeführt.

    Hatte bisher eine Compress And Repair Funktion nach jedem Durchlauf eingeschaltet (wegen der vielen Importe) aber diese nun auf alle 24h zwischen 2 und 3 Uhr gelegt. Seitdem taucht der Speicherfresser nur noch alle 5-6 Stunden auf. Vorher fast alle 45 Minuten.

    Muss das Programm nun alle 2 Tage neu starten. Das ist echt nervig, da es autark laufen soll.

    Haben auch schon einen anderen Rechner ausprobiert, aber der Speicherfresser hat Wanderschuhe an.

    Könnte es mit dem Provider Microsoft.ACE.OLEDB.12.0, Windows 7, Office 2007 oder den Updates zu tun haben?

    Hier ein Screenshot der Updates:

    Windwos 7 Office 2007 Update November 2016

    Bin Dankbar für jede weitere Idee!

    Viele Grüße,

    Axel

    Mittwoch, 11. Januar 2017 19:16
  • Hallo,

    hast Du das ganze auch mal auf einem anderen Rechner mit gleichen Updates getestet?

    Plötzlich erhörter Speicherverbrauch kann auf einen defekten Ram hindeuten


    Gruß, Thomas

    Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!

    Icon für UWP

    Mittwoch, 11. Januar 2017 19:26
  • Hallo Thomas,

    ja, bereits auf 2 Rechnern. Und die Updates sind firmenintern alle gleich. Da wird man keinen Rechner mehr finden, der diese Updates nicht hat. Deinstallieren / Zurücksetzen geht auch nicht, da den Beschäftigten die Rechte dazu fehlen.

    Gruß, Axel

    Mittwoch, 11. Januar 2017 21:14