delphi dispose釋放內存的方法 New 和 GetMem 的區別【轉】


delphi dispose釋放內存的方法 New  GetMem 的區別

 

定義一個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 給了我們充分的自由,試用范圍更廣。當然,具體選用哪個,還要看實際情況而定。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM