📄 fastmm4.pas
字号:
end;
{Memory map}
TChunkStatus = (csUnallocated, csAllocated, csReserved,
csSysAllocated, csSysReserved);
TMemoryMap = array[0..65535] of TChunkStatus;
{$ifdef EnableMemoryLeakReporting}
{List of registered leaks}
TRegisteredMemoryLeak = packed record
LeakAddress: Pointer;
LeakedClass: TClass;
LeakSize: Integer;
LeakCount: Integer;
end;
TRegisteredMemoryLeaks = array of TRegisteredMemoryLeak;
{$endif}
{--------------------------Public variables----------------------------}
{$ifdef ManualLeakReportingControl}
{Variable is declared in system.pas in newer Delphi versions.}
{$ifndef BDS2006AndUp}
var
ReportMemoryLeaksOnShutdown: Boolean;
{$endif}
{$endif}
{-------------------------Public procedures----------------------------}
{Installation procedures must be exposed for the BCB helper unit FastMM4BCB.cpp}
{$ifdef BCB}
procedure InitializeMemoryManager;
function CheckCanInstallMemoryManager: boolean;
procedure InstallMemoryManager;
{$endif}
{$ifndef FullDebugMode}
{The standard memory manager functions}
function FastGetMem(ASize: Integer): Pointer;
function FastFreeMem(APointer: Pointer): Integer;
function FastReallocMem(APointer: Pointer; ANewSize: Integer): Pointer;
function FastAllocMem(ASize: Cardinal): Pointer;
{$else}
{The FullDebugMode memory manager functions}
function DebugGetMem(ASize: Integer): Pointer;
function DebugFreeMem(APointer: Pointer): Integer;
function DebugReallocMem(APointer: Pointer; ANewSize: Integer): Pointer;
function DebugAllocMem(ASize: Cardinal): Pointer;
{Specify the full path and name for the filename to be used for logging memory
errors, etc. If ALogFileName is nil or points to an empty string it will
revert to the default log file name.}
procedure SetMMLogFileName(ALogFileName: PChar = nil);
{Returns the current "allocation group". Whenever a GetMem request is serviced
in FullDebugMode, the current "allocation group" is stored in the block header.
This may help with debugging. Note that if a block is subsequently reallocated
that it keeps its original "allocation group" and "allocation number" (all
allocations are also numbered sequentially).}
function GetCurrentAllocationGroup: Cardinal;
{Allocation groups work in a stack like fashion. Group numbers are pushed onto
and popped off the stack. Note that the stack size is limited, so every push
should have a matching pop.}
procedure PushAllocationGroup(ANewCurrentAllocationGroup: Cardinal);
procedure PopAllocationGroup;
{Logs detail about currently allocated memory blocks for the specified range of
allocation groups. if ALastAllocationGroupToLog is less than
AFirstAllocationGroupToLog or it is zero, then all allocation groups are
logged. This routine also checks the memory pool for consistency at the same
time.}
procedure LogAllocatedBlocksToFile(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
{$endif}
{Releases all allocated memory (use with extreme care)}
procedure FreeAllMemory;
{Returns summarised information about the state of the memory manager. (For
backward compatibility.)}
function FastGetHeapStatus: THeapStatus;
{Returns statistics about the current state of the memory manager}
procedure GetMemoryManagerState(var AMemoryManagerState: TMemoryManagerState);
{$ifndef LINUX}
{Gets the state of every 64K block in the 4GB address space}
procedure GetMemoryMap(var AMemoryMap: TMemoryMap);
{$endif}
{$ifdef EnableMemoryLeakReporting}
{Registers expected memory leaks. Returns true on success. The list of leaked
blocks is limited, so failure is possible if the list is full.}
function RegisterExpectedMemoryLeak(ALeakedPointer: Pointer): boolean; overload;
function RegisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): boolean; overload;
function RegisterExpectedMemoryLeak(ALeakedBlockSize: Integer; ACount: Integer = 1): boolean; overload;
{Removes expected memory leaks. Returns true on success.}
function UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): boolean; overload;
function UnregisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): boolean; overload;
function UnregisterExpectedMemoryLeak(ALeakedBlockSize: Integer; ACount: Integer = 1): boolean; overload;
{Returns a list of all expected memory leaks}
function GetRegisteredMemoryLeaks: TRegisteredMemoryLeaks;
{$endif}
implementation
uses
{$ifndef Linux}
Windows,
{$else}
Libc,
{$endif}
FastMM4Messages;
{Fixed size move procedures}
procedure Move12(const ASource; var ADest; ACount: Integer); forward;
procedure Move20(const ASource; var ADest; ACount: Integer); forward;
procedure Move28(const ASource; var ADest; ACount: Integer); forward;
procedure Move36(const ASource; var ADest; ACount: Integer); forward;
procedure Move44(const ASource; var ADest; ACount: Integer); forward;
procedure Move52(const ASource; var ADest; ACount: Integer); forward;
procedure Move60(const ASource; var ADest; ACount: Integer); forward;
procedure Move68(const ASource; var ADest; ACount: Integer); forward;
{$ifdef DetectMMOperationsAfterUninstall}
{Invalid handlers to catch MM operations after uninstall}
function InvalidFreeMem(APointer: Pointer): Integer; forward;
function InvalidGetMem(ASize: Integer): Pointer; forward;
function InvalidReallocMem(APointer: Pointer; ANewSize: Integer): Pointer; forward;
function InvalidAllocMem(ASize: Cardinal): Pointer; forward;
function InvalidRegisterAndUnRegisterMemoryLeak(APointer: Pointer): Boolean; forward;
{$endif}
{-------------------------Private constants----------------------------}
const
{The size of a medium block pool. This is allocated through VirtualAlloc and
is used to serve medium blocks. The size must be a multiple of 16 and at
least 4 bytes less than a multiple of 4K (the page size) to prevent a
possible read access violation when reading past the end of a memory block
in the optimized move routine (MoveX16L4). In Full Debug mode we leave a
trailing 256 bytes to be able to safely do a memory dump.}
MediumBlockPoolSize = 20 * 64 * 1024{$ifndef FullDebugMode} - 16{$else} - 256{$endif};
{The granularity of small blocks}
{$ifdef Align16Bytes}
SmallBlockGranularity = 16;
{$else}
SmallBlockGranularity = 8;
{$endif}
{The granularity of medium blocks. Newly allocated medium blocks are
a multiple of this size plus MediumBlockSizeOffset, to avoid cache line
conflicts}
MediumBlockGranularity = 256;
MediumBlockSizeOffset = 48;
{The granularity of large blocks}
LargeBlockGranularity = 65536;
{The maximum size of a small block. Blocks Larger than this are either
medium or large blocks.}
MaximumSmallBlockSize = 2608;
{The smallest medium block size. (Medium blocks are rounded up to the nearest
multiple of MediumBlockGranularity plus MediumBlockSizeOffset)}
MinimumMediumBlockSize = 11 * 256 + MediumBlockSizeOffset;
{The number of bins reserved for medium blocks}
MediumBlockBinsPerGroup = 32;
MediumBlockBinGroupCount = 32;
MediumBlockBinCount = MediumBlockBinGroupCount * MediumBlockBinsPerGroup;
{The maximum size allocatable through medium blocks. Blocks larger than this
fall through to VirtualAlloc ( = large blocks).}
MaximumMediumBlockSize = MinimumMediumBlockSize + (MediumBlockBinCount - 1) * MediumBlockGranularity;
{The target number of small blocks per pool. The actual number of blocks per
pool may be much greater for very small sizes and less for larger sizes. The
cost of allocating the small block pool is amortized across all the small
blocks in the pool, however the blocks may not all end up being used so they
may be lying idle.}
TargetSmallBlocksPerPool = 48;
{The minimum number of small blocks per pool. Any available medium block must
have space for roughly this many small blocks (or more) to be useable as a
small block pool.}
MinimumSmallBlocksPerPool = 12;
{The lower and upper limits for the optimal small block pool size}
OptimalSmallBlockPoolSizeLowerLimit = 29 * 1024 - MediumBlockGranularity + MediumBlockSizeOffset;
OptimalSmallBlockPoolSizeUpperLimit = 64 * 1024 - MediumBlockGranularity + MediumBlockSizeOffset;
{The maximum small block pool size. If a free block is this size or larger
then it will be split.}
MaximumSmallBlockPoolSize = OptimalSmallBlockPoolSizeUpperLimit + MinimumMediumBlockSize;
{-------------Block type flags--------------}
{The lower 3 bits in the dword header of small blocks (4 bits in medium and
large blocks) are used as flags to indicate the state of the block}
{Set if the block is not in use}
IsFreeBlockFlag = 1;
{Set if this is a medium block}
IsMediumBlockFlag = 2;
{Set if it is a medium block being used as a small block pool. Only valid if
IsMediumBlockFlag is set.}
IsSmallBlockPoolInUseFlag = 4;
{Set if it is a large block. Only valid if IsMediumBlockFlag is not set.}
IsLargeBlockFlag = 4;
{Is the medium block preceding this block available? (Only used by medium
blocks)}
PreviousMediumBlockIsFreeFlag = 8;
{Is this large block segmented? I.e. is it actually built up from more than
one chunk allocated through VirtualAlloc? (Only used by large blocks.)}
LargeBlockIsSegmented = 8;
{The flags masks for small blocks}
DropSmallFlagsMask = -8;
ExtractSmallFlagsMask = 7;
{The flags masks for medium and large blocks}
DropMediumAndLargeFlagsMask = -16;
ExtractMediumAndLargeFlagsMask = 15;
{-------------Block resizing constants---------------}
SmallBlockDownsizeCheckAdder = 64;
SmallBlockUpsizeAdder = 32;
{When a medium block is reallocated to a size smaller than this, then it must
be reallocated to a small block and the data moved. If not, then it is
shrunk in place down to MinimumMediumBlockSize. Currently the limit is set
at a quarter of the minimum medium block size.}
MediumInPlaceDownsizeLimit = MinimumMediumBlockSize div 4;
{-------------Memory leak reporting constants---------------}
ExpectedMemoryLeaksListSize = 64 * 1024;
{-------------FullDebugMode constants---------------}
{$ifdef FullDebugMode}
{The stack trace depth}
StackTraceDepth = 9;
{The number of entries in the allocation group stack}
AllocationGroupStackSize = 1000;
{The number of fake VMT entries - used to track virtual method calls on
freed objects. Do not change this value without also updating TFreedObject.GetVirtualMethodIndex}
MaxFakeVMTEntries = 200;
{The pattern used to fill unused memory}
DebugFillByte = $80;
DebugFillDWord = $01010101 * Cardinal(DebugFillByte);
{The address that is reserved so that accesses to the address of the fill
pattern will result in an A/V}
DebugReservedAddress = $01010000 * Cardinal(DebugFillByte);
{$endif}
{-------------Other constants---------------}
{$ifndef NeverSleepOnThreadContention}
{Sleep time when a resource (small/medium/large block manager) is in use}
InitialSleepTime = 0;
{Used when the resource is still in use after the first sleep}
AdditionalSleepTime = 10;
{$endif}
{Hexadecimal characters}
HexTable: array[0..15] of char = '0123456789ABCDEF';
{Copyright message - not used anywhere in the code}
Copyright: string = 'FastMM4
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -