我们先看分配内存这部分的代码
 
  1.调用new方式分配
| int* pInt = new int; 0000004c  mov         ecx,4  00000051  call        dword ptr ds:[03B51554h] | 
  可以看到,和以前在vc6中一样,分配内存的步骤如下:
  1.  首先把sizeof(int) = 4 放到ecx中
  2.  调用operator new 去分配4个字节
  3.  调用构造函数等等......(这里不是我们的重点)
  成功分配后,会把返回地址放在eax中。
 
  2.调用gcnew方式分配
|     int^ rInt = gcnew int; 0000005d  mov         ecx,788EF9D8h  00000062  call        FCFAF66C 。。。     Foo^ rFoo = gcnew Foo; 00000072  mov         ecx,3B51768h  00000077  call        FCFAF66C | 
  可以看到gcnew也是通过把一个参数放到ecx中,然后再调用一个函数来完成分配的操作,显然0x788EF9D8应该是一个地址,而不可能是一个数值。我们可以看到这里gcnew创建两个不同类型的变量,调用的函数地址却都是0xFCFAF66C,而存放到ecx中的两个地址就不一样。究竟这几个地址代表什么呢?
 
  和new一样gcnew也是把返回地址放在eax中。我们直接从内存窗口看eax指向的内存块好了。Aha,看到了没有?
  这次的eax = 0x00F73404  对应的内存块为
 
| 0x00F73404  d8 f9 8e 78 00 00 00 00 。。。 | 
 
  这个不就是 mov 到 ecx中的值么?再回忆昨天写的分析Object对象布局的文章,可以肯定这个就是 MethodTable地址了,对于这个int来说,后面的4个字节对应的就是存放它的RawData,比如如果你初始化为 4 那么内存对应的就变化为 d8 f9 8e 79 04 00 00 00
 
  分析清楚存放到ecx中的是 MethodTable指针,我们再分析那个对应的call函数,从vm的代码可以看出,有三个全局函数用来根据MethodTable创建对象,同时MethodTable本身也提供一个成员函数Allocate(),只不过这个成员函数也是调用的下面的函数:
OBJECTREF AllocateObject( MethodTable *pMT )
OBJECTREF AllocateObjectSpecial( MethodTable *pMT )
OBJECTREF FastAllocateObject( MethodTable *pMT )
 
  其中AllocateObject又是调用AllocateObjectSpecial来完成工作。那么我们调用的应该就是AllocateObject或者FastAllocateObject了。
  在我们的例子里面两个call的地址都一样,但是你如果写下代码 double ^ pDouble = gcnew double;这个时候的地址是多少?它和int 的一样么?
  目前我还没有仔细去研究这个地址到底对应的是该类型的MethodTable::Allocate()或是上面的这三个全局函数,如果对应MethodTable::Allocate(),那么2.0中应该有个MethodTable::FastAllocate()吧,否则应该就是对应的全局函数AllocateObject 以及FastAllocateObject了。过几天一定要抽空再好好研究一下。
 
  下面看对应的delete函数。
|     delete pInt; 000000d8  mov         ecx,dword ptr [esp+18h]  000000dc  call        dword ptr ds:[03B51540h]   比较简单,就是传入地址,然后调用operator delete来释放类存,会调用析构函数 | 
 
  对应的,释放gcnew创建的对象的代码如下:
|     delete rInt; 000000ac  mov         edx,edi  000000ae  mov         ecx,788F747Ch  000000b3  call        FC8D20FD | 
  这个也相对简单,它对应vm里面的一个函数:
void  CallFinalizer(Thread* FinalizerThread, Object* fobj)
  那么也就是
fobjà edx
FinalizerThread à ecx
Call CallFinalizer
 
  但是,请注意!!!!!!!一个类包含析构函数和不包含析构函数,它对应的delete代码是不一样的,这点可以通过汇编代码比较得到,我这里就不多说了。
查看本文来源