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