delphi dispose释放内存的方法 New 和 GetMem 的区别

来自:http://blog.sina.com.cn/s/blog_4bc47d2301018trf.html

------------------------------------------------------------------

delphi dispose释放内存的方法NewGetMem的区别

定义一个record 类型,经过多次new dispose后,从windows任务管理器看,占用的内存比启动时大了很多,似乎越来越大

设置 ReportMemoryLeaksOnShutdown := true; 再运行,仍然没有提示 memory leak。

其实就是dispose 本身的原因。

delphi设计的 dispose 释放内存时,只是标记这部分内存可以再用来被 new 等函数分配,并不是把从系统申请到的内存归还给操作系统,只在程序结束时,才全部释放给操作系统。

比如 new 申请 15 个记录(sizeof=64字节) 的空间,然后 dispose 释放。再使用 new 申请 10个,此时这 10 个就不再请求系统了,直接从刚才的 15个 (此时已经空闲) 中分10 个出来。只有在占用的空闲内存不够使用时,才请求操作系统分配内存(剩余部分)。

若前一次15个空间地址如左列,释放后,下一次10个空间的地址如右列,即从前次分配的最后一个地址开始,按前次的顺序,倒过来分配10个。

00F23860

00F23818

00F237D0

00F23788

00F23740

00F236F8

00F236B0

00F23668

00F23620

00F235D8

00F23590

00F23548

00F23500

00F234B8

00F23470

00F23470

00F234B8

00F23500

00F23548

00F23590

00F235D8

00F23620

00F23668

00F236B0

00F236F8

如果操作一个 record 指针中的字符串变量,会不会丢失 string 的内

存空间,造成内存泄漏?

结果是:使用 New() 分配的内存,会自动初始化 record 的内容,并且在 Dispose 时自动

清除所有已分配的内存,包括 string 或其他动态数组的内存。GetMem/FreeMem 没有这个

性质。事实上,New() 中调用了 GetMem,并且执行了一些初始化的操作。

代码如下:

type

PMyRecord = ^TMyRecord;

TMyRecord = record

I: Integer;

S: string;

V: Variant;

end;

{;$DEFINE NEW}

procedure TForm1.Button1Click(Sender: TObject);

var

R: PMyRecord;

I: Integer;

begin

for I := 1 to 1024 do

begin

{$IFDEF NEW}

New(R); //正确将 R.S 初始化

SetLength(R.S, $FFFF);

Dispose(R); // 正确释放 R.S 内存空间

{$ELSE}

GetMem(R, SizeOf(TMyRecord));

R.S := ''; // 出错

SetLength(R.S, $FFFF);

FreeMem(R);

{$ENDIF}

end;

end;

===================================================

GetMem只负责分配空间,不会负责清空刚分配的空间,如果需要分配来就

清空的空间可以用 AllocMem,AllocMem = GetMem + FillChar

===================================================

哦,我上面犯错误了,以为 S = 0 时也会出错,所以没有 FillChar,其实不会。

这样就可以正确测出结果了:

(将 $DEFINE NEW 前面的 ; 去掉,可以从任务管理器中查看二种方法的内存占用)

type

PMyRecord = ^TMyRecord;

TMyRecord = record

I: Integer;

S: string;

V: Variant;

end;

{;$DEFINE NEW}

procedure TForm1.Button1Click(Sender: TObject);

var

R: PMyRecord;

I: Integer;

begin

for I := 1 to 1024 do

begin

{$IFDEF NEW}

New(R); //正确将 R.S 初始化

SetLength(R.S, $FFFF);

Dispose(R); // 正确释放 R.S 内存空间

{$ELSE}

GetMem(R, SizeOf(TMyRecord));

FillChar(R^, SizeOf(TMyRecord), #0);

SetLength(R.S, $FFFF);

FreeMem(R); // 不会释放 R.S 内存空间 !!

{$ENDIF}

end;

end;

===================================================

是的,FreeMem 不会释放其中的生存期自动管理的内容,因为在 FreeMem

看来,那些都是一致的二进制数据,没有任何意义可言。不过可以通过一个

Finalize 调用(或者 FinalizeRecord)解决该问题 我估计 Dispose 就直接

或间接调用了 Finalize

===================================================

呵呵,不用估计,帮助中明确地说明了。

In Delphi code, FreeMem destroys the variable referenced by P and returns its memory to the heap. If P does not point to memory in the heap, a runtime error occurs. If P points to a structure that includes long strings, variants, dynamic arrays, or interfaces, call Finalize before calling Freemem.

......

Note: It is preferable to use the New and Dispose procedures rather than GetMem and FreeMem. When using New and Dispose, there is no need to explicitly call Finalize.

===================================================

guttier wrote:

呵呵,不用估计,帮助中明确地说明了。

In Delphi code, FreeMem destroys the variable referenced by P and returns its memory to the heap. If P does not point to memory in the heap, a runtime error occurs. If P points to a structure that includes long strings, variants, dynamic arrays, or interfaces, call Finalize before calling Freemem.

......

Note: It is preferable to use the New and Dispose procedures rather than GetMem and FreeMem. When using New and Dispose, there is no need to explicitly call Finalize.

这段英文我翻译一下。

在DELPHI代码中,FreeMem根据变量所引用的指针释放内存,并将内存归还给堆。如果指针不是指向堆中的内存地址,将发生一个运行时错误。如果指针所指向的是一个数据结构,且其中包含有长字符串、Variants、动态数组、或接口,则在使用用FreeMem之前须调用Finalize ”

"注意:使用New 和 Dispose 过程要强于使用GetMem与FreeMem。但我们使用New和Dispose的时候,不需要显示的调用Finalize "

翻译完了。肯定有不准确的地方。

不过我有一个问题,内存分配既然new和Dispose要比GetMem与FreeMem容易使用,那么还有没有必要使用GetMem、FreeMem,在什么情况下使用它们?

===================================================

内存分配既然new和Dispose要比GetMem与FreeMem容易使用

容易使用”通常只能是一个相对的概念,在这里,我们讨论指向结构体的指针,在这种情况下,New/Dispose 通常是易用的。但是它们是有局限的,就是那个指针指向的空间的大小必须能够在编译期间确定,它们才知道需要分配多大的空间。对于指向结构体的指针,这个值就是结构体的大小,这当然是确定的,所以能够使用它们,并且能够带来便利。

可是还有一些情况,例如你只有一个 Pointer 类型,这是无类型指针,你把它传给 New,编译器就不知道它指向的是什么内容,也就不知道它指向的空间有多大,也就不知道需要分配多少空间,就根本不能用,更不用说易用了。

还有没有必要使用GetMem、FreeMem,在什么情况下使用它们?

当然有了,如前面提到的,相对来说,New/Dispose 操作更为高层一些,我们通常用它们来操作指向结构体的指针,即是说指针指向的内容是编译期间就已知的数据结构。而当我们需要更加低级的去操作一些内存空间的时候,比如你要自己处理字符串的时候,你的指针指向的就是只有你自己才知道或者说是你自己去进行理解的内存空间,没有编译期间的明确的数据结构与之对应。这个时候,就要用到 GetMem/FreeMem 了。

所以说,New/Dispose 的局限性实际上是很大的,或者说适用范围是很小的,而 GetMem/FreeMem 给了我们充分的自由,试用范围更广。当然,具体选用哪个,还要看实际情况而定。