大家看看这样释放内存是否干净。
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过程正常释放内存吗?
做法1可以正常释放内存
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进去 感觉上面两个事件的执行代码没什么差别,楼主说 第一种有问题, 我也只是感觉有问题,但现在看来,代码也没问题.
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看看。
编译器不会知道你的Data的原来是什么类型的指针的,必须显式告诉编译器,
所以方法1不对,方法2才对
理论上我觉得应该没什么区别...
可能性是,在只指定一个pointer去释放内存时,delphi可能只是把pointer释放,而实际的内存块在变量表中还有一个指明的type - TRECA被引用(因为你在create的时候没有handle它,delphi可能会有在变量表中handle它), 而指定type后,在释放时delphi会查找引用此内存块的变量表中的handle,然后释放该内存...
没有找到深层的内存管理资料..sorry.
高手就是高手
我怕下一步连01也搬过来了
帮你顶下,大哥给我5分,我要求不高
Dispose()需要通过参数指针的类型判断释放空间的大小
Dispose(Data)相当于释放4个字节(Pointer类型)
Dispose(PRECA(Data))则释放16个字节(TRect类型)
很明显如果用第一种方法将有12个字节的内存泄漏
楼上讲解好清楚啊! 又学习到一点.
zswang的说法也不对,dispose需要类型信息主要是为了清除结构的生存期自管理对象,就是例中的
s: string;
至于释放多少字节,是由指针本身分配时的大小决定的,dispose不理会这一点,它直接调用freemem,让freemem去查找指针原来分配的大小,大龙驹列出的原码已经说明了这一点
如果PRECA被转换成无类型指针,则pRecA的s所分配的内存将遗漏(如果S被附值的话),但是pRecA结构所分配的内存则按其原来分配的大小被释放
所以方法1将遗失所有记录的s的空间开销,但是记录的空间还是被正确释放了
记得前一段时间讨论过类似的问题
dispose只是释放这个指针所占用的内存,和指针类型下的结构。
如果不指定Data指针的类型,编译器不知道指针的类型,所以就没有办法完全释放的。
个人觉得伴水说的比较对...
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个字节。