上篇文章跟大家一起学习了TLIST释放不了的问题,但是应用到项目后,仍然发生了在某些情况下释放不了的情况。在此我简单的把问题描述下。项目中有很多数据阶段性操作,例如第一步先取1万笔数据放LIST做前期,第二步再取1万笔单据更新,第三步根据第一步和第二步数据产生一万笔合计,第四步将合计数据写到临时表,第五步从临时表显示到网格中。为了节省内存一般我们把前面用不到的数据先释放,所以第三步做完后,就可将第一步和第二的数据释放。但是事实是,虽然我们按上篇文章中所说用显示结构类型释放,但是仍然释放不了前两步的内存。在经过一天的研究后,也了解了原因,现再跟大家分享下:
为方便说明问题,还是以一个独立小的程序为例来说明:
结构定义:
type
TBatchKey = record
int1:integer;
int2:integer;
f2 :double;
f3 :double;
sKind: string;
sSlipno: string;
dtTime : TDateTime;
end;
pBatchKey = ^TBatchKey;
过程操作:
procedure TForm1.Button1Click(Sender: TObject);
var
i:integer;
list,list2:TList;
p1,p2:pBatchKey;
begin
list := TList.Create;
list2 := TList.Create;
try
for i:=0 to 1000000 do
begin
p1 := new (pBatchKey);
p1.int1 :=i;
p1.int2 := i+1;
p1.f2 := i+1;
p1.f3 := i+3;
p1.dtTime := now;
p1.sKind := 'test'+inttostr(i);
p1.sSlipno := 'tes2t'+inttostr(i);
list.Add(p1);
end;
for i:=0 to 1000000 do
begin
p2 := list[i];
p1 := new (pBatchKey);
p1.int1 :=p2.int1;
p1.int2 :=p2.int2;
p1.f2 :=p2.f2;
p1.f3 := p2.f3;
p1.dtTime := p2.dtTime;
p1.sKind :=p2.sKind;
p1.sSlipno := p2.sSlipno;
list2.Add(p1);
end;
//开始释放
for i:=0 to list.Count-1 do
dispose(pBatchKey(list[i]));
list.Clear;
list.Free;
showmessage('aa');
finally
if list.Count>1 then
begin
for i:=0 to list.Count-1 do
dispose(pBatchKey(list[i]));
list.Clear;
list.Free;
end;
if list2.Count>1 then
begin
for i:=0 to list2.Count-1 do
dispose(pBatchKey(list2[i]));
list2.Clear;
list2.Free;
end;
end;
end;
经过测试,红色字体的开始释放处,并不能释放内存,只有到FINALLY后才能全部释放。经过一些时间的研究已经找到原因(限于篇幅不详细说明查的过程)。DELPHI在处理字符串时,采用的是”计数引用“方式,即同一字符串被多次赋值时,不会拷贝多份内存,而是在字符串的首位偏移两字节处记录引用次数,当释放字符串时,系统会自动判断该引用是否是1,如果大于1则不释放。因为我们小程序中LIST2中有二个字符串字段是直接引用LIST1中内容,所以导致LIST1中字符串字段计数引用变成2了,所以不能释放。经多次测试除了字符串以外,如果结构中有指针型的引用,例如嵌套LIST,也会引起多引用产生的不能释放。
解决办法是彻底删除两个LIST间的引用关系,在DELPHI下字符串拷贝有现成的系统函数COPY。将 LIST2赋值的代码改成如下就可正确释放:
p1.sKind :=copy(p2.sKind,1,length(p2.sKind));
p1.sSlipno := copy(p2.sSlipno ,1,length(p2.sSlipno ));
如果结构中有地址引用字段,例TLIST,则要先释放子LIST内存,再将该字段赋值成NIL。如下:
for i:=0 to list.Count -1 do
begin
p := list[i];
for j:=0 to p.ListSStkHouse.Count-1 do
begin
p2 := p.ListSStkHouse[j];
dispose(p2);
end;
p.ListSStkHouse.Clear;
p.ListSStkHouse.free;
p.ListSStkHouse := nil;
end;
if list.Count>1 then
begin
list.Clear;
if isfree then
list.Free;
end; |