RAM-Flutung beim Hantieren mit SQLite-Verbindung

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von mbfan.

    RAM-Flutung beim Hantieren mit SQLite-Verbindung

    Hallo,
    ich wende mich heute mit etwas an euch, wobei ich eindeutig am Ende meines Lateins angekommen bin. Wenn die hier vorgestellte Praxis idiotisch ist, freue ich mich natürlich über Alternativvorschläge.

    Ich betreibe einen Server für eine Software. Seitdem ich die Datenspeicherung auf eine SQLite-Verbindung umgestellt habe, müllt sich der RAM im laufenden Betrieb sehr schnell zu.
    Um einfachen Zugriff auf die Daten der Datenbank möglich zu haben, rufe ich diese als Beispiel so auf:

    VB.NET-Quellcode

    1. Public ReadOnly Property Tasks As List(Of Task)
    2. Get
    3. Dim data As DataTable = Server.dbConnect.getReader("SELECT ID FROM Tasks WHERE Spedition_ID = " & db_ID)
    4. If data.Rows.Count = 0 Then : Else
    5. Dim ret As New List(Of Task)
    6. For Each row As DataRow In data.Rows
    7. ret.Add(New Task(row.Item("ID")))
    8. Next
    9. Return ret
    10. End If
    11. Return Nothing
    12. End Get
    13. End Property

    "Server.dbConnect" ist eine von mir geschriebene Klasse, die folgendermaßen aussieht:

    VB.NET-Quellcode

    1. Imports Devart.Data.SQLite
    2. Public Class DatabaseConnector
    3. Private ConnectionString As String
    4. Public Sub New(ByVal connectionString As String)
    5. Me.ConnectionString = connectionString
    6. End Sub
    7. Function getConnection() As SQLiteConnection
    8. Dim dbConnect As New SQLiteConnection(ConnectionString)
    9. Do
    10. Try
    11. dbConnect.ConnectionTimeout = 100
    12. dbConnect.Open()
    13. Exit Do
    14. Catch ex As Exception
    15. System.Threading.Thread.Sleep(30)
    16. End Try
    17. Loop
    18. Return dbConnect
    19. End Function
    20. Public Function getReader(ByVal command As String) As DataTable
    21. Dim dbConnect As SQLiteConnection = getConnection()
    22. Dim da As New SQLiteDataAdapter(command, dbConnect)
    23. Dim dt As New DataTable()
    24. da.Fill(dt)
    25. dbConnect.Close()
    26. dbConnect.Dispose()
    27. GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced)
    28. Return dt
    29. End Function
    30. Public Sub execute(ByVal command As String)
    31. Dim dbConnect As SQLiteConnection = getConnection()
    32. Dim dbCommand As SQLiteCommand = dbConnect.CreateCommand
    33. dbCommand.CommandText = command
    34. dbCommand.ExecuteNonQuery()
    35. dbConnect.Close()
    36. dbConnect.Dispose()
    37. GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced)
    38. End Sub
    39. Public Function executeScalar(ByVal command As String) As Integer
    40. Dim dbConnect As SQLiteConnection = getConnection()
    41. Dim dbCommand As SQLiteCommand = dbConnect.CreateCommand
    42. dbCommand.CommandText = command
    43. Dim i As Integer = dbCommand.ExecuteScalar
    44. dbConnect.Close()
    45. dbConnect.Dispose()
    46. GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced)
    47. Return i
    48. End Function
    49. End Class

    Wie man in meiner Klasse sieht, habe ich schon versucht, den Garbage-Collector manuell anzustoßen (wenn das überhaupt der richtige Befehl ist)
    Ich bin bei der Programmierung davon ausgegangen, das Objekte wie die SQLite-Connection am Ende der Sub, die ihn enthält, "zum Abschuss freigegeben" werden und dann aus dem RAM entfernt werden. Dasselbe habe ich für die Funktionen angenommen, die die Propertys aufrufen. Deren Subs dauern im Maximalfall 10 Sekunden, nach denen die in der Property erstellten Objekte nicht mehr aufrufbar sind und dementsprechend gelöscht werden können.

    Der Server läuft mit mono-sgen auf einem Debian 8-System.

    Nun ist die Frage: Was mache ich falsch? Wie kann ich diese RAM-Flutung beheben oder verringern?
    Mit hoffenden Grüßen,
    Nico
    You should live for that what you belive. - Drag-Drop Beschreibung
    Von wie viel Speicher reden wir hier? 100Mb? 1Gb? Wie viel Arbeitsspeicher ist denn vorhanden auf dem System? Wie viel ist dort ständig frei?

    Innerhalb einer Property einen DB-Abruf machen?
    Kann man durchaus tun, ich hätte das jedoch eher in eine eigenständige Methode gepackt.
    Hat aber nichts mit deinem Speicherproblem zu tun.

    Und noch was SQL-Technisches:
    Deine Execute und ExecuteScalar Methode bekommen einen Fertigen SQL-String übergeben...
    Werden die von dir außerhalb zusammengesetzt? Werden die entsprechend escaped?
    Du hast nämlich absolut keinen Bock darauf, wenn dir jemand ein ;DROP TABLE Tasks; inne Textbox schreibt.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „EaranMaleasi“ ()

    zunächstmal vom GC die Finger lassen. Der funktioniert am besten, wenn man ihm nicht dazwischenfunkt.

    Dann wüsste ich viel zu vereinfachen, sehe aber allenfalls deinen DataAdapter als nicht disposed - vlt. isses das, vlt. auch nicht. Also machma:

    VB.NET-Quellcode

    1. Public Function getReader(ByVal command As String) As DataTable
    2. Using dbConnect = getConnection(), da As New SQLiteDataAdapter(command, dbConnect)
    3. Dim dt As New DataTable()
    4. da.Fill(dt)
    5. Return dt
    6. End Using
    7. End Function

    Benamung ist natürlich falsch, die Methode gibt eine DataTable zurück, keinen Reader.
    Auch scheinst du eine besondere SqLite-Dll zu verwenden - in meiner Dll tickt zB das mittm Timeout anders.
    Das kann auch der Hund sein, dass deine Dll ein MemoryLeak hat.

    Was noch eigentümlich schaurig ist, ist deine Tasks - Property: Die erstellt bei jedem Abruf eine neue Liste mit evtl. hunderten von Tasks drinne - kann nicht zufällig sein, dass dein Resourcen-Problem daher rührt?
    Ja, und das müsste eiglich auch immer abstürzen - evtl. ist da ein TryCatch drum - jedenfalls scheinst du noch mit den ungeeigneten vb-Voreinstellungen zu proggen, sodass du Datentypen evtl. noch garnet richtig auseinander halten kannst.
    Dringend empfohlen: Visual Studio - Empfohlene Einstellungen

    Edit: Ah - vlt. ist Task ja bei dir garnet, was Task bei mir ist (nämlich System.Threading.Tasks.Task). Dann kannman da natürlich nicht abschätzen, was das an Resource frisst, dann ist das nur auch besch...eiden benamt.
    Hallo,

    EaranMaleasi schrieb:

    Von wie viel Speicher reden wir hier? 100Mb? 1Gb? Wie viel Arbeitsspeicher ist denn vorhanden auf dem System? Wie viel ist dort ständig frei?

    Arbeitsspeicher auf dem System vorhanden sind 4 GB, normalerweise frei 3 GB.

    EaranMaleasi schrieb:

    Innerhalb einer Property einen DB-Abruf machen?
    Kann man durchaus tun, ich hätte das jedoch eher in eine eigenständige Methode gepackt.
    Hat aber nichts mit deinem Speicherproblem zu tun.

    Dies war eigentlich nur eine Vereinfachung für mich, um auf die Datenbankbestandteile wie auf normale Objekte zugreifen zu können.

    EaranMaleasi schrieb:

    Und noch was SQL-Technisches:
    Deine Execute und ExecuteScalar Methode bekommen einen Fertigen SQL-String übergeben...
    Werden die von dir außerhalb zusammengesetzt? Werden die entsprechend escaped?
    Du hast nämlich absolut keinen Bock darauf, wenn dir jemand ein ;DROP TABLE Tasks; inne Textbox schreibt.


    ErfinderDesRades schrieb:

    zunächstmal vom GC die Finger lassen. Der funktioniert am besten, wenn man ihm nicht dazwischenfunkt.

    Aye.

    ErfinderDesRades schrieb:

    Dann wüsste ich viel zu vereinfachen, sehe aber allenfalls deinen DataAdapter als nicht disposed - vlt. isses das, vlt. auch nicht. Also machma:

    VB.NET-Quellcode

    1. ...

    Benamung ist natürlich falsch, die Methode gibt eine DataTable zurück, keinen Reader.

    Danke für die Vereinfachung, und die Methode getReader gab auch ursprünglich nen Reader zurück - bis ich gemerkt habe (als jemand der hier zum ersten Mal mit SQL in VB umgeht) dass der Reader nach dem Closen natürlich keine Daten mehr lesen kann...

    ErfinderDesRades schrieb:

    Auch scheinst du eine besondere SqLite-Dll zu verwenden - in meiner Dll tickt zB das mittm Timeout anders.
    Das kann auch der Hund sein, dass deine Dll ein MemoryLeak hat.

    Diese DLL war die einzige die ich gefunden habe, die sowohl unter Windows als auch in Mono auf Linux funktioniert.

    ErfinderDesRades schrieb:

    Was noch eigentümlich schaurig ist, ist deine Tasks - Property: Die erstellt bei jedem Abruf eine neue Liste mit evtl. hunderten von Tasks drinne - kann nicht zufällig sein, dass dein Resourcen-Problem daher rührt?
    Ja, und das müsste eiglich auch immer abstürzen - evtl. ist da ein TryCatch drum - jedenfalls scheinst du noch mit den ungeeigneten vb-Voreinstellungen zu proggen, sodass du Datentypen evtl. noch garnet richtig auseinander halten kannst.
    Dringend empfohlen: Visual Studio - Empfohlene Einstellungen

    Edit: Ah - vlt. ist Task ja bei dir garnet, was Task bei mir ist (nämlich System.Threading.Tasks.Task). Dann kannman da natürlich nicht abschätzen, was das an Resource frisst, dann ist das nur auch besch...eiden benamt.

    Nein, Task ist bei mir nicht das Task, sondern ein Auftrag in der Software. Danke auf jeden Fall für die Empfohlenen Einstellungen, die ich mir gerne zu Gemüte führe.

    Danke für eure Hilfe, villeicht wird das mit dieser Antwort etwas klarer.
    Nico
    You should live for that what you belive. - Drag-Drop Beschreibung

    mbfan schrieb:

    Arbeitsspeicher auf dem System vorhanden sind 4 GB, normalerweise frei 3 GB.
    @mbfan Ich muss nochmal nachhaken, wie "groß" ist denn dein Speicherleck? Wie viel Speicher nimmt deine Anwendung ein?
    Aber gut, wenn der GC nicht aufräumt, ists wohl schon ein echtes Leck (auch wenn er wohl anders als der CLR-GC arbeitet)

    Und was wolltest du noch zum Thema SQL sagen? du hast es zitiert, aber nicht kommentiert.
    Die Software nimmt nach einer halben Stunde ca. 3 GB ein -> Serverauslastung bei 4 GB, nach einem Neustart mit selber Serverbenutzung dann wieder nur 1 GB.

    Ups ^^
    Ich wollte eigentlich fragen, wie man das besser machen könnte.
    You should live for that what you belive. - Drag-Drop Beschreibung

    mbfan schrieb:

    Ich wollte eigentlich fragen, wie man das besser machen könnte.

    Les dich mal ein zum Thema Parameter in SQLite bzw. SQL generell. Damit umgehst du, sehr einfach, jegliche Art von SQL-Injection und machst die Welt zu einem besseren Ort. ;)

    Hier mal ein Beispiel der Anwendung, die zweite Antwort mit 44+ Upvotes ist hier besonders zu beachten:
    stackoverflow.com/questions/80…rs-in-sqlite-with-c-sharp

    Ich hoffe nur, dass das mit deiner Library funktioniert.

    EaranMaleasi schrieb:

    Hier mal ein Beispiel der Anwendung, die zweite Antwort mit 44+ Upvotes ist hier besonders zu beachten:
    stackoverflow.com/questions/80…rs-in-sqlite-with-c-sharp

    Ich hoffe nur, dass das mit deiner Library funktioniert.

    Danke für den Link dazu :)
    Bezüglich der Library: Gibt es da villeicht eine bessere, die sowohl mit Windows als auch Mono funktioniert? Die Standard-Library stürzt auf Linux leider ab.

    EDIT: Anscheinend arbeitet nur der Mono-GC auf Linux nicht korrekt, auf Windows habe ich das Problem anscheinend nicht so sehr.
    You should live for that what you belive. - Drag-Drop Beschreibung

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „mbfan“ ()

    Das Problem scheint sich nun, indem ich anstatt DevArt Mono benutze, aufgelöst zu haben. Vielen Dank trotzdem an alle die mir hier geholfen haben :) Hat mir ein bisschen Lerneffekt beschert.
    You should live for that what you belive. - Drag-Drop Beschreibung