日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件園多重安全檢測下載網(wǎng)站、值得信賴的軟件下載站!
      軟件
      軟件
      文章
      搜索

      首頁編程開發(fā)Delphi → delphi 中的內(nèi)存管理及 Delphi7 的FastMM配置

      delphi 中的內(nèi)存管理及 Delphi7 的FastMM配置

      前往專題相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來源:西西整理時(shí)間:2012/8/28 22:16:34字體大。A-A+

      作者:西西點(diǎn)擊:172次評(píng)論:1次標(biāo)簽: delphi

      FastMM 是適用于delphi的第三方內(nèi)存管理器,在國外已經(jīng)是大名鼎鼎,在國內(nèi)也有許多人在使用或者希望使用,就連 Borland 也在delphi2007拋棄了自己原有的飽受指責(zé)的內(nèi)存管理器,改用FastMM.
      但是,內(nèi)存管理的復(fù)雜性以及缺乏 FastMM 中文文檔導(dǎo)致國內(nèi)許多人在使用時(shí)遇到了許多問題,一些人因此而放棄了使用,我在最近的一個(gè)項(xiàng)目中使用了FastMM,也因此遇到了許多問題,經(jīng)過摸索和研究,終于解決了這些問題。

      二、為什么要用FastMM
      第一個(gè)原因是FastMM的性能接近與delphi缺省內(nèi)存管理器的兩倍,可以做一個(gè)簡單的測試,運(yùn)行下面的代碼:
      var
      I: Integer;
      Tic: Cardinal;
      S: string;
      begin
      tic := GetTickCount;
      try
      for I := 0 to 100000 do
      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;
      在我的IBM T23筆記本上,使用FastMM4(FastMM的最新版本)用時(shí)約為3300ms,而使用缺省的內(nèi)存管理器,用時(shí)約為6200ms,F(xiàn)astMM4的性能提高達(dá)88%.
      第二個(gè)原因FastMM的共享內(nèi)存管理器功能使用簡單可靠。當(dāng)一個(gè)應(yīng)用程序有多個(gè)模塊(exe和dll)組成時(shí),模塊之間的動(dòng)態(tài)內(nèi)存變量如string的傳遞就是一個(gè)很大的問題,缺省情況下,各個(gè)模塊都由自己的內(nèi)存管理器,由一個(gè)內(nèi)存管理器分配的內(nèi)存也必須在這個(gè)內(nèi)存管理器才能安全釋放,否則就會(huì)出現(xiàn)內(nèi)存錯(cuò)誤,這樣如果在一個(gè)模塊分配的內(nèi)存在另外一個(gè)模塊釋放就會(huì)出現(xiàn)內(nèi)存錯(cuò)誤。解決這個(gè)問題就需要使用到共享內(nèi)存管理器,讓各個(gè)模塊都使用同一個(gè)內(nèi)存管理器。Delphi缺省的共享內(nèi)存管理器是BORLNDMM.DLL,這個(gè)內(nèi)存管理器并不可靠,也常常出現(xiàn)問題,并且,在程序發(fā)布的時(shí)候必須連同這個(gè)DLL一起發(fā)布。而FastMM的共享內(nèi)存管理器功能不需要DLL支持,并且更加可靠。
      第三個(gè)原因是FastMM還擁有一些幫助程序開發(fā)的輔助功能,如內(nèi)存泄漏檢測功能,可以檢測程序是否存在未正確釋放的內(nèi)存等。

      三、出現(xiàn)什么問題
      如果我們開發(fā)的應(yīng)用程序,只有一個(gè)exe模塊,那么,使用FastMM是一件非常簡單的事情,只需要把FastMM.pas(最新版是FastMM4.pas)作為工程文件的第一個(gè)uses單元即可,如:

      program Test;
      uses
      FastMM4,

      但是,通常情況下,我們的應(yīng)用程序都是由一個(gè)exe模塊加上多個(gè)dll組成的,這樣,當(dāng)我們跨模塊傳遞動(dòng)態(tài)內(nèi)存變量如string變量時(shí),就會(huì)出問題,比如,下面的測試程序由一個(gè)exe和一個(gè)dll組成:

      library test; // test.dll
      uses
      FastMM4, …;
      procedure GetStr(var S: string; const Len: Integer); stdcall;
      begin
      SetLength(S, Len); // 分配內(nèi)存
      FillChar(S[1], Len, ‘A’);
      end;
      exports
      GetStr;
      -------------------------------------
      program TestPrj;
      uses
      FastMM4, …;
      //------------------
      unit mMain; // 測試界面

      procedure TForm1.btnDoClick(Sender: TObject);
      var
      I: Integer;
      S: string;
      Begin
      try
      for I := 1 to 10000 do
      begin
      GetStr(S, I + 1);
      edt1.Text := S;
      Application.ProcessMessages;
      end;
      finally
      SetLength(S, 0);
      end;
      end;

      當(dāng)?shù)诙螆?zhí)行btnDoClick過程時(shí),就會(huì)出現(xiàn)內(nèi)存錯(cuò)誤,為什么這樣?delphi的字符串是帶引用計(jì)數(shù)的,跟接口變量一樣,一旦這個(gè)引用計(jì)數(shù)為0,則會(huì)自動(dòng)釋放內(nèi)存。在btnDoClick過程中,調(diào)用GetStr過程,用SetLength給S分配了一段內(nèi)存,此時(shí)這個(gè)字符串的引用計(jì)數(shù)為1,然后執(zhí)行edt1.Text := S語句,字符串的引用計(jì)數(shù)為2,循環(huán)再調(diào)用GetStr給S重新分配內(nèi)存,這樣原來的字符串的引用計(jì)數(shù)減1,再執(zhí)行edt1.Text := S,原來的字符串引用計(jì)數(shù)為0,這時(shí),就會(huì)被釋放(注意,是在TestPrj.exe釋放,而不是在Test.dll釋放),但這時(shí)沒有出錯(cuò),當(dāng)循環(huán)執(zhí)行完畢之后,還有一個(gè)字符串的引用計(jì)數(shù)為2,但是執(zhí)行SetLength(S, 0)之后,該字符串被edt1.Text引用,的引用計(jì)數(shù)為1,第二次執(zhí)行btnDoClick時(shí),執(zhí)行edt1.Text := S時(shí),上次的引用計(jì)數(shù)為1的字符串引用計(jì)數(shù)減一變?yōu)?,就會(huì)被釋放,此時(shí),會(huì)出現(xiàn)內(nèi)存錯(cuò)誤。
      由此,可以看到,在另一個(gè)模塊釋放別的模塊分配的內(nèi)存,并不一定馬上出現(xiàn)內(nèi)存錯(cuò)誤,但是,如果頻繁執(zhí)行,則會(huì)出現(xiàn)內(nèi)存錯(cuò)誤,這種不確定的錯(cuò)誤帶有很大的隱蔽性,常常在調(diào)試時(shí)不出現(xiàn),但實(shí)際應(yīng)用時(shí)出現(xiàn),不仔細(xì)分析很難找到原因。
      要解決這個(gè)問題,就要從根源找起,這個(gè)根源就是內(nèi)存管理。
      一、Delphi的內(nèi)存管理
      Delphi應(yīng)用程序可以使用的有三種內(nèi)存區(qū):全局內(nèi)存區(qū)、堆、棧,全局內(nèi)存區(qū)存儲(chǔ)全局變量、棧用來傳遞參數(shù)以及返回值,以及函數(shù)內(nèi)的臨時(shí)變量,這兩種都是由編譯器自動(dòng)管理,而如字符串、對象、動(dòng)態(tài)數(shù)組等都是從堆中分配的,內(nèi)存管理就是指對堆內(nèi)存管理,即從堆中分配內(nèi)存和釋放從堆中分配的內(nèi)存(以下稱內(nèi)存的分配和釋放)。
      我們知道,一個(gè)進(jìn)程只有一個(gè)棧,因此,也很容易誤以為一個(gè)進(jìn)程也只有一個(gè)堆,但實(shí)際上,一個(gè)進(jìn)程除了擁有一個(gè)系統(tǒng)分配的默認(rèn)堆(默認(rèn)大小1MB),還可以創(chuàng)建多個(gè)用戶堆,每個(gè)堆都有自己的句柄,delphi的內(nèi)存管理所管理的正是自行創(chuàng)建的堆,delphi還把一個(gè)堆以鏈表的形式分成多個(gè)大小不等的塊,實(shí)際的內(nèi)存操作都是在這些塊上。
      delphi把內(nèi)存管理定義為內(nèi)存的分配(Get)、釋放(Free)和重新分配(Realloc)。內(nèi)存管理器也就是這三種實(shí)現(xiàn)的一個(gè)組合,delphi在system單元中定義了這個(gè)內(nèi)存管理器TMemoryManager:

      PMemoryManager = ^TMemoryManager;
      TMemoryManager = record
      GetMem: function (Size: Integer): Pointer;
      FreeMem: function (P: Pointer): Integer;
      ReallocMem: function (P: Pointer; Size: Integer): Pointer;
      end;
      由此知道,delphi的內(nèi)存管理器就是一個(gè) TMemoryManager 記錄對象,該記錄有三個(gè)域,分別指向內(nèi)存的分配、釋放和重新分配例程。
      System單元還定義了一個(gè)變量 MemoryManager :
      MemoryManager: TMemoryManager = (
      GetMem: SysGetMem;
      FreeMem: SysFreeMem;
      ReallocMem: SysReallocMem);
      該變量是delphi程序的內(nèi)存管理器,缺省情況下,這個(gè)內(nèi)存管理器的三個(gè)域分別指向GETMEM.INC中實(shí)現(xiàn)的SysGetMem、SysFreeMem、SysReallocMem。這個(gè)內(nèi)存管理器變量只在system.pas中可見,但是system單元提供了三個(gè)可以訪問該變量的例程:

      // 讀取內(nèi)存管理器,也即讀取MemoryManager
      procedure GetMemoryManager (var MemMgr: TMemoryManager);
      // 安裝內(nèi)存管理器(即用新的內(nèi)存管理器替換缺省的內(nèi)存管理器)
      procedure SetMemoryManager (const MemMgr: TMemoryManager);
      // 是否已經(jīng)安裝了內(nèi)存管理器(即缺省的內(nèi)存管理器是否已經(jīng)被替換)
      function IsMemoryManagerSet: Boolean;

      四、共享內(nèi)存管理器
      什么是共享內(nèi)存管理器?
      所謂共享內(nèi)存管理器,就是一個(gè)應(yīng)用程序的所有的模塊,不管是exe還是dll,都使用同一個(gè)內(nèi)存管理器來管理內(nèi)存,這樣,內(nèi)存的分配和釋放都是同一個(gè)內(nèi)存管理器完成的,就不會(huì)出現(xiàn)內(nèi)存錯(cuò)誤的問題。
      那么如何共享內(nèi)存管理器呢?
      由上分析,我們可以知道,既然要使用同一個(gè)內(nèi)存管理器,那么干脆就創(chuàng)建一個(gè)獨(dú)立的內(nèi)存管理器模塊(dll),其他的所有模塊都使用這個(gè)模塊的內(nèi)存管理器來分配和釋放內(nèi)存。Delphi7默認(rèn)就是采取這種方法,當(dāng)我們使用向?qū)?chuàng)建一個(gè)dll工程時(shí),工程文件會(huì)有這樣一段話:
      {Important note about DLL memory management: ShareMem must be the
      first unit in your library's USES clause AND your project's (select
      Project-View Source) USES clause if your DLL exports any procedures or
      functions that pass strings as parameters or function results. This
      applies to all strings passed to and from your DLL--even those that
      are nested in records and classes. ShareMem is the interface unit to
      the BORLNDMM.DLL shared memory manager, which must be deployed along
      with your DLL. To avoid using BORLNDMM.DLL, pass string information
      using PChar or ShortString parameters. }
      這段話提示我們,ShareMem 是 BORLNDMM.DLL 共享內(nèi)存管理器的接口單元,我們來看看這個(gè)ShareMem,這個(gè)單元文件很簡短,其中有這樣的聲明:

      const
      DelphiMM = 'borlndmm.dll';
      function SysGetMem (Size: Integer): Pointer;
      external DelphiMM name '@Borlndmm@SysGetMem$qqri';
      function SysFreeMem(P: Pointer): Integer;
      external DelphiMM name '@Borlndmm@SysFreeMem$qqrpv';
      function SysReallocMem(P: Pointer; Size: Integer): Pointer;
      external DelphiMM name '@Borlndmm@SysReallocMem$qqrpvi';
      這些聲明保證了BORLNDMM.DLL將被靜態(tài)加載。
      在ShareMem的Initialization是這樣的代碼:
      if not IsMemoryManagerSet then
      InitMemoryManager;
      首先判斷內(nèi)存管理器是否已經(jīng)被安裝(也即是否默認(rèn)的內(nèi)存管理器被替換掉),如果沒有,則初始化內(nèi)存管理器,InitMemoryManager也非常簡單(把無用的代碼去掉了):

      procedure InitMemoryManager;
      var
      SharedMemoryManager: TMemoryManager;
      MM: Integer;
      begin
      // force a static reference to borlndmm.dll, so we don't have to LoadLibrary
      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;
      這個(gè)函數(shù)定義了一個(gè)內(nèi)存管理器對象,并設(shè)置域指向Borlndmm.dll的三個(gè)函數(shù)實(shí)現(xiàn),然后調(diào)用SetMemoryManager來替換默認(rèn)的內(nèi)存管理器。
      這樣,不管那個(gè)模塊,因?yàn)槎家獙hareMem作為工程的第一個(gè)uses單元,因此,每個(gè)模塊的ShareMem的Initialization都是最先被執(zhí)行的,也就是說,每個(gè)模塊的內(nèi)存管理器對象雖然不相同,但是,內(nèi)存管理器的三個(gè)函數(shù)指針都是指向Borlndmm.dll的函數(shù)實(shí)現(xiàn),因此,所有模塊的內(nèi)存分配和釋放都是在Borlndmm.dll內(nèi)部完成的,這樣就不會(huì)出現(xiàn)跨模塊釋放內(nèi)存導(dǎo)致錯(cuò)誤的問題。
      那么,F(xiàn)astMM又是如何實(shí)現(xiàn)共享內(nèi)存管理器呢?
      FastMM采取了一個(gè)原理上很簡單的辦法,就是創(chuàng)建一個(gè)內(nèi)存管理器,然后將這個(gè)內(nèi)存管理器的地址放到一個(gè)進(jìn)程內(nèi)所有模塊都能讀取到的位置,這樣,其他模塊在創(chuàng)建內(nèi)存管理器之前,先查查是否有別的模塊已經(jīng)把內(nèi)存管理器放到這個(gè)位置,如果是則使用這個(gè)內(nèi)存管理器,否則才創(chuàng)建一個(gè)新的內(nèi)存管理器,并將地址放到這個(gè)位置,這樣,這個(gè)進(jìn)程的所有模塊都使用一個(gè)內(nèi)存管理器,實(shí)現(xiàn)了內(nèi)存管理器的共享。
      而且,這個(gè)內(nèi)存管理器并不確定是哪個(gè)模塊創(chuàng)建的,所有的模塊,只要將FastMM作為其工程文件的第一個(gè)uses單元,就有可能是這個(gè)內(nèi)存管理器的創(chuàng)建者,關(guān)鍵是看其在應(yīng)用程序的加載順序,第一個(gè)被加載的模塊將成為內(nèi)存管理器的創(chuàng)建者。
      那么,F(xiàn)astMM具體是如何實(shí)現(xiàn)的呢?
      打開 FastMM4.pas(FastMM的最新版本),還是看看其Initialization部分:

      {Initialize all the lookup tables, etc. for the memory manager}
      InitializeMemoryManager;
      {Has another MM been set, or has the Borland MM been used? If so, this file
      is not the first unit in the uses clause of the project's .dpr file.}
      if CheckCanInstallMemoryManager then
      begin
      InstallMemoryManager;
      end;
      InitializeMemoryManager 是初始化一些變量,完成之后就調(diào)用CheckCanInstallMemoryManager檢測FastMM是否是作為工程的第一個(gè)uses單元,如果返回True,則調(diào)用InstallMemoryManager安裝FastMM自己的內(nèi)存管理器,我們按順序摘取該函數(shù)的關(guān)鍵性代碼進(jìn)行分析:
      {Build a string identifying the current process}
      LCurrentProcessID: = GetCurrentProcessId;
      for i := 0 to 7 do
      UniqueProcessIDString [8 - i]:= HexTable [((LCurrentProcessID shr (i * 4)) and $F)];
      MMWindow: = FindWindow ('STATIC', PChar (@UniqueProcessIDString [1]));
      首先,獲取該進(jìn)程的ID,并轉(zhuǎn)換為十六進(jìn)制字符串,然后查找以該字符串為窗口名稱的窗口。
      如果進(jìn)程中還沒有該窗口,則MMWindow 將返回0,那就,就創(chuàng)建該窗口:
      MMWindow: = CreateWindow ('STATIC', PChar (@UniqueProcessIDString [1]),
      WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
      創(chuàng)建這個(gè)窗口有什么用呢,繼續(xù)看下面的代碼:

      if MMWindow <> 0 then
      SetWindowLong (MMWindow, GWL_USERDATA, Integer (@NewMemoryManager));
      NewMemoryManager.Getmem: = FastGetMem;
      NewMemoryManager.FreeMem: = FastFreeMem;
      NewMemoryManager.ReallocMem: = FastReallocMem;
      查閱MSDN可以知道,每個(gè)窗口都有一個(gè)可供創(chuàng)建它的應(yīng)用程序使用的32位的值,該值可以通過以GWL_USERDATA為參數(shù)調(diào)用SetWindowLong來進(jìn)行設(shè)置,也可以通過以GWL_USERDATA為參數(shù)調(diào)用GetWindowLong來讀取。由此,我們就很清楚地知道,原來FastMM把要共享的內(nèi)存管理器的地址保存到這個(gè)值上,這樣其他模塊就可以通過GetWindowLong來獲讀取到這個(gè)值,從而得到共享的內(nèi)存管理器:

      NewMemoryManager: = PMemoryManager (GetWindowLong (MMWindow, GWL_USERDATA)) ^;
      通過上面的分析,可以看出,F(xiàn)astMM 在實(shí)現(xiàn)共享內(nèi)存管理器上要比borland巧妙得多,borland的實(shí)現(xiàn)方法使得應(yīng)用程序必須將BORLNDMM.DLL一起發(fā)布,而FastMM的實(shí)現(xiàn)方法不需要任何dll的支持。
      但是,上面的摘錄代碼把一些編譯選項(xiàng)判斷忽略掉了,實(shí)際上,要使用FastMM的共享內(nèi)存管理器功能,需要在各個(gè)模塊編譯的時(shí)候在FastMM4.pas單元上打開一些編譯選項(xiàng):
      {$define ShareMM} //是打開共享內(nèi)存管理器功能,是其他兩個(gè)編譯選項(xiàng)生效的前提
      {$define ShareMMIfLibrary} //是允許一個(gè)dll共享其內(nèi)存管理器,如果沒有定義這個(gè)選項(xiàng),則一個(gè)應(yīng)用程序中,只有exe模塊才能夠創(chuàng)建和共享其內(nèi)存管理器,由于靜態(tài)加載的dll總是比exe更早被加載,因此,如果一個(gè)dll會(huì)被靜態(tài)加載,則必須打開該選項(xiàng),否則可能會(huì)出錯(cuò)
      {$define AttemptToUseSharedMM} //是允許一個(gè)模塊使用別的模塊共享的內(nèi)存管理器
      這些編譯選項(xiàng)在FastMM4.pas所在目錄的FastMM4Options.inc文件中都有定義和說明,只不過這些定義都被注釋掉了,因此,可以取消注釋來打開這些編譯選項(xiàng),或者可以在你的工程目錄下創(chuàng)建一個(gè).inc文件(如FastShareMM.inc),把這些編譯選項(xiàng)寫入這個(gè)文件中,然后,在FastMM4.pas開頭的“{$Include FastMM4Options.inc}”之前加入“{$include FastShareMM.inc}”即可,這樣,不同的工程可以使用不同的FastShareMM.inc文件。

      五、多線程下的內(nèi)存管理
      多線程環(huán)境下,內(nèi)存管理是安全的嗎?顯然,如果不采取一定的措施,那么肯定是不安全的,borland已經(jīng)考慮到這種情況,因此,在delphi的system.pas中定義了一個(gè)系統(tǒng)變量IsMultiThread,這個(gè)系統(tǒng)變量指示當(dāng)前是否為多線程環(huán)境,那么,它是如何工作的?打開TThread.Create函數(shù)的代碼可以看到它調(diào)用了BeginThread來創(chuàng)建一個(gè)線程,而BeginThread把IsMultiThread設(shè)置為了True.
      再來看看GETMEM.INC的SysGetMem、SysFreeMem、SysReallocMem的實(shí)現(xiàn),可以看到,在開始都由這樣的語句:
      if IsMultiThread then EnterCriticalSection(heapLock);
      也就是說,在多線程環(huán)境下,內(nèi)存的分配和釋放都要用臨界區(qū)進(jìn)行同步以保證安全。
      而FastMM則使用了一條CUP指令lock來實(shí)現(xiàn)同步,該指令作為其他指令的前綴,可以在在一條指令的執(zhí)行過程中將總線鎖住,當(dāng)然,也是在IsMultiThread為True的情況下才會(huì)進(jìn)行同步。
      而IsMultiThread是定義在system.pas的系統(tǒng)變量,每個(gè)模塊(exe或者dll)都有自己的IsMultiThread變量,并且,默認(rèn)為Fasle,只有該模塊中創(chuàng)建了用戶線程,才會(huì)把這個(gè)變量設(shè)置為True,因此,我們在exe中創(chuàng)建線程,只會(huì)把exe中的IsMultiThread設(shè)置為True,并不會(huì)把其他的dll模塊中的IsMultiThread設(shè)置為True,但是,前面已經(jīng)說過,如果我們使用了靜態(tài)加載的dll,這些dll將會(huì)比exe更早被系統(tǒng)加載,這時(shí),第一個(gè)被加載的dll就會(huì)創(chuàng)建一個(gè)內(nèi)存管理器并共享出來,其他模塊都會(huì)使用這個(gè)內(nèi)存管理器,也就是說,exe的IsMultiThread變量沒有影響到應(yīng)用程序的內(nèi)存管理器,內(nèi)存管理器還是認(rèn)為當(dāng)前不是多線程環(huán)境,因此,沒有進(jìn)行同步,這樣就會(huì)出現(xiàn)內(nèi)存錯(cuò)誤的情況。
      解決這個(gè)問題就是要想辦法當(dāng)處于多線程環(huán)境時(shí),讓所有的模塊的IsMultiThread都設(shè)置為True,以保證不管哪個(gè)模塊實(shí)際創(chuàng)建了內(nèi)存管理器,該內(nèi)存管理器都知道當(dāng)前是多線程環(huán)境,需要進(jìn)行同步處理。
      還好,windows提供了一個(gè)機(jī)制,可以讓我們的dll知道應(yīng)用程序創(chuàng)建了線程。DllMain函數(shù)是dll動(dòng)態(tài)鏈接庫的入口函數(shù),delphi把這個(gè)入口函數(shù)封裝起來,提供了一個(gè)TDllProc的函數(shù)類型和一個(gè)該類型的變量DllProc:

      TDLLProc = procedure (Reason: Integer); // 定義在system.pas
      // 定義在sysinit.pas:
      var
      DllProc: TDllProc;

      當(dāng)系統(tǒng)調(diào)用dll的DllMain時(shí),delphi最后會(huì)調(diào)用DllProc進(jìn)行處理,DllProc可以被指向我們自己的TDllProc實(shí)現(xiàn)。而當(dāng)進(jìn)程創(chuàng)建了一個(gè)新線程時(shí),操作系統(tǒng)會(huì)以Reason=DLL_THREAD_ATTACH為參數(shù)調(diào)用DllMain,那么delphi最后會(huì)以該參數(shù)調(diào)用DllProc,因此我們只要實(shí)現(xiàn)一個(gè)新的TDllProc實(shí)現(xiàn)ThisDllProc,并將DllProc指向ThisDllProc,而在ThisDllProc中,當(dāng)收到DLL_THREAD_ATTACH時(shí)把IsMultiThread設(shè)置為True即可。實(shí)現(xiàn)代碼如下:

      library xxx;
      var
      OldDllProc: TDLLProc;
      procedure ThisDllProc(Reason: Integer);
      begin
      if Reason = DLL_THREAD_ATTACH then
      IsMultiThread := True;
      if Assigned(OldDllProc) then
      OldDllProc(Reason);
      end;
      begin
      OldDllProc := DllProc;
      DllProc := ThisDllProc;
      ThisDllProc(DLL_PROCESS_ATTACH);


      六、總結(jié)
      本文主要討論了下面幾個(gè)問題:
      1、為什么要使用FastMM
      2、跨模塊傳遞動(dòng)態(tài)內(nèi)存變量會(huì)出現(xiàn)什么問題,原因是什么
      3、delphi的內(nèi)存管理和內(nèi)存管理器是怎么回事
      4、為什么要共享內(nèi)存管理器,delphi和FastMM分別是如何實(shí)現(xiàn)內(nèi)存管理器共享的
      5、多線程環(huán)境下,內(nèi)存管理器如何實(shí)現(xiàn)同步
      6、多線程環(huán)境下,如何跨模塊設(shè)置IsMultiThread變量以保證內(nèi)存管理器會(huì)進(jìn)行同步

      要正確使用FastMM,在模塊開發(fā)的時(shí)候需要完成以下工作:
      1、打開編譯選項(xiàng){$define ShareMM}、{$define ShareMMIfLibrary}、{$define AttemptToUseSharedMM}
      2、將FastMM(4).pas作為每個(gè)工程文件的第一個(gè)uses單元
      3、如果是dll,需要處理以DLL_THREAD_ATTACH為參數(shù)的DllMain調(diào)用,設(shè)置IsMultiThread為True

       

      1>  FastMM是開源項(xiàng)目, 從 http://sourceforge.net/projects/fastmm  下載最新版
      2>  文件夾Replacement BorlndMM DLL/Precompiled/for Delphi IDE/Performance/BorlndMM.dll,替換掉Delphi/Bin下的相應(yīng)文件就可以完成對IDE的提速
      3> Enviroment->Library->Directories-> Library Path 添加FassMM路徑,我放在Delphi安裝目錄下,直接設(shè)置為$(DELPHI)/FastMM
      4> 在你的項(xiàng)目文件中,Project->View Source打開后,uses 后第一個(gè)添加FastMM4單元
      5> 編譯運(yùn)行你的程序,如果有Memory leak,在關(guān)閉程序時(shí)會(huì)有一個(gè)提示對話框.

      對話框也是可以關(guān)閉的
      打開FastMM4Options.inc文件。在文件的末尾添加如下代碼:
      {$define Release}
      {$ifdef Release}
        {Specify the options you use for release versions below}
        {$undef FullDebugMode}
        {$undef CheckHeapForCorruption}
        {$define ASMVersion}
        {$undef EnableMemoryLeakReporting}
        {$undef UseOutputDebugString}
        {$undef LogErrorsToFile}
        {$undef LogMemoryLeakDetailToFile}
      {$else}
        {Specify the options you use for debugging below}
        {$define FullDebugMode}
        {$define EnableMemoryLeakReporting}
      {$define UseOutputDebugString}{$endif}

      重新打開把上面第一行條件編譯定義選項(xiàng){$define Release} 注釋掉就可以了
      如 //{$define Release} 即可.

        相關(guān)評(píng)論

        閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過難過
        • 5 囧
        • 3 圍觀圍觀
        • 2 無聊無聊

        熱門評(píng)論

        最新評(píng)論

        發(fā)表評(píng)論 查看所有評(píng)論(1)

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過審核才能顯示)