首页
社区
课程
招聘
[原创] CLR运行时细节 - 继承多态的实现
发表于: 2018-3-29 09:56 6176

[原创] CLR运行时细节 - 继承多态的实现

2018-3-29 09:56
6176

关于多态不多解释了,在运行时决定和调用具体的实现,是面向对象的基础 设计模式的基础.
准备把继承多态和接口多态分开,因为从CLR实现的角度继承多态相比于接口多态要简单得多,也更容易理解,本篇只讨论继承多态, .NET Framework 2.0 和 4.0 这两个版本在实现上稍微有点区别(这里先忽略方法Jit编译的过程,只关注实现的方式).

废话不多,先看代码: C# Polymorphism01.cs

运行 Polymorphism01_2.0.exe

启动windbg 附加进程 加载SOS

查找对应的模块:
!Name2EE *!Polymorphism01_2.0.exe

先分别看下 BaseClass BrotherClass DerivedOfBrotherClass 这3个继承关系类的方法表(MethodTable)

图片描述

图片描述
图片描述

可以看到第一个虚方法(ToString)的入口都是在方法表偏移28h的位置,其顺序是先父类,再子类,这样的安排让所有同一个家族(继承关系)的类型继承虚方法的顺序是一样的,并且偏移量是一样的,所有的类型(除了接口类型)的父类都是(或者间接是)System.Object,所以前4个虚方法肯定是Object里的4个虚方法(ToString Equals GetHashCode Finalize)

这里最重要的几行:

可以看到 继承多态在CLR运行时的实现是通过方法表的偏移 间接调用的,而方法表内继承虚方法的构建顺序是先父类再子类,由于.NET是单一继承,这样就确保了在同一家族的同一虚方法的偏移量是一样的.

接下来用Framework 4.0 编译下源码,4.0 和2.0相比 在实现上多了一层间接寻址,但思路是一样的

运行 Polymorphism01_4.0.exe

启动windbg 附加进程 加载SOS (这里要加载对于4.0的sos.dll)

个人页地址:https://espider.github.io/CLR/inheritance-polymorphism/
欢迎讨论~

https://www.microsoft.com/china/MSDN/library/netFramework/netframework/JITCompiler.mspx?mfr=true
http://www.codeproject.com/Articles/20481/NET-Type-Internals-From-a-Microsoft-CLR-Perspecti
http://blogs.microsoft.co.il/sasha/2012/03/15/virtual-method-dispatch-and-object-layout-changes-in-clr-40/
http://www.cnblogs.com/BlueTzar/articles/884694.html

 
using System;
using System.Runtime.CompilerServices;

public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Polymorphism01 demo");

        BaseClass bc = new BaseClass();
        BaseClass bc1 = new ChlidClass();
        BaseClass bc2 = new BrotherClass();
        BaseClass bc3 = new DerivedOfBrotherClass();
        BrotherClass bc4 = new DerivedOfBrotherClass();

        bc.VirtualFun1();
        bc1.VirtualFun1();
        bc2.VirtualFun1();
        bc3.VirtualFun1();
        bc3.VirtualFun2();
        bc4.VirtualFun3();

        Console.ReadLine();
    }
}

public class BaseClass
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public virtual void VirtualFun1()
    {
        Console.WriteLine("BaseClass VirtualFun1");
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public virtual void VirtualFun2()
    {
        Console.WriteLine("BaseClass VirtualFun2");
    }
}

public class ChlidClass : BaseClass
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public override void VirtualFun1()
    {
        Console.WriteLine("ChlidClass VirtualFun1");
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public override void VirtualFun2()
    {
        Console.WriteLine("ChlidClass VirtualFun2");
    }
}

public class BrotherClass : BaseClass
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public override void VirtualFun1()
    {
        Console.WriteLine("BrotherClass VirtualFun1");
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public override void VirtualFun2()
    {
        Console.WriteLine("BrotherClass VirtualFun2");
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public virtual void VirtualFun3()
    {
        Console.WriteLine("BrotherClass VirtualFun3");
    }
}

public class DerivedOfBrotherClass : BrotherClass
{
    [MethodImpl(MethodImplOptions.NoInlining)]
    public override void VirtualFun1()
    {
        Console.WriteLine("DerivedOfBrotherClass VirtualFun1");
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public override void VirtualFun2()
    {
        Console.WriteLine("DerivedOfBrotherClass VirtualFun2");
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public override void VirtualFun3()
    {
        Console.WriteLine("DerivedOfBrotherClass VirtualFun3");
    }
}
%windir%\Microsoft.NET\Framework\v2.0.50727\csc.exe /debug /target:exe /out:e:\temp\Polymorphism01_2.0.exe e:\temp\Polymorphism01.cs
pause
0:004> !Name2EE *!Polymorphism01_2.0.exe
Module: 790c1000 (mscorlib.dll)
--------------------------------------
Module: 00af2c5c (Polymorphism01_2.0.exe)
0:004> !DumpModule -mt 00af2c5c
Name: E:\temp\Polymorphism01_2.0.exe
Attributes: PEFile 
Assembly: 00167720
LoaderHeap: 00000000
TypeDefToMethodTableMap: 00af00c0
TypeRefToMethodTableMap: 00af00dc
MethodDefToDescMap: 00af0100
FieldDefToDescMap: 00af0144
MemberRefToDescMap: 00af0148
FileReferencesMap: 00af016c
AssemblyReferencesMap: 00af0170
MetaData start address: 00402170 (1756 bytes)

Types defined in this module

      MT    TypeDef Name
------------------------------------------------------------------------------
00af302c 0x02000002 Program
00af3098 0x02000003 BaseClass
00af310c 0x02000004 ChlidClass
00af3188 0x02000005 BrotherClass
00af3208 0x02000006 DerivedOfBrotherClass

Types referenced in this module

      MT    TypeRef Name
------------------------------------------------------------------------------
793308f8 0x01000001 System.Object
79334648 0x01000006 System.Console
0:004> !DumpMT -md 00af302c
EEClass: 00af12f4
Module: 00af2c5c
Name: Program
mdToken: 02000002  (E:\temp\Polymorphism01_2.0.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 6
--------------------------------------
MethodDesc Table
   Entry MethodDesc      JIT Name
79286aa0   79104960   PreJIT System.Object.ToString()
79286ac0   79104968   PreJIT System.Object.Equals(System.Object)
79286b30   79104998   PreJIT System.Object.GetHashCode()
792f76d0   791049bc   PreJIT System.Object.Finalize()
00afc015   00af3024     NONE Program..ctor()
01010070   00af3018      JIT Program.Main(System.String[])
0:004> !u 01010070
Normal JIT generated code
Program.Main(System.String[])
Begin 01010070, size 10a
>>> 01010070 55              push    ebp
01010071 8bec            mov     ebp,esp
01010073 83ec2c          sub     esp,2Ch
01010076 894dfc          mov     dword ptr [ebp-4],ecx
01010079 833d142eaf0000  cmp     dword ptr ds:[0AF2E14h],0
01010080 7405            je      01010087
01010082 e832ff0d79      call    mscorwks!JIT_DbgIsJustMyCode (7a0effb9)
01010087 33d2            xor     edx,edx
01010089 8955ec          mov     dword ptr [ebp-14h],edx
0101008c 33d2            xor     edx,edx
0101008e 8955f0          mov     dword ptr [ebp-10h],edx
01010091 33d2            xor     edx,edx
01010093 8955f4          mov     dword ptr [ebp-0Ch],edx
01010096 33d2            xor     edx,edx
01010098 8955f8          mov     dword ptr [ebp-8],edx
0101009b 33d2            xor     edx,edx
0101009d 8955e8          mov     dword ptr [ebp-18h],edx
010100a0 90              nop
010100a1 8b0d30204202    mov     ecx,dword ptr ds:[2422030h] ("Polymorphism01 demo")
*** WARNING: Unable to verify checksum for C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\b14359470744c840c59fbe4e58034fd6\mscorlib.ni.dll
010100a7 e8b0457878      call    mscorlib_ni+0x6d465c (7979465c) (System.Console.WriteLine(System.String), mdToken: 060007c8)
010100ac 90              nop
010100ad b99830af00      mov     ecx,0AF3098h (MT: BaseClass)
010100b2 e8651fadff      call    00ae201c (JitHelp: CORINFO_HELP_NEWSFAST)
010100b7 8945e4          mov     dword ptr [ebp-1Ch],eax
010100ba 8b4de4          mov     ecx,dword ptr [ebp-1Ch]
010100bd ff15d830af00    call    dword ptr ds:[0AF30D8h] (BaseClass..ctor(), mdToken: 06000005)
010100c3 8b45e4          mov     eax,dword ptr [ebp-1Ch]
010100c6 8945f8          mov     dword ptr [ebp-8],eax
010100c9 b90c31af00      mov     ecx,0AF310Ch (MT: ChlidClass)
010100ce e8491fadff      call    00ae201c (JitHelp: CORINFO_HELP_NEWSFAST)
010100d3 8945e0          mov     dword ptr [ebp-20h],eax
010100d6 8b4de0          mov     ecx,dword ptr [ebp-20h]
010100d9 ff154c31af00    call    dword ptr ds:[0AF314Ch] (ChlidClass..ctor(), mdToken: 06000008)
010100df 8b45e0          mov     eax,dword ptr [ebp-20h]
010100e2 8945f4          mov     dword ptr [ebp-0Ch],eax
010100e5 b98831af00      mov     ecx,0AF3188h (MT: BrotherClass)
010100ea e82d1fadff      call    00ae201c (JitHelp: CORINFO_HELP_NEWSFAST)
010100ef 8945dc          mov     dword ptr [ebp-24h],eax
010100f2 8b4ddc          mov     ecx,dword ptr [ebp-24h]
010100f5 ff15cc31af00    call    dword ptr ds:[0AF31CCh] (BrotherClass..ctor(), mdToken: 0600000c)
010100fb 8b45dc          mov     eax,dword ptr [ebp-24h]
010100fe 8945f0          mov     dword ptr [ebp-10h],eax
01010101 b90832af00      mov     ecx,0AF3208h (MT: DerivedOfBrotherClass)
01010106 e8111fadff      call    00ae201c (JitHelp: CORINFO_HELP_NEWSFAST)
0101010b 8945d8          mov     dword ptr [ebp-28h],eax
0101010e 8b4dd8          mov     ecx,dword ptr [ebp-28h]
01010111 ff154c32af00    call    dword ptr ds:[0AF324Ch] (DerivedOfBrotherClass..ctor(), mdToken: 06000010)
01010117 8b45d8          mov     eax,dword ptr [ebp-28h]
0101011a 8945ec          mov     dword ptr [ebp-14h],eax
0101011d b90832af00      mov     ecx,0AF3208h (MT: DerivedOfBrotherClass)
01010122 e8f51eadff      call    00ae201c (JitHelp: CORINFO_HELP_NEWSFAST)
01010127 8945d4          mov     dword ptr [ebp-2Ch],eax
0101012a 8b4dd4          mov     ecx,dword ptr [ebp-2Ch]
0101012d ff154c32af00    call    dword ptr ds:[0AF324Ch] (DerivedOfBrotherClass..ctor(), mdToken: 06000010)
01010133 8b45d4          mov     eax,dword ptr [ebp-2Ch]
01010136 8945e8          mov     dword ptr [ebp-18h],eax
01010139 8b4df8          mov     ecx,dword ptr [ebp-8]
0101013c 8b01            mov     eax,dword ptr [ecx]
0101013e ff5038          call    dword ptr [eax+38h]
01010141 90              nop
01010142 8b4df4          mov     ecx,dword ptr [ebp-0Ch]
01010145 8b01            mov     eax,dword ptr [ecx]
01010147 ff5038          call    dword ptr [eax+38h]
0101014a 90              nop
0101014b 8b4df0          mov     ecx,dword ptr [ebp-10h]
0101014e 8b01            mov     eax,dword ptr [ecx]
01010150 ff5038          call    dword ptr [eax+38h]
01010153 90              nop
01010154 8b4dec          mov     ecx,dword ptr [ebp-14h]
01010157 8b01            mov     eax,dword ptr [ecx]
01010159 ff5038          call    dword ptr [eax+38h]
0101015c 90              nop
0101015d 8b4dec          mov     ecx,dword ptr [ebp-14h]
01010160 8b01            mov     eax,dword ptr [ecx]
01010162 ff503c          call    dword ptr [eax+3Ch]
01010165 90              nop
01010166 8b4de8          mov     ecx,dword ptr [ebp-18h]
01010169 8b01            mov     eax,dword ptr [ecx]
0101016b ff5040          call    dword ptr [eax+40h]
0101016e 90              nop
0101016f e8fc477878      call    mscorlib_ni+0x6d4970 (79794970) (System.Console.ReadLine(), mdToken: 060007ba)
01010174 90              nop
01010175 90              nop
01010176 8be5            mov     esp,ebp
01010178 5d              pop     ebp
01010179 c3              ret
01010139 8b4df8          mov     ecx,dword ptr [ebp-8]   // 这里是BaseClass实例对象的地址 放到 ecx寄存器,Jit采用类似fastcall的调用协定,前2个不大于4字节的参数用 ecx edx来传递,而实例方法的调用第一个参数是隐含的this指针(托管对象在托管堆上的地址),如果是静态方法就不需要传this pointer了
0101013c 8b01            mov     eax,dword ptr [ecx]    // 托管堆上的对象(值类型装箱后也是一样)第一个4字节(64位8字节)是对象的方法表地址(MethodTable),这里是把方法表(MethodTable)地址赋给eax寄存器
0101013e ff5038          call    dword ptr [eax+38h]    // 这里就是实际的方法调用 上面说了 第一个虚方法在方法表的偏移28h位置,前4个是Object里的4个虚方法,所以 VirtualFun1 的入口在方法表地址(MT) + 28h + 4×4字节 也就是偏移38h的位置
01010141 90              nop
01010142 8b4df4          mov     ecx,dword ptr [ebp-0Ch]    // 这里是 ChlidClass的对象地址赋给ecx
01010145 8b01            mov     eax,dword ptr [ecx]    // 同样ChlidClass的方法表地址赋给eax
01010147 ff5038          call    dword ptr [eax+38h]    // 调用ChlidClass方法表偏移38h的方法,也是VirtualFun1 方法
0101014a 90              nop
0101014b 8b4df0          mov     ecx,dword ptr [ebp-10h]    // BrotherClass的对象地址赋给ecx
0101014e 8b01            mov     eax,dword ptr [ecx]    // BrotherClass方法表地址赋给eax
01010150 ff5038          call    dword ptr [eax+38h]    // 调用BrotherClass方法表偏移38h的方法,也是VirtualFun1 方法
01010153 90              nop
01010154 8b4dec          mov     ecx,dword ptr [ebp-14h]    // DerivedOfBrotherClass的对象地址赋给ecx
01010157 8b01            mov     eax,dword ptr [ecx]    // DerivedOfBrotherClass方法表地址赋给eax
01010159 ff5038          call    dword ptr [eax+38h]     // 调用DerivedOfBrotherClass方法表偏移38h的方法,也是VirtualFun1 方法
0101015c 90              nop
0101015d 8b4dec          mov     ecx,dword ptr [ebp-14h]    // 还是DerivedOfBrotherClass对象地址
01010160 8b01            mov     eax,dword ptr [ecx]    // DerivedOfBrotherClass的方法表赋给eax
01010162 ff503c          call    dword ptr [eax+3Ch]    // 这次偏移不一样了,第6个方法 VirtualFun2 (28h+5×4字节)
01010165 90              nop
01010166 8b4de8          mov     ecx,dword ptr [ebp-18h]    // 还是DerivedOfBrotherClass对象地址
01010169 8b01            mov     eax,dword ptr [ecx]    // DerivedOfBrotherClass的方法表赋给eax
0101016b ff5040          call    dword ptr [eax+40h]    // 这次偏移又不一样了,第7个方法 VirtualFun3 (28h+6×4字节)
%windir%\Microsoft.NET\Framework\v4.0.30319\csc.exe /debug /target:exe /out:e:\temp\Polymorphism01_4.0.exe e:\temp\Polymorphism01.cs
pause
0:004> !Name2EE Polymorphism01_4.0.exe Program.Main
Module:      00b32ea4
Assembly:    Polymorphism01_4.0.exe
Token:       06000001
MethodDesc:  00b33838
Name:        Program.Main(System.String[])
JITTED Code Address: 033a0070

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2019-2-1 20:12 被admin编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (5)
雪    币: 9
活跃值: (205)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
2
2018-4-2 12:21
0
雪    币: 38
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
辛苦了
2018-4-2 14:49
0
雪    币: 66
活跃值: (2807)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
论坛关于托管程序的分析还比较少,赞一个
2018-4-3 15:07
0
雪    币: 5467
活跃值: (3854)
能力值: ( LV13,RANK:283 )
在线值:
发帖
回帖
粉丝
5
多谢分享
2018-4-5 08:38
0
雪    币: 157
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
看不懂,但是膜拜一下
2018-4-7 16:29
0
游客
登录 | 注册 方可回帖
返回
//