Memory leak on JSON array processing lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- ldvmikro · 2025-04-24 11:54 Hello, I have this JSon: Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i [    {      "id": "1",        "name": "Area 1",        "regions": [            {                "id": "1.1",                "zoneId": "1",              "name": "Espacio 1",                "zoneDescription": "Max=10009;Total=6200"            },            {                "id": "1.2",                "zoneId": "1",                "name": "Espacio 2",                "zoneDescription": "Max=10000;Total=0"            },            {                "id": "1.3",                "zoneId": "1",                "name": "Espacio 3",                "zoneDescription": "Max=10000;Total=-3222"            }        ]    },    {        "id": "2",        "name": "Area 2",        "regions": [            {                "id": "2.1",                "zoneId": "2",                "name": "Espacio 4",                "zoneDescription": "Max=10000;Total=0"            },            {              "id": "2.2",                "zoneId": "2",                "name": "Espacio 5",                "zoneDescription": "Max=10000;Total=0"            }        ]    }  ]  and code for process it: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses  Classes, SysUtils, fpjson, jsonparser; procedure ParseZonesJSON(const AJsonString: string);var  JSONData: TJSONData;  ZonesArray: TJSONArray;  ZoneObject, RegionObject: TJSONObject;  ZoneItem, RegionItem: TJSONEnum;  ZoneId, ZoneName, RegionId, RegionName, ZoneDesc, ZoneRegionId: string;begin  try    //JSON-line parsing    JSONData := GetJSON(AJsonString);     // Checking that its array    if not (JSONData is TJSONArray) then    begin      WriteLn('Error: Waitng JSON-array.');      Exit;    end;     ZonesArray := TJSONArray(JSONData);     // Reviewing all areas    for ZoneItem in ZonesArray do    begin      ZoneObject := TJSONObject(ZoneItem.Value);       // Get data for      ZoneId := ZoneObject.Get('id', '');      ZoneName := ZoneObject.Get('name', '');       WriteLn('Zone ID: ', ZoneId);      WriteLn('Zone Name: ', ZoneName);      WriteLn('Regions:');       // Get an array of regions      if ZoneObject.Find('regions', JSONData) and (JSONData is TJSONArray) then      begin        for RegionItem in TJSONArray(JSONData) do        begin          RegionObject := TJSONObject(RegionItem.Value);           // Extracting region data        RegionId := RegionObject.Get('id', '');          RegionName := RegionObject.Get('name', '');          ZoneDesc := RegionObject.Get('zoneDescription', '');          ZoneRegionId := RegionObject.Get('zoneId', '');           WriteLn('  - Region ID: ', RegionId);          WriteLn('    Zone ID: ', ZoneRegionId);          WriteLn('    Name: ', RegionName);          WriteLn('    Description: ', ZoneDesc);        end;      end;      WriteLn('------------------');    end;  finally    if Assigned(JSONData) then      JSONData.Free; // Freeing up the memory  end;end;  The code works but if I don't comment this block, Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  if ZoneObject.Find('regions', JSONData) and (JSONData is TJSONArray) then      begin        for RegionItem in TJSONArray(JSONData) do      begin          RegionObject := TJSONObject(RegionItem.Value);         // Extracting region data          RegionId := RegionObject.Get('id', '');          RegionName := RegionObject.Get('name', '');          ZoneDesc := RegionObject.Get('zoneDescription', '');          ZoneRegionId := RegionObject.Get('zoneId', '');           WriteLn('  - Region ID: ', RegionId);          WriteLn('    Zone ID: ', ZoneRegionId);          WriteLn('    Name: ', RegionName);          WriteLn('    Description: ', ZoneDesc);        end;      end;      WriteLn('------------------');    end;  this line in particular, Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  if ZoneObject.Find('regions', JSONData) and (JSONData is TJSONArray) then  the RAM consumption goes up until the app won't close. Any idea what is the reason? Thanks. ---------------------------------------------------------------------- paweld · 2025-04-24 12:14 In this line: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if ZoneObject.Find('regions', JSONData) and (JSONData is TJSONArray) thenyou are replacing the JSONData variable with a new class, not releasing the previous instance. all you need to do is add a new variable for the purpose of this loop: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure ParseZonesJSON(const AJsonString: string);var  JSONData, jsondata2: TJSONData;  ZonesArray: TJSONArray;  ZoneObject, RegionObject: TJSONObject;  ZoneItem, RegionItem: TJSONEnum;  ZoneId, ZoneName, RegionId, RegionName, ZoneDesc, ZoneRegionId: string;begin  try    //JSON-line parsing    JSONData := GetJSON(AJsonString);     // Checking that its array    if not (JSONData is TJSONArray) then    begin      WriteLn('Error: Waitng JSON-array.');      Exit;    end;   ZonesArray := TJSONArray(JSONData);     // Reviewing all areas    for ZoneItem in ZonesArray do    begin      ZoneObject := TJSONObject(ZoneItem.Value);       // Get data for      ZoneId := ZoneObject.Get('id', '');      ZoneName := ZoneObject.Get('name', '');       WriteLn('Zone ID: ', ZoneId);      WriteLn('Zone Name: ', ZoneName);      WriteLn('Regions:');       // Get an array of regions      if ZoneObject.Find('regions', jsondata2) and (jsondata2 is TJSONArray) then      begin        for RegionItem in TJSONArray(jsondata2) do        begin          RegionObject := TJSONObject(RegionItem.Value);           // Extracting region data        RegionId := RegionObject.Get('id', '');          RegionName := RegionObject.Get('name', '');          ZoneDesc := RegionObject.Get('zoneDescription', '');          ZoneRegionId := RegionObject.Get('zoneId', '');           WriteLn('  - Region ID: ', RegionId);          WriteLn('    Zone ID: ', ZoneRegionId);          WriteLn('    Name: ', RegionName);          WriteLn('    Description: ', ZoneDesc);        end;      end;      WriteLn('------------------');    end;  finally    if Assigned(JSONData) then      JSONData.Free; // Freeing up the memory  end;end; ---------------------------------------------------------------------- H₂SO₄ · 2025-04-24 14:12 In addition to what @paweld said, I'd also recommend initializing  JSONData  to  Nil  before calling  GetJSON. This is because  GetJSON  is a regular function and not a constructor, meaning that if an exception is raised (such as when the input JSON is malformed), no destructor runs and the result is never assigned, which will lead to the  finally  block (probably) calling  Free  on an uninitialized reference. (By the way, checking  Assigned  before calling  Free  is redundant;  Free  already does that for you.) Another peculiarity of  GetJSON  is that it may simply return  Nil  without throwing an exception if the input string is empty. You've already got this case covered by checking  is TJSONArray,  but it's another pitfall one needs to be wary of.  To recap, this is the minimal skeleton I'd recommend for parsing JSON: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure ExampleParseJSON(const Source: String);var  Data: TJSONData = Nil;begin  try    Data := GetJSON(Source);    if Data = Nil then raise Exception.Create('JSON source is empty'); // Alternatively you could call Exit()    // Do actual processing [...]  finally    Data.Free();  end;end; ---------------------------------------------------------------------- ldvmikro · 2025-04-24 14:46 By modifying the code according to the comments, everything works perfectly. I appreciate your great help in solving my problem. Thank you @paweld and @Khrys for your immediate help.