[求助]关于用dispose释放内存的问题

 所属目录:Java   |   类型:技术问答   |   时间:2007-05-21
 问题:

大家看看这样释放内存是否干净。  
  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过程正常释放内存吗?

· 网友精彩回答:

发表者:postren

做法1可以正常释放内存

发表者:beyondtkl

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进去   感觉上面两个事件的执行代码没什么差别,楼主说   第一种有问题,   我也只是感觉有问题,但现在看来,代码也没问题.

发表者:beyondtkl

in   system.pas  
   
  procedure   _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);   //   typeinfo  
   
  function   _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看看。  
   
 

发表者:alphax

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

发表者:luyang

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

发表者:zhouzhouzhou

高手就是高手  
   
  我怕下一步连01也搬过来了

发表者:mqjshanghai

帮你顶下,大哥给我5分,我要求不高

发表者:zswang

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

发表者:kl2000

楼上讲解好清楚啊!   又学习到一点.

发表者:alphax

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

发表者:heluqing

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

发表者:alphax

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)   =   1048576  
   
  in   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的大小为1m  
   
  dispose1()调用的是dispose(p);其中p为无类型指针,如果dispose(p);只释放4个字节的话,那么memoryleak应该大于1m,而输出的是memleak   =   12,说明trec所占用的空间被释放了,而泄露的这12字节就是tmyintfobj所占用的空间8个字节加上保存指针大小sizeflag的4个字节。  
 

.
处理 SSI 文件时出错
© 2006-2008 All Rights Reserved