Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Memory Leaks bei Verwendung von System.JSON (https://www.delphipraxis.net/203377-memory-leaks-bei-verwendung-von-system-json.html)

TheSledgeHammer 12. Feb 2020 12:10

Delphi-Version: 10.3 Rio

Memory Leaks bei Verwendung von System.JSON
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo Community,

aus aktuellem Anlass mal eine Frage in die Runde bzgl. Memory Leaks verursacht durch Objekte aus der System.JSON.

Da gab es die letzten Jahre ja immer mal wieder Threads zu, auch hier im Forum. So richtig gelöst hat sich das aber nie so wirklich. Aktuell geht's in meinem Fall um Memory Leaks, wie sie im Anhang gezeigt werden. Laut den Meldungen soll irgendwo ein TJSONObject erzeugt worden sein, das wiederum TJSONPair-Objekte enthält und die wiederum TJSONSTring-Objekte. Soweit, so gewöhnlich.

Das Ding ist nur: ich erzeuge im Code 1 TJSONObject und erzeuge zwar TJSONStrings, die aber wiederum an das eine TJSONObject angehängt werden. Am Ende (= Destruktor) wird das eine TJSONObject brav zerstört und damit sollten sich eigentlich auch alle TJSONString-Objekte zerstören. Korrigiert mich bitte, wenn ich da falsch liege.

Offenbar klappt das auch, wenn ich den Code EINmal ausgeführt habe und dann die Anwendung schließe. Dann kommt kein MemLeak-Report. Führe ich den Code ZWEImal hintereinander aus, erhalte ich den Report wie im Anhang "2mal.png" gezeigt, bei einer DREIfachen Ausführung entsprechend dem Anhang "3mal.png". Ich denke, es wird klar, wie das weitergehen wird...

Doch was wird in dem Code genau gemacht? Kurz umrissen ist das ein TWebModule das auf HTTP-Post-Messages reagiert; ein lokaler Webserver also, den man via "http://localhost:3000/Pfad/zum/handler" abfragen kann. Die Abfrage bzw. der Body der Message ist eine JSON-Datei, die von der Struktur her wie folgt aussieht:
{
"selectedDataset": {
"idno": "1532",
"id_db": "1",
"db": "main"
},
"getValues": {
"projekt": "",
"appl": ""
}
}

Diese Datei enthält ein JSONObject, das wiederum aus 2 JSONObjects besteht. In diesen beiden Objekten befinden sich einfache JSONPairs. Das erste Objekt "selectedDataset" ist mal außen vor, worum es hier geht ist das zweite: "getValues". Jedes TJSONPair besteht ja aus dem JsonString und dem JsonValue. Wenn die HTTP-Anfrage zum Webserver kommt, ist der JsonValue immer empty (s.o.). Der Webserver füllt diesen Wert aber entsprechend aus. Und an dieser Stelle kommt der Konstruktor für einen TJSONString ins Spiel:

Delphi-Quellcode:
var
  TmpPair: TJSONPair;
  JsonObjectGetValues: TJSONObject;
// ...
for TmpPair in JsonObjectGetValues do
  TmpPair.JsonValue := CallbackReadFromSelectedDataset(Idno, IdDb, TmpPair.JsonString.Value);
Der Callback macht im Prinzip das hier:
Delphi-Quellcode:
Result := TJSONString.Create('irgendwas');

Wer sich fragt, wo denn das TJSONObject erzeugt wird, der findet die Antwort nachfolgend. Das ist quasi die allererste Zeile im Handler-Ereignis für die POST-Message:
Delphi-Quellcode:
FMainJsonObj := TJSONObject.ParseJSONValue(Request.Content) as TJSONObject;

Das "FMainJsonObj" hängt an dem TWebModul-Derivat als private-Attribut. Dieses Attribut wird im WebModuleDestroy-Ereignis dann wie folgt zerstört:
Delphi-Quellcode:
if FMainJsonObj <> nil then
  FreeAndNil(FMainJsonObj);
Also nur, um es nochmal klar zu fragen: wo kommen diese Memory Leaks her? Mir erschließt sich das überhaupt nicht, wenn man meinen Kenntnisstand voraussetzt, dass durch das Zerstören des Main-JSON-Objekts auch alle TJSONValue-Objekte automatisch zerstört werden. Und ganz besonders irritiert mich, dass das erst ab dem ZWEITEN Aufruf passieren soll.

Der schöne Günther 12. Feb 2020 12:30

AW: Memory Leaks bei Verwendung von System.JSON
 
Zitat:

Zitat von TheSledgeHammer (Beitrag 1457262)
wenn man meinen Kenntnisstand voraussetzt, dass durch das Zerstören des Main-JSON-Objekts auch alle TJSONValue-Objekte automatisch zerstört werden

Nicht immer. Wird irgendwo an der Owned-Eigenschaft etwas geändert? Ein standardmäßig erzeugter
Delphi-Quellcode:
TJsonString
hat
Delphi-Quellcode:
GetOwned() = True
, das sorgt eigentlich dafür dass er von seinem Parent freigegeben wird wenn es selbst endet. Ist das nicht der Fall, wird er auch nicht freigegeben.


Ansonsten solltest du unbedingt FastMM4 in der vollen Version einbauen, der kann dir bei Speicherlecks genau anzeigen wo es erstellt wurde.

Der schöne Günther 12. Feb 2020 12:43

AW: Memory Leaks bei Verwendung von System.JSON
 
Die konkrete Antwort ist wahrscheinlich folgende Zeile:

Delphi-Quellcode:
TmpPair.JsonValue := ...
Die Implementation von TJsonPair.SetJsonValue(..) sieht nur so aus:

Delphi-Quellcode:
procedure TJSONPair.SetJsonValue(const Val: TJSONValue);
begin
  if Val <> nil then
    FJsonValue := Val;
end;
Heißt: Er biegt einfach nur seine Referenz FJsonValue um. Richtig wäre wahrscheinlich erst zu schauen ob das bisherige FJsonValue.GetOwned() = True hat und dieses dann erst einmal freizugeben. Das tut er nicht, und das alte JsonValue verbleibt als Speicherleck. Zumindest in meinem Delphi 10.0 Seattle.

himitsu 12. Feb 2020 12:49

AW: Memory Leaks bei Verwendung von System.JSON
 
Die komplette JSON-Implementation von Delphi ist ein rießiger Graus, wenn es darum geht "selbst" JSON erstellen oder verändern zu wollen. (JSON lesen oder automatisch zu serialisieren und zu deserialisieren ist noch nutzbar)
Da kann man nur empfehlen die Komponenten anderer Hersteller zu nutzen.

Der schöne Günther 12. Feb 2020 12:55

AW: Memory Leaks bei Verwendung von System.JSON
 
Wir kommen leicht vom Thema ab, aber ich glaube ich habe schon schlimmeres gesehen.

Kennst du den TJsonObjectBuilder?
https://www.delphipraxis.net/198874-...-funktion.html

Oder analog von diesem guten Mann hier etwas in der Richtung:
https://github.com/flobernd/delphi-u...Utils.JSON.pas

TheSledgeHammer 12. Feb 2020 14:51

AW: Memory Leaks bei Verwendung von System.JSON
 
Also erstmal vielen Dank für die Posts und Nachfragen.

An der Owned-Eigenschaft ändere ICH nichts, also wenn dann macht das Delphi im Hintergrund ;) glaube ich an der Stelle aber nicht :)

