📄 fastmm4.pas
字号:
- Changed large block resizing behavior to be a bit more conservative. Large
blocks will be downsized if the new size is less than half of the old size
(the threshold was a quarter previously).
Version 4.23 (6 August 2005):
- Fixed BCB6 support (Thanks to Omar Zelaya).
- Renamed "OutputInstallUninstallDebugString" to "UseOutputDebugString", and
added debug string output on memory leak or error detection.
Version 4.24 (11 August 2005):
- Added the "NoMessageBoxes" option to suppress the display of message boxes,
which is useful for services that should not be interrupted. (Thanks to Dan
Miser).
- Changed the stack trace code to return the line number of the caller and not
the line number of the return address. (Thanks to Dan Miser).
Version 4.25 (15 August 2005):
- Fixed CheckExpectedLeak not detecting expected leaks registered by class
when in "FullDebugMode". (Thanks to Arjen de Ruijter).
Version 4.26 (18 August 2005):
- Added a "UseRuntimePackages" option that allows FastMM to be used in a main
application together with DLLs that all use runtime packages. (Thanks to
Aleksander Oven.)
*)
unit FastMM4;
interface
{$Include FastMM4Options.inc}
{$RANGECHECKS OFF}
{$BOOLEVAL OFF}
{$OVERFLOWCHECKS OFF}
{$OPTIMIZATION ON}
{$TYPEDADDRESS OFF}
{Some features not currently supported under Kylix}
{$ifdef Linux}
{$undef LogErrorsToFile}
{$undef LogMemoryLeakDetailToFile}
{$undef ShareMM}
{$undef AttemptToUseSharedMM}
{$undef RequireIDEPresenceForLeakReporting}
{$undef UseOutputDebugString}
{$endif}
{Do we require debug info for leak checking?}
{$ifdef RequireDebugInfoForLeakReporting}
{$ifopt D-}
{$undef EnableMemoryLeakReporting}
{$endif}
{$endif}
{Enable heap checking and leak reporting in full debug mode}
{$ifdef FullDebugMode}
{$STACKFRAMES ON}
{$define CheckHeapForCorruption}
{$ifndef CatchUseOfFreedInterfaces}
{$define CheckUseOfFreedBlocksOnShutdown}
{$endif}
{$else}
{Error logging requires FullDebugMode}
{$undef LogErrorsToFile}
{$undef CatchUseOfFreedInterfaces}
{$endif}
{Only the pascal version supports extended heap corruption checking.}
{$ifdef CheckHeapForCorruption}
{$undef ASMVersion}
{$endif}
{$ifdef UseRuntimePackages}
{$define AssumeMultiThreaded}
{$endif}
{Delphi versions}
{$ifndef BCB}
{$ifdef ver130}
{$define Delphi5}
{$endif}
{$ifdef ver140}
{$define Delphi6}
{$endif}
{$endif}
{$ifndef Delphi5}
{$ifndef BCB}
{$define Delphi6AndUp}
{$endif}
{$ifndef Delphi6}
{$define BCB6OrDelphi7AndUp}
{$ifndef BCB}
{$define Delphi7AndUp}
{$endif}
{$endif}
{$endif}
{$ifdef Delphi6AndUp}
{$WARN SYMBOL_PLATFORM OFF}
{$endif}
{$ifdef BCB}
{Cannot uninstall safely under BCB}
{$define NeverUninstall}
{$endif}
{Leak detail logging requires error logging}
{$ifndef LogErrorsToFile}
{$undef LogMemoryLeakDetailToFile}
{$undef ClearLogFileOnStartup}
{$endif}
{Manual leak reporting control requires leak reporting to be enabled}
{$ifndef EnableMemoryLeakReporting}
{$undef ManualLeakReportingControl}
{$endif}
{-------------------------Public constants-----------------------------}
const
{The number of small block types}
{$ifdef Align16Bytes}
NumSmallBlockTypes = 46;
{$else}
NumSmallBlockTypes = 55;
{$endif}
{----------------------------Public types------------------------------}
type
TSmallBlockTypeState = packed record
{The internal size of the block type}
InternalBlockSize: Cardinal;
{Useable block size: The number of non-reserved bytes inside the block.}
UseableBlockSize: Cardinal;
{The number of allocated blocks}
AllocatedBlockCount: Cardinal;
{The total address space reserved for this block type (both allocated and
free blocks)}
ReservedAddressSpace: Cardinal;
end;
TSmallBlockTypeStates = array[0..NumSmallBlockTypes - 1] of TSmallBlockTypeState;
TMemoryManagerState = packed record
{Small block type states}
SmallBlockTypeStates: TSmallBlockTypeStates;
{Medium block stats}
AllocatedMediumBlockCount: Cardinal;
TotalAllocatedMediumBlockSize: Cardinal;
ReservedMediumBlockAddressSpace: Cardinal;
{Large block stats}
AllocatedLargeBlockCount: Cardinal;
TotalAllocatedLargeBlockSize: Cardinal;
ReservedLargeBlockAddressSpace: Cardinal;
end;
{Memory map}
TChunkStatus = (csUnallocated, csAllocated, csReserved,
csSysAllocated, csSysReserved);
TMemoryMap = array[0..65535] of TChunkStatus;
{--------------------------Public variables----------------------------}
{$ifdef ManualLeakReportingControl}
var
ReportMemoryLeaks: Boolean = True;
{$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 FastFreeMem(APointer: Pointer): Integer;
function FastGetMem(ASize: Integer): Pointer;
function FastReallocMem(APointer: Pointer; ANewSize: Integer): Pointer;
function FastAllocMem(ASize: Cardinal): Pointer;
{$else}
{The FullDebugMode memory manager functions}
function DebugFreeMem(APointer: Pointer): Integer;
function DebugGetMem(ASize: Integer): Pointer;
function DebugReallocMem(APointer: Pointer; ANewSize: Integer): Pointer;
function DebugAllocMem(ASize: Cardinal): Pointer;
{$endif}
{Releases all allocated memory (use with extreme care)}
procedure FreeAllMemory;
{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;
{$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;
{-------------------------Private constants----------------------------}
const
{The size of a medium block pool. This is allocated through
VirtualAlloc and is used to serve medium blocks. In Full Debug mode we leave
a trailing 256 bytes to be able to safely do a memory dump.}
MediumBlockPoolSize = 20 * 64 * 1024{$ifdef FullDebugMode} - 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?}
PreviousMediumBlockIsFreeFlag = 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;
{-------------Memory leak reporting constants---------------}
ExpectedMemoryLeaksListSize = 64 * 1024;
{-------------FullDebugMode constants---------------}
{$ifdef FullDebugMode}
{The stack trace depth}
StackTraceDepth = 9;
{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---------------}
{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;
{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 + -