CLR运行时细节 - Method Descriptor

方法描述符:MethodDesc

  • 运行时用来描述类型的托管方法,它保存在方法描述桶(MethodDescChunk)内;

  • 方法描述符保存了方法在运行时的一些重要信息:

    • 是否JIT编译;

    • 是否有方法表槽(决定了方法入口是跟在方法描述符(MethodDesc)后还是在方法表(MethodTable)后面);

    • 距离MethodDescChunk的索引(chunkIndex);

    • Token的末位(这个在编译期确定了);

    • 方法的一些标识比如是否静态 非内联等;

    • 方法表槽(slot number);

    • 以及最重要的方法入口(entrypoint);

官方的描述:
MethodDesc (method descriptor) is the internal representation of a managed method. It serves several purposes:

  • Provides a unique method handle, usable throughout the runtime. For normal methods, the MethodDesc is a unique handle for a triplet.

  • Caches frequently used information that is expensive to compute from metadata (e.g. whether the method is static).

  • Captures the runtime state of the method (e.g. whether the code has been generated for the method already).

  • Owns the entry point of the method.

先看下Demo C# MethodDesc.cs 代码:



using System;

using System.Runtime.CompilerServices;

public class Program

{

public static void Main(string[] args)

{

Console.WriteLine("MethodDesc demo");

Console.ReadLine();

MDChlidClass cc = new MDChlidClass();

cc.VirtualFun1();

cc.VirtualFun2();

cc.IFun1();

cc.IFun2();

cc.InstanceFun1();

cc.InstanceFun2();

MDChlidClass.StaticFun1();

Console.ReadLine();

}

}

public class MDBaseClass

