如果操作一個 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.
“在DELPHI代碼中,FreeMem根據變量所引用的指針釋放內存,並將內存歸還給堆。如果指針不是指向堆中的內存地址,將發生一個運行時錯誤。如果指針所指向的是一個數據結構,且其中包含有長字符串、Variants、動態數組、或接口,則在使用用FreeMem之前須調用Finalize ” "注意:使用New 和 Dispose 過程要強於使用GetMem與FreeMem。但我們使用New和Dispose的時候,不需要顯示的調用Finalize "
在這裏,我們討論指向結構體的指針,在這種情況下,New/Dispose 通常是易用的。但是它們是有侷限的,就是那個指針指向的空間的大小必須能夠在編譯期間確定,它們才知道需要分配多大的空間。對於指向結構體的指針,這個值就是結構體的大小,這當然是確定的,所以能夠使用它們,並且能夠帶來便利。 可是還有一些情況,例如你只有一個 Pointer 類型,這是無類型指針,你把它傳給 New,編譯器就不知道它指向的是什麼內容,也就不知道它指向的空間有多大,也就不知道需要分配多少空間,就根本不能用,更不用說易用了。New/Dispose 操作更爲高層一些,我們通常用它們來操作指向結構體的指針,即是說指針指向的內容是編譯期間就已知的數據結構。而當我們需要更加低級的去操作一些內存空間的時候,比如你要自己處理字符串的時候,你的指針指向的就是隻有你自己才知道或者說是你自己去進行理解的內存空間,沒有編譯期間的明確的數據結構與之對應。這個時候,就要用到 GetMem/FreeMem 了。 所以說,New/Dispose 的侷限性實際上是很大的,或者說適用範圍是很小的,而 GetMem/FreeMem 給了我們充分的自由,試用範圍更廣。當然,具體選用哪個,還要看實際情況而定。