Dass das Ganze ein Grauß ist, das haben mir andere Kollegen auch schon bestätigt, aber was soll ich sagen: es ist ja eigentlich nichts Großes zu implementieren und extra Komponenten dafür zu installieren, halte ich schon für recht übertrieben. Zumal da es eigentlich im Interesse des Herstellers sein sollte, dass die eigenen System-Komponenten nicht dazu führen, dass Speicherlecks enstehen.

Also zusammengefasst: dass Speicherlecks entstehen ist (leider) normal?!

Uwe Raabe 12. Feb 2020 14:54

AW: Memory Leaks bei Verwendung von System.JSON
 
Kannst du ein minimales Beispielprojekt anhängen, mit dem du das reproduzieren kannst? Ich finde, hier gibt zur Zeit es noch recht viel Spekulation.

TheSledgeHammer 12. Feb 2020 15:41

AW: Memory Leaks bei Verwendung von System.JSON
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1457290)
Kannst du ein minimales Beispielprojekt anhängen, mit dem du das reproduzieren kannst? Ich finde, hier gibt zur Zeit es noch recht viel Spekulation.

Hab ich versucht, hab ich an sich auch hin bekommen, bloß hat sich damit heraus gestellt, dass nicht das JSON der Übeltäter war, sondern das TWebModule. Aus Gründen, die ich noch untersuchen muss, wird dieses Modul zwei Mal instanziiert (und auch zerstört, das passt also an sich). Warum allerdings zweimal ist herauszufinden.

Fakt ist - und damit erklärt sich der Mem-Leak - dass das geparste Main-JSON-Objekt nur einmal freigegeben wird. D.h. nach EINmal aufrufen ist korrekt zerstört, nach ZWEImal aufrufen bleibt eine Instanz im Speicher usw. Was ich jetzt eben rausfinden muss, ist, wieso das TWebModule sich mehrmals erzeugt. Das ist der eigentliche Fehler.

Wobei ein Kollege von mir das gleiche Phänomen bestätigen kann, der macht allerdings was ganz was anderes mit JSON, da gibt's kein TWebModule oder ähnliches :)

Daher meine ursprüngliche Vermutung, dass es am JSON liegt -> falsch gedacht, mea culpa :duck:

BastiFantasti 4. Jun 2020 08:18

AW: Memory Leaks bei Verwendung von System.JSON
 
Hallo zusammen,

es liegt schon an der Delphi Implementierung des JSON Parsers.
Ich habe gerade das gleiche Problem. Ich polle eine REST Schnittstelle von Delphi aus, die mir einen JSON String zurück liefert. Nach kurzer Zeit bekomme ich die Meldung das der Speicher voll ist.

Ich versuche ein einfaches Beispielprogramm zu erstellen und stelle es hier ein.

Viele Grüße
Bastian

BastiFantasti 4. Jun 2020 08:49

AW: Memory Leaks bei Verwendung von System.JSON
 
Liste der Anhänge anzeigen (Anzahl: 1)
Sodele,

Projekt ist im Anhang.

Zum Testen:
- Projekt auf Debug kompilieren
- über IDE starten
- 1-2 mal die Schaltfläche btnParse klicken
- Anwendung beenden

FastMM4 zeigt die Leaks und schreibt ein Logfile im gleichen Verzeichnis


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:46 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz