對Object Pascal編譯器給類對象分配堆內(nèi)存細(xì)節(jié)的一種大膽猜測_第1頁
對Object Pascal編譯器給類對象分配堆內(nèi)存細(xì)節(jié)的一種大膽猜測_第2頁
對Object Pascal編譯器給類對象分配堆內(nèi)存細(xì)節(jié)的一種大膽猜測_第3頁
對Object Pascal編譯器給類對象分配堆內(nèi)存細(xì)節(jié)的一種大膽猜測_第4頁
對Object Pascal編譯器給類對象分配堆內(nèi)存細(xì)節(jié)的一種大膽猜測_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、對Object Pascal編譯器給類對象分配堆內(nèi)存細(xì)節(jié)的一種大膽猜測讀過我以前寫的文章的網(wǎng)友,都知道我是一個喜歡“刨根問底”、“死鉆牛角尖”的家伙。最近由于工作需要轉(zhuǎn)學(xué)DELPHI,在接觸Object Pascal之后,果然領(lǐng)會到了它的整潔和優(yōu)美,怪不得連程序設(shè)計語言:設(shè)計與實現(xiàn)一書的作者也稱贊pascal是“一種極優(yōu)美的語言”。但在學(xué)習(xí)過程中遇到了好多問題,特別是對于像我這樣由C+轉(zhuǎn)至OP對Object Pascal的簡稱學(xué)習(xí)的人,由于兩種語言風(fēng)格不同,問號就會更多了。其中,OP和C+語言的一個很大的區(qū)別就是:類對象或稱之為類實例的內(nèi)存分配機制不同。其中有兩方面要說:一、什么時候分配?在C

2、+中,定義了對象,那么馬上分配其內(nèi)存,之后調(diào)用其構(gòu)造函數(shù),這個內(nèi)存可能在堆中,也可能在棧內(nèi),也可能在全程數(shù)據(jù)區(qū)內(nèi)。但OP卻截然不同,定義一對象,如:obj : TObject;只是為其分配了4字節(jié)的一個指針空間,而真正的對象空間還沒有分配,那怎么用?在使用前當(dāng)然要給對象分配空間,不然就會造成訪問內(nèi)存出錯,給對象分配空間的辦法也很簡單:obj := Tobject.Create;就OK,這個對象空間是分配在堆內(nèi)的,大家知道,棧內(nèi)空間可以在使用期過后自動回收,但堆內(nèi)存需要程序員自己管理,所以在使用完類對象之后,別忘了 obj.Free 真正實現(xiàn)析構(gòu)的是obj.Destroy,但obj.free是一

3、種更安全的方式?!笆裁磿r候分配”這個問題在OP和C+上的答案確實不同,但還不至于讓我“疑惑”。知道了OP類對象是通過調(diào)用這樣的語句(構(gòu)造函數(shù)):obj := Tobject.Create;來得到堆內(nèi)存的,但在這個處理細(xì)節(jié)上,編譯器在內(nèi)部是如何實現(xiàn)分配堆內(nèi)存的呢?請看下一個問題:二、OP編譯器是如何分配的內(nèi)存?首先要感謝Lippman的Inside C+ Ojbect Model,這是一本不可多得的好書,她告訴了你對于C+編譯器實現(xiàn)的一些你最迷惑、也是最想關(guān)心的細(xì)節(jié),但不知DELPHI業(yè)界內(nèi)有沒有這樣一本書,可以讓我清楚的了解到OP編譯器具體具體到每個細(xì)節(jié)是如何給一個類對象分配堆內(nèi)存的 如果有這

4、樣的書,您一定要通知我:coder ?我大膽的做了猜測!一些小動作都是在Tobject類內(nèi)部事先已經(jīng)定義好的!下面讓我們來關(guān)注一下這幾個Tobject類方法(Tobject定義于System.pas): TObject = class constructor Create; procedure Free; class function InitInstance(Instance: Pointer): TObject; procedure CleanupInstance; class function InstanceSize: Longint; class function NewInstan

