I'm working on upgrading an old program for an old employer of mine. The project produces Postscript based on input files (layout files and data files, producing printer-ready ID cards). The original project was in Delphi 1.0 and Visual C++ 1.5; later on I wrote a Delphi component which does the Postscript generation in Delphi 5, and this was used in subsequent projects.
Now I have been called back to rewrite the front end, using Delphi 5 and the Delphi 5 component (with some minor additions to support Unicode). During testing, some major memory leaks were found, which apparently have always been there, but never been noticed before. The new card layout, which uses lots of graphics, has exacerbated the problem.
I downloaded AQTime (
http://www.automatedqa.com) (which, at this point, let me highly recommend; I will be buying it when I finally get paid for this project) to search for the source of the memory leaks. It showed me a few which were simple to solve, but the bulk of them point to this or something like this:
Fpage.FActualPostscriptDat
a.Strings[
FPage.FCur
rentIDSide
-1] :=
Fpage.FActualPostscriptDat
a.Strings[
FPage.FCur
rentIDSide
-1] +
TTF.ProducePostscript;
TTF is a method which generates and returns a very large string (it can be several hundred K), and the stringlist Fpage.FActualPostscriptDat
a contains the strings, per ID, of each of the cards). The leak is actually on the concatenation, where the AQTime shows the concatenation calling to LStrCat3, which calls eventually to VirtualAlloc in GetMem.Inc; and this memory is never freed.
I have tried to move the concatenation to local strings, and made the method function returning a string to a method procedure operating on a string which belonged to the object:
TTF.ProducePostscript;
helper := Fpage.FActualPostscriptDat
a.Strings[
FPage.FCur
rentIDSide
-1];
helper1 := TTF.ProducedPostscript;
Fpage.FActualPostscriptDat
a.Strings[
FPage.FCur
rentIDSide
-1] := helper + helper1;
Here, too the leak occurred on the concatenation. I tried concat(helper, helper1). No good. I tried delete and insert.
TTF.ProducePostscript;
helper := Fpage.FActualPostscriptDat
a.Strings[
FPage.FCur
rentIDSide
-1];
helper1 := TTF.ProducedPostscript;
Fpage.FActualPostscriptDat
a.Delete(F
Page.FCurr
entIDSide-
1);
Fpage.FActualPostscriptDat
a.Insert(h
elper+help
er1,FPage.
FCurrentID
Side-1);
Didn't help. Tried using a dynamic array to avoid the concatenation:
var
copier : array of char; (this is local to the procedure doing this)
...
SetLength(Copier,
Length(Fpage.FActualPostsc
riptData.S
trings[FPa
ge.FCurren
tIDSide-1]
)
+ Length(TTF.ProducedPostscr
ipt));
Move(Fpage.FActualPostscri
ptData.Str
ings[FPage
.FCurrentI
DSide-1][1
],Copier[0
],
Length(Fpage.FActualPostsc
riptData.S
trings[FPa
ge.FCurren
tIDSide-1]
));
Move(TTF.ProducedPostscrip
t[1],
Copier[Length(FPage.FActua
lPostscrip
tData.Stri
ngs[FPage.
FCurrentID
Side-1])],
Length(TTF.ProducedPostscr
ipt));
SetLength(helper,Length(Co
pier));
Move(Copier[0],helper[1],L
ength(Copi
er));
SetLength(Copier,0);
Copier := nil;
FPage.FActualPostscriptDat
a.Strings[
FPage.FCur
rentIDSide
-1] := helper;
helper := '';
Now AQTime shows the memory of the first SetLength(Copier, etc.) as unreleased, despite the SetLength(Copier,0) and Copier := nil. It's actually worse than the string version.
I am at a loss here - what could cause these leaks?