0

I run a small Windows program which uses Indy10 server and client and processing data.

Compiled using the latest 11.3 compiler.

When I close the program, in the memory loss report (which I have enabled), I see this, after one day:

25 - 40 bytes: UnicodeString x 235939, Unknown x2.

As I don't Create Unicode strings, where in the program should I be looking for this?

Can it be, for example, a TStringList which is not Free'd?

Somewhere, I must be missing something in the area where new data is continuously be processed, but I have been going through it in detail and cannot find it.

Unless it has something to do with the Indy10 TCPServer or TCPClient or UDPServer...

Update: Basically my question is in which circumstances it is possible that a standard String is not freed in Delphi. The program is an 'AIS decoding' program, AIS vessel traffic. It uses 3 different Indy10 inputs, a TCPClient, TCPServer and an UDPServer. The data (a string inside a Record) is then send to the Main Thread using PostMessage. This is a standard system I always use. The pointer to this Record is always Disposed of. The problem must be in the actual decoding of the AIS data, which is quite complicated. There are a few code snippets which may be related:

BitString := '';
    if not CheckNMEA(string(Sentence)) then
    begin
      if DebugLevel >= 1 then LocalLog('DecodeAIS: Checksum error: '+Sentence,d_error);
      exit;
    end;
    Idx := LastDelimiter('*', Sentence);
    if Idx <> 0 then
    begin
      Checksum := Copy(Sentence, Idx+1, MaxInt);
      SetLength(Sentence, Idx-1);
    end;

Can the 'SetLength' causes problems, if the length is bigger that actually required?

Then these two functions are called a LOT (GetInteger and GetCardinal):

function BinToInt(Value: String): cardinal;
var i: Integer;
begin
  Result:=0;
  try
  //remove leading zeroes
    while Copy(Value,1,1)='0' do
     Value:=Copy(Value,2,Length(Value)-1) ;
  //do the conversion
    for i:=Length(Value) downto 1 do
     if Copy(Value,i,1)='1' then
      Result:=Result+(1 shl (Length(Value)-i)) ;
  except
    on E:Exception do
    begin
      LocalLog('BinToInt: '+E.Message,d_error);
      raise;
    end;
  end;
end;


function Bits2Int(BitStr : string) : integer;
var i : byte; sign :integer;
begin
  sign := 1;
  Result := 0;
  if copy(BitStr,1,1)='1' then
  begin
    Sign := -1;
    for i := 1 to length(BitStr) do
    if BitStr[i] = '1' then BitStr[i] := '0' else BitStr[i] := '1';
  end;
  // writeln(BitStr); // 1's complement bitstring
  for i := 1 to length(BitStr) do
  begin
    if BitStr[length(Bitstr)-i+1] = '1' then
      Result := Result+trunc(Power(2,i-1));
  end;

  // finally adding 1 to get 2's complement bitstring
  if Sign=-1 then
  begin
    Result := -1*(Result+1);
  end;
end;

function GetCardinal(StartBit, BitsWanted: Integer): Cardinal;
begin
  result := 0;
  try
    result := BinToInt(copy(BitString,StartBit+1,BitsWanted));
  except
    on E:Exception do
    begin
      if DebugLevel >= 1 then LocalLog('GetCardinal: '+E.Message,d_error);
      raise;
    end;
  end;
end;


function GetInteger(StartBit, BitsWanted: Integer): integer;
begin
  result := 0;
  try
    result := Bits2Int(copy(BitString,StartBit+1,BitsWanted));
  except
    on E:Exception do if DebugLevel >= 1 then
    begin
      LocalLog('GetInteger: '+E.Message,d_error);
      raise;
    end;
  end;
  // LocalLog('GetInteger2: Bits='+copy(BitString,StartBit+1,BitsWanted)+'  Result='+IntToStr(result));
end;

If anybody sees something in here what I missed, let me know.


Resolved: What has happened here, is that the Unicode String involved was part of a TRecord (which also contained a lot of other variables). This Record was re-used every time new external data came in, at which point, before filled with new data, it was cleared with a

fillchar(myrecord,sizeof(myrecord),#0);

It has now become clear that this caused the MemoryManager to loose access to the String, and it could not Free it.

The result was hundreds of thousands of Strings memory losses.

Resolved by removing the 'fillchar' of the Record. Learned something new here...

Bart Kindt
  • 142
  • 13
  • 3
    It is very unlikely that Indy itself is leaking strings, so something like a leaked TStringList would make sense, yes. But, there is simply not enough info provided for us to diagnose that for you. But, if you use the full version of FastMM, it will tell you exactly where those leaks are coming from. – Remy Lebeau Mar 28 '23 at 21:25
  • Alternatively use EurekaLog or madExcept or Deleaker – Delphi Coder Mar 29 '23 at 05:11
  • "As I don't Create Unicode strings" but you are very likely using `string` type somewhere. `UnicodeString` is alias for default `string` type in Unicode Delphi versions. Application is leaking strings, but It is unlikely that it is leaking `TStringList` because it would show in report as `TStringList xN`. – Dalija Prasnikar Mar 29 '23 at 06:23
  • @DalijaPrasnikar unless maybe a `TStringList` (or similar object/container) is one of the `Unknown`s, ie if for whatever reason its RTTI wasn't accessible to identify it. – Remy Lebeau Mar 29 '23 at 16:37
  • @BartKindt Good that you found a solution. The correct way to resolve this post would be to [move your last edit to an answer](https://stackoverflow.com/help/self-answer) and then [accept it](https://stackoverflow.blog/2009/01/06/accept-your-own-answers/), rather than editing the question in the first place. – Remy Lebeau May 03 '23 at 15:34
  • @BartKindt In any case, another solution would be to precede the `FillChar()` with [`Finalize()`](https://docwiki.embarcadero.com/Libraries/en/System.Finalize) first, eg: `Finalize(myrecord); FillChar(myrecord, Sizeof(myrecord), 0);`, or in modern Delphi versions you can use [`Default()`](https://docwiki.embarcadero.com/Libraries/en/System.Default) instead, eg: `myrecord := Default(myrecordtype);` – Remy Lebeau May 03 '23 at 15:35
  • @Remy That is interesting, "Default()" never heard of that. I also thought I did click on that button, 'Answer your own question'. I may not have been avaliable any more at the time because you closed the question? – Bart Kindt May 04 '23 at 19:01
  • @BartKindt I'm not the person who closed it – Remy Lebeau May 04 '23 at 19:11
  • Ok, sorry. Still do not fully understand how stackoverflow works. I barely use it. – Bart Kindt May 05 '23 at 01:06

0 Answers0