当前位置:首页
开发技术指南» 文章正文
    引言:

 ·udp数据包大小问题    »显示摘要«
    摘要: 问题如下: 我有tidudpserver控件(indy的),和nmudp控件(fastnet面版里面),我用端口映射把本udp的udp端口映射到本网的外网的ip,当我向这个外网端口发数据的时候,只要数据包大于1464个字节,就收不到,小于等于1464字节都可以收到,另外一个外网的ip向我这个外网端口发数据的时候情况也一样。奇怪的是我向本网内的其它机器和我本机发大于1464字节的数据,却可以......
    摘要: 问题如题 carts.html: <html> <head> <title>请选择cd</title> </head> <body> <form action = "carts.jsp" method="post"> <select name="......


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

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

NO.1   作者: postren

做法1可以正常释放内存

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

NO.3   作者: 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看看。  
   
 

NO.4   作者: alphax

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

NO.5   作者: LuYang

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

NO.6   作者: zhouzhouzhou

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

NO.7   作者: mqjshanghai

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

NO.8   作者: zswang

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

NO.9   作者: kl2000

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

NO.10   作者: alphax

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

NO.11   作者: heluqing

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

NO.12   作者: 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个字节。  
 


    摘要: 需要pb 相关技术的文章,最好原著是英文,且有中文的翻译。 2 天内结帖,人多了,另外开帖! ......
» 本期热门文章:
· 热门栏目:
» 相关精选文章
» 其它相关:

©2000-2007 All Rights Reserved. 最佳浏览:1024X768 MSIE