大家看看这样释放内存是否干净。
var
  PRECA = ^TRECA;  TRECA = record
    s: string;
    i: Integer;
  end;procedure DoSth;
var
  LI: TListItem;
  tmpPRECA: PRECA;
begin
  New(tmpPRECA);
  tmpPRECA.s := 'hi';
  tmpPRECA.i := 123;
  LI := ListView1.Items.Add;
  LI.Caption := tmpPRECA.s;
  LI.Data := tmpPRECA;
end;procedure ClearLV;
var
  i: Integer;
begin
  for i := 0 to ListView1.Items.Count - 1 do
  begin
    if ListView1.Items.Item[i].Data <> nil then
      Dispose(ListView1.Items.Item[i].Data);//做法1
    if ListView1.Items.Item[i].Data <> nil then
      Dispose(PRECA(ListView1.Items.Item[i].Data));//做法2
  end;
end;
================================================
想请教在做法1里面,用Dispose过程正常释放内存吗?

解决方案 »

  1.   

    但是我试过,做法1并不能正确的释放内存,可以通过任务管理器观察到。当运行1万次添加LISTITEM动作,再调用函数释放内存,做法1只释放1部分内存,并不能完全释放。而做法2能够完全的释放内存。有朋友指点一下吗?
      

  2.   

    Dispose(PRECA(ListView1.Items.Item[i].Data));//做法2这样应该是比较稳妥的做法, 看汇编代码:  TBufRec = record
        szBuf: array[0..1024] of Char;
        nLen:  Integer;
      end;
      PBufRec = ^TBufRec;procedure TForm1.Button2Click(Sender: TObject);
    var
      pBuf: PBufRec;
      pv:   Pointer;
    begin
      New(pBuf);
      // mov  eax, $00000408 sizeof(BufRec); eax $00000408
      // call @Getmem
      // eax  00ce4afc
      // pBuf $CE4AFC
      pv := pBuf;
      Dispose(pv);
      // xor  edx, edx 将edx清零 EDX     Pointer to type info
      // call @FreeMem
    end;procedure TForm1.Button3Click(Sender: TObject);
    var
      pBuf: PBufRec;
      pv:   Pointer;
    begin
      New(pBuf);
      // mov  eax, $00000408 sizeof(BufRec);
      // call @Getmem
      pv := pBuf;
      Dispose(PBufRec(pv));
      // mov  edx, $00000408注意: 此时edx为sizeof(BufRec) EDX     Pointer to type info
      // call @FreeMem
    end;
    因为Pointer 就是 void* 它是不能用来计算的 虽然指针的大小在win32下都是4byte的,但是它覆盖的地址空间,是不能被计算出来的.
    但是 也比较奇怪 我跟踪Dispose进去 感觉上面两个事件的执行代码没什么差别,楼主说 第一种有问题, 我也只是感觉有问题,但现在看来,代码也没问题.
      

  3.   

    这样就奇怪了,说实在我不懂汇编,beyondtkl的代码我看不懂。
    不过我打开任务管理器,分别运行1万次添加数据的循环,然后分别用方法1和方法2来释放内存,结果是方法1的内存只释放一部分,而方法2的内存是完全释放的。
    按实际应用,我也一定选择方法2来释放内存的,只不过想深入一些了解DELPHI释放内存的机制,不过我的知识还不够,没能理解beyondtkl的代码,看看还有没有更多的讨论,2星期后结帖。
    感谢楼上2位的参与。
      

  4.   

    in system.pasprocedure _Dispose(p: Pointer; typeInfo: Pointer);
    asm
            { ->    EAX     Pointer to object to be disposed        }
            {       EDX     Pointer to type info            }        PUSH    EAX
            CALL    _Finalize
            POP     EAX
            CALL    _FreeMem
    end;procedure _Finalize(p: Pointer; typeInfo: Pointer);
    asm
            MOV     ECX,1
            JMP     _FinalizeArray
    end;procedure _FinalizeArray(p: Pointer; typeInfo: Pointer; elemCount: Cardinal); // typeInfofunction _FreeMem(P: Pointer): Integer;
    begin
      if P <> nil then
      begin
        Result := MemoryManager.FreeMem(P);
        if Result <> 0 then
          Error(reInvalidPtr);
      end
      else
        Result := 0;
    end;
    给你理了一下.....typeInfo: Pointer 这个参数很重要。。 你去System.pas P14305看看。
      

  5.   

    编译器不会知道你的Data的原来是什么类型的指针的,必须显式告诉编译器,
    所以方法1不对,方法2才对
      

  6.   

    理论上我觉得应该没什么区别...可能性是,在只指定一个pointer去释放内存时,delphi可能只是把pointer释放,而实际的内存块在变量表中还有一个指明的type - TRECA被引用(因为你在create的时候没有handle它,delphi可能会有在变量表中handle它), 而指定type后,在释放时delphi会查找引用此内存块的变量表中的handle,然后释放该内存...没有找到深层的内存管理资料..sorry.
      

  7.   

    Dispose()需要通过参数指针的类型判断释放空间的大小
    Dispose(Data)相当于释放4个字节(Pointer类型)
    Dispose(PRECA(Data))则释放16个字节(TRect类型)
    很明显如果用第一种方法将有12个字节的内存泄漏
      

  8.   

    zswang的说法也不对,dispose需要类型信息主要是为了清除结构的生存期自管理对象,就是例中的
    s: string;
    至于释放多少字节,是由指针本身分配时的大小决定的,dispose不理会这一点,它直接调用freemem,让freemem去查找指针原来分配的大小,大龙驹列出的原码已经说明了这一点如果PRECA被转换成无类型指针,则pRecA的s所分配的内存将遗漏(如果S被附值的话),但是pRecA结构所分配的内存则按其原来分配的大小被释放所以方法1将遗失所有记录的s的空间开销,但是记录的空间还是被正确释放了
      

  9.   

    记得前一段时间讨论过类似的问题
    dispose只是释放这个指针所占用的内存,和指针类型下的结构。
    如果不指定Data指针的类型,编译器不知道指针的类型,所以就没有办法完全释放的。
    个人觉得伴水说的比较对...
      

  10.   

    Data指针本是占用的内存有List释放,但是Data指向的东西由FreeMem释放,Data所指向的结构内的生存期自管理对象则由finalize负责清除,释放Data所指向的东西是不需要类型信息的其实很简单,我们平常总是GetMem(P, X); FreeMem(P);这样用
    我们都知道FreeMem(P)中的P是可以不需要指定大小的,并且P可以是无类型指针,也就是Pointer,FreeMem也能正确释放,说明Delphi对指针保留了指针已分配空间大小的信息,这个信息存放在被指向内容的前4个字节,这可以从getmem.inc源码中看出。那么既然Dispose调用了FreeMem,所以方法1的Dispose(Data)依然能够释放TRecA所占用的空间,只是因为没有类型信息,Finalize过程不起作用,也就是TRecA.S所指向的空间的内存被泄漏了。我做了个小程序,也说明了这一点program Project1;{$APPTYPE CONSOLE}uses
      SysUtils;const
      MBytes = 1024 * 1024;type
      TMyIntfObj = class(TInterfacedObject)
        destructor Destroy; override;
      end;  TRec = packed record
        I: IInterface;
        Stub: array[1..1*MBytes-SizeOf(IInterface)] of Byte;
      end;
      PRec = ^TRec;
    var
      HS: THeapStatus;
      SelfManagedObjReleased: Boolean;{ TMyIntfObj }  destructor TMyIntfObj.Destroy;
      begin
        SelfManagedObjReleased := True;
        inherited;
      end;  procedure Check;
      var
        CurrHS: THeapStatus;
        MemLeak: Integer;
      begin
        CurrHS := GetHeapStatus();
        MemLeak := CurrHS.TotalAllocated - HS.TotalAllocated;
        WriteLn('Memory leak = ', MemLeak);
        if not SelfManagedObjReleased then
          WriteLn('Boy, you lost!')
        else WriteLn('Ok, the self managed object is released.');
        WriteLn;
        WriteLn;
      end;  procedure MemBlkSzAllocatedForPtr(p: Pointer; const aPtrName: string);
      //返回为一个指针分配的块的大小,>= GetMem Size + SizeOf(Integer)
      const
        cThisUsedFlag = 2;
        cPrevFreeFlag = 1;
        cFillerFlag   = Integer($80000000);
        cFlags        = cThisUsedFlag or cPrevFreeFlag or cFillerFlag;  var
        SzFlags: PInteger;
        Sz: Integer;
      begin
        SzFlags := P;
        Dec(SzFlags);
        Sz := SzFlags^ and not cFlags;
        WriteLn('Memory block size allocated for ', aPtrName, ' = ', Sz);
      end;  procedure Dispose1();
      var
        R: PRec;
        P: Pointer;
      begin
        WriteLn('In Dispose1()');
        HS := GetHeapStatus();
        SelfManagedObjReleased := False;    New(R);
        R.I := TMyIntfObj.Create();    P := R;   //cast to untyped pointer    MemBlkSzAllocatedForPtr(P, 'P');    Dispose(P);     //try dispose the untyped pointer;    Check();
      end;  procedure Dispose2();
      var
        R: PRec;
      begin
        WriteLn('In Dispose2()');
        HS := GetHeapStatus();
        SelfManagedObjReleased := False;    New(R);
        R.I := TMyIntfObj.Create();    MemBlkSzAllocatedForPtr(R, 'R');    Dispose(R);    Check();
      end;begin
      WriteLn('SizeOf(TRec) = ', SizeOf(TRec));
      WriteLn;
      
      Dispose1();
      Dispose2();  Write('Press ENTER to continue...'); Readln;
    end.程序的输出:
    ========================================
    SizeOf(TRec) = 1048576In Dispose1()
    Memory block size allocated for P = 1048580
    Memory leak = 12
    Boy, you lost!
    In Dispose2()
    Memory block size allocated for R = 1048580
    Memory leak = 0
    Ok, the self managed object is released.
    Press ENTER to continue...
    ========================================首先TRec的大小为1MDispose1()调用的是Dispose(P);其中P为无类型指针,如果Dispose(P);只释放4个字节的话,那么Memoryleak应该大于1M,而输出的是MemLeak = 12,说明TRec所占用的空间被释放了,而泄露的这12字节就是TMyIntfObj所占用的空间8个字节加上保存指针大小sizeFlag的4个字节。