{

[MethodImpl(MethodImplOptions.NoInlining)]

public virtual void VirtualFun1()

{

Console.WriteLine("MDBaseClass VirtualFun1");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public virtual void VirtualFun2()

{

Console.WriteLine("MDBaseClass VirtualFun2");

}

}

public class MDChlidClass : MDBaseClass, IFoo

{

public static int TempInt = 0;

[MethodImpl(MethodImplOptions.NoInlining)]

public override void VirtualFun1()

{

Console.WriteLine("MDChlidClass VirtualFun1");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public override void VirtualFun2()

{

Console.WriteLine("MDChlidClass VirtualFun2");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public void IFun1()

{

Console.WriteLine("MDChlidClass IFun1");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public void IFun2()

{

Console.WriteLine("MDChlidClass IFun2");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public void InstanceFun1()

{

Console.WriteLine("MDChlidClass InstanceFun1");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public void InstanceFun2()

{

Console.WriteLine("MDChlidClass InstanceFun2");

}

[MethodImpl(MethodImplOptions.NoInlining)]

public static void StaticFun1()

{

Console.WriteLine("MDChlidClass StaticFun1");

}

}

public interface IFoo

{

void IFun1();

void IFun2();

}


  • 编译代码:


1

2

%windir%\Microsoft.NET\Framework\v2.0.50727\csc.exe /debug /target:exe /out:e:\temp\MethodDesc_2.0.exe e:\temp\MethodDesc.cs

pause


  • 运行 MethodDesc_2.0.exe

  • 启动windbg 加载SOS

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

  • 根据模块查找方法表:
    !DumpModule -mt 00af2c5c

  • 通过MDChlidClass方法表地址查看其EEClass 查找方法描述桶在EEClass偏移40h的位置(64位的话偏移60h 因为标记位使用不变 所有地址类型由4字节变成8字节):

!DumpMT -md 00af31e4

通过方法桶的地址00af3180观察其下方法描述符:

  • 可以看到方法描述桶的第一个4字节(64位8字节)是方法表(MethodTable)的地址
    可以看到MDChlidClass的方法描述符(MD)

  • VirtualFun1 方法描述符地址:00af3190 其内容:00000008 20000004 第一个00代表方法入口在方法表(MT)后面以及还没jit编译,第二个00代表距方法描述桶(MethodDescChunk)的索引(便于找到桶的起始位置),后面的0008是方法的token末位 在编译成IL时确定,可以通过ildasm查看 MethodDesc_2.0.exe 文件,这个token是在编译期程序集内自增的,也就是在运行时并不是唯一的接下来的2000代表方法非内联,0004代表方法表槽slot number 也就是方法入口(entrypoint)在方法表(MT)后的索引(索引从0开始 一般来说前4个方法都是从Object继承下来的4个虚方法 除了接口类型),方法入口:00afc075

  • VirtualFun2 方法描述符地址:00af3198 其内容:00020009 20000005 依旧是没jit编译,方法入口在方法表后,token:0009,非内联,slot number:0005,方法入口:00afc079

  • IFun1 方法描述符地址:00af31a0 其内容:0004000a 20000006 依旧是没jit编译,方法入口在方法表后,token:000a,非内联,slot number:0006,方法入口:00afc07d

  • IFun2 方法描述符地址:00af31a8 其内容:0006000b 20000007 依旧是没jit编译,方法入口在方法表后,token:000b,非内联,slot number:0007,方法入口:00afc081

  • InstanceFun1 方法描述符地址:00af31b0 其内容:4008000c 2000000a 00afc085 ‘40’这位(bit)代表方法入口(slot)是跟在方法描述符(MD)后面的并非在方法表(MT)后面,依旧是没jit编译,方法入口在方法表后,token:000c,非内联,slot number:000a(这里的slot number依然有值,但值是大于等方法表的slot长度的),方法入口:00afc085

  • InstanceFun2 方法描述符地址:00af31bc 其内容:400b000d 2000000b 00afc089 依旧是没jit编译,方法入口在方法描述符(MD)后,token:000d,非内联,slot number:000b,方法入口:00afc089

  • StaticFun1 方法描述符地址:00af31c8 其内容:400b000e 2020000c 00afc08d 依旧是没jit编译,方法入口在方法描述符(MD)后,token:000e,2020非内联 并且静态,slot number:000c,方法入口:00afc08d

  • .ctor 实例构造方法 方法描述符地址:00af31d4 其内容:0011000f 00000008 依旧是没jit编译,方法入口在方法表后,token:000f,slot number:0008,方法入口:00afc091

  • .cctor 静态构造方法 方法描述符地址:00af31dc 其内容:00130010 00200009 依旧是没jit编译,方法入口在方法表后,token:0010,静态的:0020,slot number:0009,方法入口:00afc095

可以看到所有的虚方法(继承或者实现接口)以及构造器方法(实例或者静态)的方法入口(slot)都是在方法表后面的,而其他实例方法和静态方法的方法入口(slot)是跟在方法描述符(MD)后面的

这里引用下CLR文档的一段:
Each MethodDesc has a slot, which contains the entry point of the method. The slot and entry point must exist for all methods, even the ones that never run like abstract methods. There are multiple places in the runtime that depend on the 1:1 mapping between entry points and MethodDescs, making this relationship an invariant.
The slot is either in MethodTable or in MethodDesc itself. The location of the slot is determined by mdcHasNonVtableSlot bit on MethodDesc.
The slot is stored in MethodTable for methods that require efficient lookup via slot index, e.g. virtual methods or methods on generic types. The MethodDesc contains the slot index to allow fast lookup of the entry point in this case.

接下来让 MethodDesc_2.0.exe 继续执行,并回车跳过第一个ReadLine(),再中断到调试器,观察MDChlidClass的方法表00af31e4(MT)和其方法描述桶00af3180(MDC)

可以看到所有Jit编译过的方法,其方法描述符的 00h或者40h 会 逻辑  31h,都是按位的,其中30h是安全描述符先忽略,01h代表是否Jit编译过,同时所有Jit编译过的方法其方法入口(entrypoint)会更新

更新安全描述符:






先更新方法入口,再更新是否Jit编译标记位:
https://github.com/dotnet/coreclr/blob/master/src/vm/method.cpp#L5099






0:000> uf mscorwks!MethodDesc::SetStableEntryPointInterlocked

mscorwks!MethodDesc::SetStableEntryPointInterlocked:

79ed9a27 55              push    ebp

79ed9a28 8bec            mov     ebp,esp

79ed9a2a 53              push    ebx

79ed9a2b 56              push    esi

79ed9a2c 57              push    edi

79ed9a2d 8bf9            mov     edi,ecx

79ed9a2f e8b96afbff      call    mscorwks!MethodDesc::GetTemporaryEntryPoint (79e904ed)

79ed9a34 8bcf            mov     ecx,edi

79ed9a36 8bd8            mov     ebx,eax

79ed9a38 e8156bfbff      call    mscorwks!MethodDesc::GetAddrOfSlot (79e90552)

79ed9a3d 8b5508          mov     edx,dword ptr [ebp+8]

79ed9a40 53              push    ebx

79ed9a41 8bc8            mov     ecx,eax

79ed9a43 ff15d0c33c7a    call    dword ptr [mscorwks!FastInterlockCompareExchange (7a3cc3d0)]

79ed9a49 8bf0            mov     esi,eax

79ed9a4b 2bf3            sub     esi,ebx

79ed9a4d f7de            neg     esi

79ed9a4f 1bf6            sbb     esi,esi

79ed9a51 46              inc     esi

79ed9a52 ba00000001      mov     edx,1000000h

79ed9a57 8bcf            mov     ecx,edi

79ed9a59 ff1574c23c7a    call    dword ptr [mscorwks!FastInterlockOr (7a3cc274)]

79ed9a5f 5f              pop     edi

79ed9a60 8bc6            mov     eax,esi

79ed9a62 5e              pop     esi

79ed9a63 5b              pop     ebx

79ed9a64 5d              pop     ebp

79ed9a65 c20400          ret     4


  • 可以通过 DumpMD SOS扩展命令观察方法描述符:

为毛要研究方法描述符这个东西?
方法描述符在CLR运行时作为方法的最基础服务,继承多态在运行时的实现依赖方法描述符,接口多态的运行时DispatchToken以及实现也依赖.

参考文档:

https://github.com/dotnet/coreclr/blob/master/Documentation/botr/method-descriptor.md
https://github.com/dotnet/coreclr/blob/master/src/vm/methodtablebuilder.cpp
https://github.com/dotnet/coreclr/blob/master/src/vm/method.hpp
http://blogs.microsoft.co.il/sasha/2009/09/27/how-are-methods-compiled-just-in-time-and-only-then/

原文地址:https://espider.github.io/CLR/method-descriptor/


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/326660.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2015蓝桥杯省赛---java---B---3(三羊献瑞)

题目 三羊献瑞 思路分析 由于是填空题,没有时间和内存的要求,所以看到这个题,第一想法就是暴力破解,当然了,怎么快就怎么做。 由于 "三"是数字的首位,低位的数字进位后必然为1,所…

mysql common是什么_MySQL common_schema简介

common_schema为MySQL提供了查询脚本,分析并且信息化的视图和一个函数库,以便更容易的管理和诊断。它引入的一些基于SQL的工具简common_schema的简介:Shlomi Noach 的common_schema项目()是一套针对服务器脚本化和管理的强大的代码和视图。co…

数据库权限分配探讨

上周, 有位朋友给我提出了这样的需求:区分用户访问数据库的权限。顺便总结了下有如下要求: 某个用户查询所有数据库的权限 某个用户只有备份数据库的权限 给一个用户只能查看指定数据库的权限 给一个用户只有某个表的权限 要进行以上任务&…

2015蓝桥杯省赛---java---B---6(加法变乘法)

题目 加法变乘法 思路分析 两个算式进行相减操作 代码实现 package com.atguigu.lanqiao;public class Main { // 简单枚举public static void main(String[] args) {for (int i 1; i < 46; i) {for (int j i 2; j < 48; j) {if (i * (i 1) - (i i 1) j * (…

DI 之Spring更多DI的知识

转载自 【第三章】 DI 之 3.3 更多DI的知识 ——跟我学spring3 3.3.1 延迟初始化Bean 延迟初始化也叫做惰性初始化&#xff0c;指不提前初始化Bean&#xff0c;而是只有在真正使用时才创建及初始化Bean。 配置方式很简单只需在<bean>标签上指定 “lazy-init” 属性值…

Microsoft发布新工具,简化JavaScript Web应用的创建

Microsoft发布了一系列工具用于快速生成基于JavaScript的Web应用&#xff0c;这些工具包含了dotnet new和对Node.js的巧妙运用。 dotnet new命令是.NET Core工具的一部分&#xff0c;它用简单的命令行语法创建&#xff08;spin up&#xff09;新项目。现在新工具已成为ASP.NET …

2015蓝桥杯省赛---java---B---7(牌型种数)

题目 牌型种数 思路分析 递归进行实现&#xff0c;弄好终止条件&#xff0c;牌的数目等于13 代码实现 package com.atguigu.lanqiao;public class Main { // 简单枚举private static int ans;public static void main(String[] args) {f(0, 0);System.out.println(ans);}…

CLR运行时细节 - 继承多态的实现

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

2015蓝桥杯省赛---java---B---8(饮料换购)

题目 饮料换购 饮料换购乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊C型饮料&#xff0c;凭3个瓶盖可以再换一瓶C型饮料&#xff0c;并且可以一直循环下去&#xff0c;但不允许赊账。请你计算一下&#xff0c;如果小明不浪费瓶盖&#xff0c;尽量地参加活动&#xff0c;那么…

python123测验9程序题_python程序设计实验二

Python程序设计实验安徽工程大学班级&#xff1a;物流191 姓名&#xff1a;许岚岚 学号&#xff1a;3190505110日期&#xff1a;2020年3月21日指导教师&#xff1a;修 宇实验二 顺序结构程序设计(验证性实验)(二学时)【实验目的】(1)掌握数据的输入输出的方法&#…

Rider IDE恢复了对.NET Core调试的支持

近期发布的JetBrain的Rider IDE&#xff08;EAP17&#xff09;移除了对.NET Core调试支持。该功能与NuGet的一个软件包在许可上存在冲突&#xff0c;而EAP17使用NuGet提供的.NET Core项目调试功能&#xff0c;所以必须要移除该功能。为此&#xff0c;JetBrains迅速推出了Rider …

2016蓝桥杯省赛---java---B---1(煤球数目)

题目 煤球数目 思路分析 代码实现 package com.atguigu.lanqiao;import java.util.Scanner;public class Main { // 简单枚举public static void main(String[] args) { // 171700int pre 1;int plus 2;long sum 1;for (int k 2; k < 100; k) {sum (pre plus); //…

vue 多页面多模块分模块打包 分插件安装_Vue渲染方式

Vue中的渲染方式总结可分四种:原有模板语法&#xff0c;挂载渲染使用render属性&#xff0c;createElement函数直接渲染使用render属性&#xff0c;配合组件的template属性&#xff0c;createElement函数渲染使用render属性&#xff0c;配合单文件组件&#xff0c;createElemen…

零配置 之 Spring注解实现Bean定义

转载自 零配置 之 12.3 注解实现Bean定义 ——跟我学spring3 12.3 注解实现Bean定义 12.3.1 概述 前边介绍的Bean定义全是基于XML方式定义配置元数据&#xff0c;且在【12.2注解实现Bean依赖注入】一节中介绍了通过注解来减少配置数量&#xff0c;但并没有完全消除在XML…

开源库 Natasha2016 ,让IL编程跑起来

背景&#xff1a; IL编程在普通的程序员的代码里几乎不会出现&#xff0c;但从Json.net、Dapper、Asp.net等等开源项目都体现出了IL编程的重要性。 在IL编程的时候&#xff0c;上百行甚至上千行的IL代码实在让人头大&#xff0c;调试不方便不说&#xff0c;IL编程的逻辑也是不同…

代码随想录27期|Python|Day24|回溯法|理论基础|77.组合

图片来自代码随想录 回溯法题目目录 理论基础 定义 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。 回溯是递归的副产品&#xff0c;只要有递归就会有回溯。回溯函数也就是递归函数&#xff0c;指的都是一个函数。 基本问题 组合问题&#xff08;无序&…

指纹识别软件安装包下载

微信关注公众号&#xff1a;1111的博客&#xff0c;回复“指纹识别”&#xff0c;即可获取指纹识别安装包的下载链接。本软件的版本为指纹识别2.0应用程序&#xff0c;在安装的过程中有以下几个注意点【一定要看】&#xff1a;安装完成之后直接运行&#xff1a;指纹识别.exe文件…

2016蓝桥杯省赛---java---B---2(生日蜡烛)

题目描述 生日蜡烛 思路分析 代码实现(方式一) package com.atguigu.TEST;class Main{public static void main(String[] args) {int sum236;int a0;//记录开始过生日的年龄for (int i 0; i < 100; i) {for (int j i; j < 100; j) {aaj;if(asum){System.out.printl…

ntp时间同步会导致mysql关闭吗?_ntp时间同步问题解决方法

http://www.pool.ntp.org/zone/asia0.asia.pool.ntp.org1.asia.pool.ntp.org2.asia.pool.ntp.org3.asia.pool.ntp.org2.1 注册表信息修改PDC配置外部时间源设置#config external NTP serverw32tm.exe /config /manualpeerlist:”0.asia.pool.ntp.org,1.asia.pool.ntp.org,2.asi…

javaSE视频教程正式启动

亲爱的小伙伴们&#xff1a;经过激烈的思想斗争之后&#xff0c;我决定了&#xff0c;决定什么呢&#xff1f;决定给大家送福利了&#xff01;对&#xff0c;是送福利了&#xff01;送什么福利呢&#xff1f;送教程&#xff0c;javaSE的教程免费送&#xff0c;主要还是由我来讲…