设为主页 销售咨询 客服电话  
   
 
 
 
 
delphi 内存释放小误区<=>
2016-02-19

        上篇文章跟大家一起学习了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;

   
 
   
 
安徽鸿奥软件有限公司
版权所有,翻版必究 ◎2007-2008copyright
皖ICP备17008567号-1
皖公网安备 34030002020793号