5、ce: TObject; virtual; procedure FreeInstance; virtual; destructor Destroy; virtual; end;從方法的名稱上我們能隱約的感覺到:NewInstance和FreeInstance肯定和類對象的構(gòu)造和析構(gòu)有些關(guān)聯(lián)!先來分析一下NewInstance:class function TObject.NewInstance: TObject;begin Result := InitInstance(_GetMem(InstanceSize);end;只有一句代碼,但卻調(diào)用了三個其它方法:1、class function T

6、Object.InstanceSize: Longint;begin Result := PInteger(Integer(Self) + vmtInstanceSize);end;這個方法是OP類實現(xiàn)RTTI的一個重要方法,它能返回類對象所需要占用堆內(nèi)存的大小,注意它并非是類對象所占有內(nèi)存大小,因為類對象是一指針,那么在32位環(huán)境下,指針永遠(yuǎn)是4字節(jié)!大家可能對這句代碼比較疑惑Result := PInteger(Integer(Self) + vmtInstanceSize);下面我定義一個OP類:TBase = class(TObject) x : Integer; y : Double

7、; constructor Create;end;然后分配內(nèi)存:b : Tbase ; b := TBase.Create;我設(shè)想分配后的內(nèi)存布局應(yīng)是這樣的按C+對象的內(nèi)存考慮聯(lián)想的:再來看這句:Result := PInteger(Integer(Self) + vmtInstanceSize);它的目的是取到VMT中Index = -40注意:常量vmtInstanceSize = -40的格子中的內(nèi)容。大家看這里的Self變量是什么值呢?是b的值也就是VPTR的ADDRESS嗎?絕對不是!因為程序在執(zhí)行到TObject.InstanceSize時只是想通過調(diào)用它知道得劃分多少堆

8、內(nèi)存,但還沒有正式分配堆內(nèi)存,也就是說,VPTR、X、Y還不存在但VMT是和類一同建立起來的,它包含了和類有關(guān)的一些信息,如類實例需要請求的堆內(nèi)存的大小等等,當(dāng)然這個Self也就不能是b的值了,我猜測它的內(nèi)容是VMT中index = 0的格子的Address,只有這樣,這里的代碼和下面要講的代碼才能被正常解釋,但,Self是怎么被Assigned為這個值的,我想是編譯器所做的處理吧。這樣,Result := PInteger(Integer(Self) + vmtInstanceSize)自然得到了類對象所需要堆內(nèi)存大小的信息!為了證明我上面的猜測是正確的,大家可以實驗以下代碼:var b :

9、Tbase;size_b : Integer;beginb := TBase.Create;ShowMessage(Format('InitanceSize of TBase : %d',b.InstanceSize); size_b := PInteger(PInteger(b) - 40);ShowMessage(Format('InitanceSize of TBase : %d',size_b);end;大家可以看到,兩種方法得到的是同一個值!好,現(xiàn)在我們回過頭來講解TObject.NewInstance中要調(diào)用的第二個函數(shù)。2、functio

10、n _GetMem(Size: Integer): Pointer;它在System.pas 中的定義如下:function _GetMem(Size: Integer): Pointer;$IF Defined(DEBUG) and Defined(LINUX)var Signature: PLongInt;$IFENDbegin if Size > 0 then begin$IF Defined(DEBUG) and Defined(LINUX) Signature := PLongInt(MemoryManager.GetMem(Size + 4); if Signatu

11、re = nil then Error(reOutOfMemory); Signature := 0; Result := Pointer(LongInt(Signature) + 4);$ELSE Result := MemoryManager.GetMem(Size); if Result = nil then Error(reOutOfMemory);$IFEND end else Result := nil;end;具體代碼就不分析了,但我們終于看到了OP中分配堆內(nèi)存的具體函數(shù),原來是OP是通過一個內(nèi)存管理器MemoryManager來管理類對象所取得的堆內(nèi)存空間的!TObject.N

12、ewInstance中第三個調(diào)用的方法:3、class function TObject.InitInstance(Instance: Pointer): TObject;$IFDEF PUREPASCALvar IntfTable: PInterfaceTable; ClassPtr: TClass; I: Integer;begin FillChar(Instance, InstanceSize, 0); PInteger(Instance) := Integer(Self); ClassPtr := Self; while ClassPtr <> nil do begin I

13、ntfTable := ClassPtr.GetInterfaceTable; if IntfTable <> nil then for I := 0 to IntfTable.EntryCount-1 do with IntfTable.EntriesI do begin if VTable <> nil then PInteger(PChar(Instance)IOffset) := Integer(VTable); end; ClassPtr := ClassPtr.ClassParent; end; Result := Instance;end;$ELSEasm

14、 PUSH EBX PUSH ESI PUSH EDI MOV EBX,EAX MOV EDI,EDX STOSD MOV ECX,EBX.vmtInstanceSize XOR EAX,EAX PUSH ECX SHR ECX,2 DEC ECX REP STOSD POP ECX AND ECX,3 REP STOSB MOV EAX,EDX MOV EDX,ESP0: MOV ECX,EBX.vmtIntfTable TEST ECX,ECX JE 1 PUSH ECX1: MOV EBX,EBX.vmtParent TEST EBX,EBX JE 2 MOV EBX,EBX JMP 0

15、2: CMP ESP,EDX JE 53: POP EBX MOV ECX,EBX.TInterfaceTable.EntryCount ADD EBX,44: MOV ESI,EBX.TInterfaceEntry.VTable TEST ESI,ESI JE 4a MOV EDI,EBX.TInterfaceEntry.IOffset MOV EAX+EDI,ESI4a: ADD EBX,TYPE TInterfaceEntry DEC ECX JNE 4 CMP ESP,EDX JNE 35: POP EDI POP ESI POP EBXend;$ENDIF剛才知道_GetMem已經(jīng)得

16、到了堆內(nèi)存空間,而我們現(xiàn)在要討論的這個方法是進(jìn)行一些必須的初始化。其它代碼不管,只看這兩句:FillChar(Instance, InstanceSize, 0);PInteger(Instance) := Integer(Self);第一就是給類對象清零,現(xiàn)在我們知道為什么OP的類實例的字段會自動被初始化為零了吧String就為空,指針就為nil!第二條語句,是讓VTPR指針指向VMT表的0號格子讀者請參考結(jié)構(gòu)圖自行分析,此處也證明上面我對Self值的猜測的正確性。到了這里,你也許會說,說了半天,都是猜測,或許,OP編譯器根本就不會調(diào)用那個TObject.NewInstance方法呢!問得好

17、,再做實驗!還是以上面的那個Tbase類為例,重載TObject.NewInstance方法,如下:TBase = class(TObject) x : Integer; y : Double; class function NewInstance: TObject; override; procedure FreeInstance; override; constructor Create; end; 實現(xiàn)constructor TBase.Create;begin self.x := 2; self.y := 3.14;end; procedure TBase.Free

18、Instance;begin inherited; ShowMessage(Format('Call %s.FreeInstance!',self.ClassName);end; class function TBase.NewInstance: TObject;begin ShowMessage(Format('call %s.NewInstance',self.ClassName); result := inherited NewInstance;end; 之后進(jìn)行簡單的聲明對象:varb : Tbase;beginb := Tbase.

19、Create; ß在這里設(shè)斷點!b.Free;end;通過對代碼進(jìn)行跟蹤果然在一進(jìn)入Create就馬上調(diào)用NewInstance方法。說明:一定要重載它才能跟蹤到它,在斷點處,觀察CPU,從反匯編后的代碼中可以發(fā)現(xiàn),是先調(diào)用一個_ClassCreate,然后才調(diào)用NewInstance 用同樣的方法可以分析出b.Free會最終調(diào)用到FreeInstance;來釋放對象。 我想基本上大的問題已經(jīng)說請了,Object Pascal為了實現(xiàn)分配堆內(nèi)存,在你調(diào)用構(gòu)造器的時候:b := Tbase.Create;在構(gòu)造方法內(nèi)你的代碼前,安插了代碼調(diào)用NewInstance方法,析構(gòu)時

20、,則在析構(gòu)函數(shù)中你的代碼后,調(diào)用FreeInstance函數(shù)。 那么,現(xiàn)在再來看這種情況:派生TBase = class(TObject) x : Integer; y : Double; class function NewInstance: TObject; override; procedure FreeInstance; override; constructor Create; end;  TSub = class (TBase) m : Integer; n : Double; constructor Create; end; 實現(xiàn)constructo

21、r TBase.Create;begin self.x := 2; self.y := 3.14;end; procedure TBase.FreeInstance;begin inherited; ShowMessage(Format('Call %s.FreeInstance!',self.ClassName); end; class function TBase.NewInstance: TObject;begin ShowMessage(Format('call %s.NewInstance',self.ClassName)

22、; result := inherited NewInstance; end;  TSub  constructor TSub.Create;begin inherited Create; ß注意這里! self.m := 4; self.n := 12.32;end;我們已經(jīng)知道,vars : Tsub; s := Tsub.Create;時,在進(jìn)入Tsub.Create內(nèi)部馬上得到了它想要的內(nèi)存這里是32字節(jié),與內(nèi)存對齊有關(guān),類里含有最大數(shù)double:8字節(jié),所以總長度要8的倍數(shù),那么當(dāng):inherited Create;時,在Tbase.Create內(nèi)部,還有內(nèi)存分配的動作嗎?我們可以通過三點證明:這里,Tbase.Creat

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論