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:
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:
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.