[英]Is this FastMM4 Invalid Pointer Exception a bug in FastMM for Delphi 5?
在Delphi 5中,當FastMM處於活動狀態時,在以下最小可重現代碼中調用FreeMem
會觸發無效指針異常 :
program Project1;
{$APPTYPE CONSOLE}
uses
FastMM4,
SysUtils,
Windows;
procedure Main;
var
token: THandle;
returnLength: Cardinal;
p: Pointer;
begin
OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, {out}token);
//Get the size of the buffer required.
//It's normally going to be 38 bytes. We'll use 16KB to eliminate the possibility of buffer overrun
// Windows.GetTokenInformation(token, TokenUser, nil, 0, {var}returnLength);
p := GetMemory(16384); //GetMemory(returnLength);
Windows.GetTokenInformation(token, TokenUser, p, 1024, {var}returnLength);
FreeMem({var}p); //FreeMem is the documented way to free memory allocated with GetMemory.
// FreeMemory(p); //FreeMemory is the C++ compatible version of FreeMem.
end;
begin
Main;
end.
對FreeMme
的調用因EInvalidPointerException
FreeMme
失敗:
FreeMem({var}p); //error
如果出現以下錯誤,則錯誤將停止:
GetTokenInformation
FreeMemory
(而不是FreeMem
) 我在新安裝的Windows 7機器上重新安裝了Delphi 5的錯誤。 FastMM4 v4.992。
這只是:
如果它是FastMM4中的錯誤,我可以解決它。 而不是打電話:
我可以用另一種方式手動分配緩沖區:
如果它不是FastMM4中的錯誤,我想修復上面的代碼。
我的印象是FastMM接管內存管理,這就是為什么我驚訝地發現:
FreeMem({var}p);
失敗 FreeMemory(p);
作品 在內部, FreeMem實現為對內存管理器的調用。 在這種情況下,內存管理器(FastMM)返回非零值,導致調用reInvalidPtr
:
System.pas
procedure _FreeMem;
asm
TEST EAX,EAX
JE @@1
CALL MemoryManager.FreeMem
OR EAX,EAX
JNE @@2
@@1: RET
@@2: MOV AL,reInvalidPtr
JMP Error
end;
並且MemoryManager.FreeMem的實現最終是:
FastMM4.pas
function FastFreeMem(APointer: Pointer);
FreeMemory的實現是:
System.pas :
function FreeMemory(P: Pointer): Integer; cdecl;
begin
if P = nil then
Result := 0
else
Result := SysFreeMem(P);
end;
SysFreeMem實現在:
GetMem.inc :
function SysFreeMem(p: Pointer): Integer;
// Deallocate memory block.
label
abort;
var
u, n : PUsed;
f : PFree;
prevSize, nextSize, size : Integer;
begin
heapErrorCode := cHeapOk;
if not initialized and not InitAllocator then begin
heapErrorCode := cCantInit;
result := cCantInit;
exit;
end;
try
if IsMultiThread then EnterCriticalSection(heapLock);
u := p;
u := PUsed(PChar(u) - sizeof(TUsed)); { inv: u = address of allocated block being freed }
size := u.sizeFlags;
{ inv: size = SET(block size) + [block flags] }
{ validate that the interpretation of this block as a used block is correct }
if (size and cThisUsedFlag) = 0 then begin
heapErrorCode := cBadUsedBlock;
goto abort;
end;
{ inv: the memory block addressed by 'u' and 'p' is an allocated block }
Dec(AllocMemCount);
Dec(AllocMemSize,size and not cFlags - sizeof(TUsed));
if (size and cPrevFreeFlag) <> 0 then begin
{ previous block is free, coalesce }
prevSize := PFree(PChar(u)-sizeof(TFree)).size;
if (prevSize < sizeof(TFree)) or ((prevSize and cFlags) <> 0) then begin
heapErrorCode := cBadPrevBlock;
goto abort;
end;
f := PFree(PChar(u) - prevSize);
if f^.size <> prevSize then begin
heapErrorCode := cBadPrevBlock;
goto abort;
end;
inc(size, prevSize);
u := PUsed(f);
DeleteFree(f);
end;
size := size and not cFlags;
{ inv: size = block size }
n := PUsed(PChar(u) + size);
{ inv: n = block following the block to free }
if PChar(n) = curAlloc then begin
{ inv: u = last block allocated }
dec(curAlloc, size);
inc(remBytes, size);
if remBytes > cDecommitMin then
FreeCurAlloc;
result := cHeapOk;
exit;
end;
if (n.sizeFlags and cThisUsedFlag) <> 0 then begin
{ inv: n is a used block }
if (n.sizeFlags and not cFlags) < sizeof(TUsed) then begin
heapErrorCode := cBadNextBlock;
goto abort;
end;
n.sizeFlags := n.sizeFlags or cPrevFreeFlag
end else begin
{ inv: block u & n are both free; coalesce }
f := PFree(n);
if (f.next = nil) or (f.prev = nil) or (f.size < sizeof(TFree)) then begin
heapErrorCode := cBadNextBlock;
goto abort;
end;
nextSize := f.size;
inc(size, nextSize);
DeleteFree(f);
{ inv: last block (which was free) is not on free list }
end;
InsertFree(u, size);
abort:
result := heapErrorCode;
finally
if IsMultiThread then LeaveCriticalSection(heapLock);
end;
end;
因此,感覺FreeMemory不會觸發錯誤; FreeMemory不會由內存管理器接管。
我想這就是為什么FreeMemory不是記錄對口GetMemory: 🕗
FreeMem
不是用GetMemory
分配的釋放內存的文檔化方法 - 這顯然是舊文檔中的錯誤,此后已被糾正。 從System.GetMemory的文檔 ( 重點添加):
GetMemory
分配一個內存塊。
GetMemory
分配給定Size的塊,並返回該內存的地址。 分配的緩沖區的字節不設置為零。 要處理緩沖區,請使用FreeMemory
。 如果沒有足夠的可用內存來分配塊,則會EOutOfMemory
異常。
如果分配與內存GetMem
,使用FreeMem
。 如果分配與完成GetMemory
,使用FreeMemory
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.