FastMM参考资料.docx
- 文档编号:7289466
- 上传时间:2023-01-22
- 格式:DOCX
- 页数:14
- 大小:82.53KB
FastMM参考资料.docx
《FastMM参考资料.docx》由会员分享,可在线阅读,更多相关《FastMM参考资料.docx(14页珍藏版)》请在冰豆网上搜索。
FastMM参考资料
FastMM、FastCode、FastMove的使用
FastMM是一个替换EmbarcaderoDelphiWin32应用程序的快速内存管理器,以及可以在多线程下使用,不容易产生内存碎片,并且无需使用外部DLL文件就可以支持共享内存。
使用方法:
1.对IDE加速
解压之后,文件夹".../FastMM/ReplacementBorlndMMDLL/Delphi/Precompiled/forDelphiIDE/Performance"下的"BorlndMM.dll"拷贝到Delphi安装目录下的".../Borland/Delphi7/Bin"进行覆盖安装(最好先备份下)。
2.对应用程序加速
打开DelphiIDE,将文件夹".../FastMM"添加到"EnvironmentOptions"下的"Library"中。
然后再在具体项目工程中,在菜单栏→"Project"→"ViewSource"下,将"FastMM4.pas"单元添加到"uses"下的第一个位置。
若需要内存报告消息为中文的话,将文件".../FastMM/Translations/Chinese(Simplified)/FastMM4Messages.pas"替换文件".../FastMM/FastMM4Messages.pas"即可。
下面测试内存泄露报告:
1)新建一个Delphi应用程序,在工程文件将"FastMM4.pas"单元添加到"uses"下的第一个位置;
2)添加一个按钮,按钮单击事件如下:
1
2
3
4
5
6
procedure TForm1.btn1Click(Sender:
TObject);
var
sl:
TStrings;
begin
sl :
= TStringList.Create;
end;
3)运行程序,单击按钮,退出程序,观察结果如下图所示:
从上面可以看到有报告内存泄露,并且提示TStringList.泄露,提醒要得到详细的内存泄露信息,需开启"FullDebugMode"和"LogMemoryLeakDetailToFile"条件编译开关。
打开文件".../FastMM/FastMM4Options.inc",在文件末尾添加以下代码:
{快速配置发布版本和调试版本}
{$ifdef Release}
{发布版本请设置}
{$undef FullDebugMode}
{$undef CheckHeapForCorruption}
{$define ASMVersion}
{$undef EnableMemoryLeakReporting}
{$undef UseOutputDebugString}
{$else}
{调试版本请设置}
{$define FullDebugMode}
{$define EnableMemoryLeakReporting}
{$define UseOutputDebugString}
{$endif}
再将文件".../FastMM/FullDebugModeDLL/Precompiled/FastMM_FullDebugMode.dll"拷贝到工程可执行程序目录下,运行程序,单击按钮,观察结果如下图所示:
在工程目录下有日志文件"Project1_MemoryManager_EventLog.txt"记录内存泄露详细信息,如下图所示:
若是发布版本的话,关闭调试模式,在菜单栏→"Project"→"Options"→"Directories/Conditionals"→"Conditionals"下,定义一个条件编译"Release",如下图所示:
再次运行程序,单击按钮,观察结果,已经无内存泄露报告提示框了。
注意以上仅在IDE中调试程序有检查内存泄露,若是要在脱离IDE运行程序也检测内存泄露,请关闭选项 {$defineRequireDebuggerPresenceForLeakReporting},此项默认开启。
FastCode为Delphi社区提供高度优化的函数,此函数比Delphi运行时库函数、VCL函数以及它们的扩展函数更快。
FastMove替换所有的system.move调用,因为它有更快的速度。
使用方法:
解压之后,将FastMove放到FastCode文件夹下,这样就只需引用一个环境路径,将".../FastCode"添加到"EnvironmentOptions"下的"Library"中。
然后再在具体项目工程中,在菜单栏→"Project"→"ViewSource"下,将"FastCode.pas"和"FastMove.pas"单元添加到"uses"下的第一个位置,如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
program Project1;
uses
FastMM4, {假如有FastMM的话,放在第一个位置}
FastCode,
FastMove,
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
若是FastMM和FastMove同时使用的话,需要禁用其中一个条件编译,打开文件".../FastMM/FastMM4Options.inc",按Ctrl+F寻找字符串"$defineUseCustomVariableSizeMoveRoutines",找到之后将此行改为如下:
{.$define UseCustomVariableSizeMoveRoutines}
使用FastMove代码可以使整个程序都使用到更快的内存移动函数而不仅仅是内存管理器。
因此建议将FastMM和FastMove代码相结合,并关闭此选项。
FastMM、FastCode、FastMove打包下载:
扩展资料:
1.Delphi中使用FastMM4结合ViewCPU避免内存泄漏
2.FastMM使用详解
FastMM使用详解
一、引言
FastMM 是适用于delphi的第三方内存管理器,在国外已经是大名鼎鼎,在国内也有许多人在使用或者希望使用,就连 Borland 也在delphi2007抛弃了自己原有的饱受指责的内存管理器,改用FastMM.
但是,内存管理的复杂性以及缺乏 FastMM 中文文档导致国内许多人在使用时遇到了许多问题,一些人因此而放弃了使用,我在最近的一个项目中使用了FastMM,也因此遇到了许多问题,经过摸索和研究,终于解决了这些问题。
二、为什么要用FastMM
第一个原因是FastMM的性能接近与delphi缺省内存管理器的两倍,可以做一个简单的测试,运行下面的代码:
var
I:
Integer;
Tic:
Cardinal;
S:
string;
begin
tic:
=GetTickCount;
try
forI:
=0to100000do
begin
SetLength(S,I+100);
edt1.Text:
=S;
end;
finally
SetLength(S,0);
tic:
=GetTickCount-Tic;
MessageDlg('Tic='+IntToStr(Tic),mtInformation,[mbOK],0);
end;
end;
在我的IBMT23笔记本上,使用FastMM4(FastMM的最新版本)用时约为3300ms,而使用缺省的内存管理器,用时约为6200ms,FastMM4的性能提高达88%.
第二个原因FastMM的共享内存管理器功能使用简单可靠。
当一个应用程序有多个模块(exe和dll)组成时,模块之间的动态内存变量如string的传递就是一个很大的问题,缺省情况下,各个模块都由自己的内存管理器,由一个内存管理器分配的内存也必须在这个内存管理器才能安全释放,否则就会出现内存错误,这样如果在一个模块分配的内存在另外一个模块释放就会出现内存错误。
解决这个问题就需要使用到共享内存管理器,让各个模块都使用同一个内存管理器。
Delphi缺省的共享内存管理器是BORLNDMM.DLL,这个内存管理器并不可靠,也常常出现问题,并且,在程序发布的时候必须连同这个DLL一起发布。
而FastMM的共享内存管理器功能不需要DLL支持,并且更加可靠。
第三个原因是FastMM还拥有一些帮助程序开发的辅助功能,如内存泄漏检测功能,可以检测程序是否存在未正确释放的内存等。
三、出现什么问题
如果我们开发的应用程序,只有一个exe模块,那么,使用FastMM是一件非常简单的事情,只需要把FastMM.pas(最新版是FastMM4.pas)作为工程文件的第一个uses单元即可,如:
programTest;
uses
FastMM4,
…
但是,通常情况下,我们的应用程序都是由一个exe模块加上多个dll组成的,这样,当我们跨模块传递动态内存变量如string变量时,就会出问题,比如,下面的测试程序由一个exe和一个dll组成:
librarytest; //test.dll
uses
FastMM4,…;
procedureGetStr(varS:
string;constLen:
Integer);stdcall;
begin
SetLength(S,Len);// 分配内存
FillChar(S[1],Len,‘A’);
end;
exports
GetStr;
-------------------------------------
programTestPrj;
uses
FastMM4,…;
//------------------
unitmMain;// 测试界面
…
procedureTForm1.btnDoClick(Sender:
TObject);
var
I:
Integer;
S:
string;
Begin
try
forI:
=1to10000do
begin
GetStr(S,I+1);
edt1.Text:
=S;
Application.ProcessMessages;
end;
finally
SetLength(S,0);
end;
end;
当第二次执行btnDoClick过程时,就会出现内存错误,为什么这样?
delphi的字符串是带引用计数的,跟接口变量一样,一旦这个引用计数为0,则会自动释放内存。
在btnDoClick过程中,调用GetStr过程,用SetLength给S分配了一段内存,此时这个字符串的引用计数为1,然后执行edt1.Text:
=S语句,字符串的引用计数为2,循环再调用GetStr给S重新分配内存,这样原来的字符串的引用计数减1,再执行edt1.Text:
=S,原来的字符串引用计数为0,这时,就会被释放(注意,是在TestPrj.exe释放,而不是在Test.dll释放),但这时没有出错,当循环执行完毕之后,还有一个字符串的引用计数为2,但是执行SetLength(S,0)之后,该字符串被edt1.Text引用,的引用计数为1,第二次执行btnDoClick时,执行edt1.Text:
=S时,上次的引用计数为1的字符串引用计数减一变为0,就会被释放,此时,会出现内存错误。
由此,可以看到,在另一个模块释放别的模块分配的内存,并不一定马上出现内存错误,但是,如果频繁执行,则会出现内存错误,这种不确定的错误带有很大的隐蔽性,常常在调试时不出现,但实际应用时出现,不仔细分析很难找到原因。
要解决这个问题,就要从根源找起,这个根源就是内存管理。
Delphi的内存管理,Delphi应用程序可以使用的有三种内存区:
全局内存区、堆、栈,全局内存区存储全局变量、栈用来传递参数以及返回值,以及函数内的临时变量,这两种都是由编译器自动管理,而如字符串、对象、动态数组等都是从堆中分配的,内存管理就是指对堆内存管理,即从堆中分配内存和释放从堆中分配的内存(以下称内存的分配和释放)。
我们知道,一个进程只有一个栈,因此,也很容易误以为一个进程也只有一个堆,但实际上,一个进程除了拥有一个系统分配的默认堆(默认大小1MB),还可以创建多个用户堆,每个堆都有自己的句柄,delphi的内存管理所管理的正是自行创建的堆,delphi还把一个堆以链表的形式分成多个大小不等的块,实际的内存操作都是在这些块上。
delphi把内存管理定义为内存的分配(Get)、释放(Free)和重新分配(Realloc)。
内存管理器也就是这三种实现的一个组合,delphi在system单元中定义了这个内存管理器TMemoryManager:
PMemoryManager=^TMemoryManager;
TMemoryManager=record
GetMem:
function(Size:
Integer):
Pointer;
FreeMem:
function(P:
Pointer):
Integer;
ReallocMem:
function(P:
Pointer;Size:
Integer):
Pointer;
end;
由此知道,delphi的内存管理器就是一个 TMemoryManager 记录对象,该记录有三个域,分别指向内存的分配、释放和重新分配例程。
System单元还定义了一个变量 MemoryManager :
MemoryManager:
TMemoryManager=(
GetMem:
SysGetMem;
FreeMem:
SysFreeMem;
ReallocMem:
SysReallocMem);
该变量是delphi程序的内存管理器,缺省情况下,这个内存管理器的三个域分别指向GETMEM.INC中实现的SysGetMem、SysFreeMem、SysReallocMem。
这个内存管理器变量只在system.pas中可见,但是system单元提供了三个可以访问该变量的例程:
// 读取内存管理器,也即读取MemoryManager
procedureGetMemoryManager(varMemMgr:
TMemoryManager);
// 安装内存管理器(即用新的内存管理器替换缺省的内存管理器)
procedureSetMemoryManager(constMemMgr:
TMemoryManager);
// 是否已经安装了内存管理器(即缺省的内存管理器是否已经被替换)
functionIsMemoryManagerSet:
Boolean;
四、共享内存管理器
什么是共享内存管理器?
所谓共享内存管理器,就是一个应用程序的所有的模块,不管是exe还是dll,都使用同一个内存管理器来管理内存,这样,内存的分配和释放都是同一个内存管理器完成的,就不会出现内存错误的问题。
那么如何共享内存管理器呢?
由上分析,我们可以知道,既然要使用同一个内存管理器,那么干脆就创建一个独立的内存管理器模块(dll),其他的所有模块都使用这个模块的内存管理器来分配和释放内存。
Delphi7默认就是采取这种方法,当我们使用向导创建一个dll工程时,工程文件会有这样一段话:
{ImportantnoteaboutDLLmemorymanagement:
ShareMemmustbethe
firstunitinyourlibrary'sUSESclauseANDyourproject's(select
Project-ViewSource)USESclauseifyourDLLexportsanyproceduresor
functionsthatpassstringsasparametersorfunctionresults.This
appliestoallstringspassedtoandfromyourDLL--eventhosethat
arenestedinrecordsandclasses.ShareMemistheinterfaceunitto
theBORLNDMM.DLLsharedmemorymanager,whichmustbedeployedalong
withyourDLL.ToavoidusingBORLNDMM.DLL,passstringinformation
usingPCharorShortStringparameters.}
这段话提示我们,ShareMem 是 BORLNDMM.DLL 共享内存管理器的接口单元,我们来看看这个ShareMem,这个单元文件很简短,其中有这样的声明:
const
DelphiMM='borlndmm.dll';
functionSysGetMem(Size:
Integer):
Pointer;
externalDelphiMMname'@Borlndmm@SysGetMem$qqri';
functionSysFreeMem(P:
Pointer):
Integer;
externalDelphiMMname'@Borlndmm@SysFreeMem$qqrpv';
functionSysReallocMem(P:
Pointer;Size:
Integer):
Pointer;
externalDelphiMMname'@Borlndmm@SysReallocMem$qqrpvi';
这些声明保证了BORLNDMM.DLL将被静态加载。
在ShareMem的Initialization是这样的代码:
ifnotIsMemoryManagerSetthen
InitMemoryManager;
首先判断内存管理器是否已经被安装(也即是否默认的内存管理器被替换掉),如果没有,则初始化内存管理器,InitMemoryManager也非常简单(把无用的代码去掉了):
procedureInitMemoryManager;
var
SharedMemoryManager:
TMemoryManager;
MM:
Integer;
begin
//forceastaticreferencetoborlndmm.dll,sowedon'thavetoLoadLibrary
SharedMemoryManager.GetMem:
=SysGetMem;
MM:
=GetModuleHandle(DelphiMM);
SharedMemoryManager.GetMem:
=GetProcAddress(MM,'@Borlndmm@SysGetMem$qqri');
SharedMemoryManager.FreeMem:
=GetProcAddress(MM,'@Borlndmm@SysFreeMem$qqrpv');
SharedMemoryManager.ReallocMem:
=GetProcAddress(MM,'@Borlndmm@SysReallocMem$qqrpvi');
SetMemoryManager(SharedMemoryManager);
end;
这个函数定义了一个内存管理器对象,并设置域指向Borlndmm.dll的三个函数实现,然后调用SetMemoryManager来替换默认的内存管理器。
这样,不管那个模块,因为都要将ShareMem作为工程的第一个uses单元,因此,每个模块的ShareMem的Initialization都是最先被执行的,也就是说,每个模块的内存管理器对象虽然不相同,但是,内存管理器的三个函数指针都是指向Borlndmm.dll的函数实现,因此,所有模块的内存分配和释放都是在Borlndmm.dll内部完成的,这样就不会出现跨模块释放内存导致错误的问题。
那么,FastMM又是如何实现共享内存管理器呢?
FastMM采取了一个原理上很简单的办法,就是创建一个内存管理器,然后将这个内存管理器的地址放到一个进程内所有模块都能读取到的位置,这样,其他模块在创建内存管理器之前,先查查是否有别的模块已经把内存管理器放到这个位置,如果是则使用这个内存管理器,否则才创建一个新的内存管理器,并将地址放到这个位置,这样,这个进程的所有模块都使用一个内存管理器,实现了内存管理器的共享。
而且,这个内存管理器并不确定是哪个模块创建的,所有的模块,只要将FastMM作为其工程文件的第一个uses单元,就有可能是这个内存管理器的创建者,关键是看其在应用程序的加载顺序,第一个被加载的模块将成为内存管理器的创建者。
那么,FastMM具体是如何实现的呢?
打开 FastMM4.pas(FastMM的最新版本),还是看看其Initialization部分:
{Initializeallthelookuptables,etc.forthememorymanager}
InitializeMemoryManager;
{HasanotherMMbeenset,orhastheBorlandMMbeenused?
Ifso,thisfile
isnotthefirstunitintheusesclauseoftheproject's.dprfile.}
ifCheckCanInstallMemoryManagerthen
begin
InstallMemoryManager;
end;
InitializeMemoryManager 是初始化一些变量,完成之后就调用CheckCanInstallMemoryManager检测FastMM是否是作为工程的第一个uses单元,如果返回True,则调用InstallMemoryManager
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- FastMM 参考资料