【前言】

  • 方法执行前,CLR 会检测方法内代码引用的所有类型。同时 CLR 会分配一个内部数据结构,用来管理对所有引用的类型的访问。
  • 首次执行方法时,托管程序集会把 IL 转换成本地 CPU 指令,并将其存储在一个动态分配的内存块中。 这是 CLR 的 JIT(just-in-time)编译器的功能。
  • 在应用程序终止前,如果再调用同一个方法,会直接执行存储在动态分配内存块里的 CPU 指令。

【图解】

1.当 “首次” 执行方法时,实际发生的事情如下图所示

就在 Main 方法执行之前,CLR 会检测出 Main 的代码引用的所有类型。这导致 CLR 分配一个内部数据结构,它用于管理对所有引用的类型的访问。如上图所示,Main 方法引用了一个 Console 类型,这导致 CLR 分配一个内部结构。在这个内部数据结构中,Console 类型定义的每个方法都有一个对应的记录项。每个记录项都容纳了一个地址,根据此地址即可找到方法的实现。对这个结构进行初始化时,CLR 将每个记录项都设置成(指向)包含在 CLR 内部的一个未文档化的函数。我们将这个函数称为 JITCompiler。

Main 方法首次调用 WriteLine 时,JITCompiler 函数会被调用。JITCompiler 函数负责将一个方法的 IL 代码编译成本地 CPU 指令。由于 IL 是“即时”(just in time)编译的,所以通常将 CLR 的这个组件称为 JITer 或者 JIT 编译器。

JITCompiler 函数被调用时,它知道要调用的是哪个方法,以及具体是什么类型定义了该方法。然后,JITCompiler 会在定义(该类型的)程序集的元数据中查找被调用的方法的 IL。接着,JITCompiler 验证 IL 代码,并将 IL 代码编译成本地 CPU 指令。本地 CPU 指令被保存到一个动态分配的内存块中。然后, JITCompiler 返回 CLR 为类型创建的内部数据结构,找到与被调用那个的方法对应的那一条记录,修改最初对 JITCompiler 的引用,让它现在指向内存块(其中包含了刚才编译好的本地 CPU 指令)的地址。最后,JITCompiler 的函数跳转到内存块中的代码。这些代码正是 WriteLine 方法(获取单个 String 参数的那个版本)的具体实现。这些代码执行完毕并返回时,会返回到 Main 中的代码,并像往常一样继续执行。

2.方法 “再次” 执行时,实际发生的事情如下图所示

第二次调用 WriteLine。这次由于已对 WriteLine 的代码进行了验证和编译,所以会直接执行内存块中的代码,完全跳过 JITCompiler 函数。 WriteLine 方法执行完毕之后,会再次返回 Main。

注意:JIT 编译器将本地 CPU 指令存储到动态内存中。一旦应用程序终止,编译好的代码也会被丢弃。所以,如果将来再次运行应用程序,或者同时启动应用程序的两个实例(使用两个不同的操作系统进程),JIT 编译器必须再次将 IL 编译成本地指令。

读经典——《CLR via C#》(Jeffrey Richter著) 笔记_方法执行的更多相关文章

  1. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_发布者策略控制

    在 读经典——<CLR via C#>(Jeffrey Richter著) 笔记_高级管理控制(配置)中,是由程序集的发布者将程序集的一个新版本发送给管理员,后者安装程序集,并手动编辑应用 ...

  2. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_通过ILDasm.exe查看编译器如何将类型及其成员编译成元数据

    [实例代码] using System; public sealed class SomeType //-------------1 { //嵌套类 private class SomeNestedT ...

  3. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_基元类型(二)

    [基元类型推荐] 推荐直接使用 FCL 类型. [理由] 编码时不至于困惑string与String的使用.由于C#的stirng(一个关键字)直接映射到System.String(一个 FCL 类型 ...

  4. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_基元类型(一)

    [概念] 编译器直接支持的数据类型 [C#基元类型与对应的 FCL 类型] C#基元类型 FCL 类型 说明 sbyte System.Sbyte 有符号8位值 byte System.Byte 无符 ...

  5. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_高级管理控制(配置)

    一个应用程序的XML配置文件示例: <?xml version="1.0"?> <configuration> <runtime> <as ...

  6. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_运行时解析类型引用

    public sealed class Program{ public static void Main() { System.Console.WriteLine("Hi"); } ...

  7. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_元数据

    1.元数据简介 全称:metadata 属性:数据表集合 产地:面向 CLR 的编译器在托管模块中生成 2.元数据内部结构及与托管模块的关系 [概述] 托管模块中包含着元数据,元数据是由一组数据表组成 ...

  8. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_友元程序集

    [应用场景] 程序集A访问程序集B定义的Internal访问类型的类的成员. [使用方式] 在构建程序集B的时候,引入System.Runtime.CompilerServices,以此来添加Inte ...

  9. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_类型的各种成员

    [Class中,可能包含的成员] 常量, 字段, 实例构造器, 类型构造器, 方法, 操作符重载, 转换操作符, 属性, 事件, 类型(Class)

随机推荐

  1. H264中的SPS、PPS提取与作用<转>

    牛逼的视频会议网站:http://wmnmtm.blog.163.com/blog/#m=0 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ...

  2. Elasticsearch之curl创建索引

    前提,是 Elasticsearch之curl创建索引库 [hadoop@djt002 elasticsearch-2.4.3]$ curl -XPUT 'http://192.168.80.200: ...

  3. python中注释的书写

    与c和c++的//不同的是,在python中我们使用#来进行注释 每个#所在的那一行都可以叫做注释:

  4. Spring2 看1

    Spring部分 1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本在程序中手动创建UserService对象的控 ...

  5. cakephp目录结构

  6. scala的map的操作

    1:map和模式匹配的结合 settings.foreach{case (k,v) => set(k,v)} 2:map转成array settings.entrySet().asScala.m ...

  7. c++中怎么实现Java中finally语句

    所有学习c++的书籍都明确提出了,不要使用goto, 以免造成程序流程的混乱,使理解和调试程序都产生困难. 但是我们遇到这样一个场景怎么办:就是不管程序执行成功与否,都要执行一些资源释放语句,相当ja ...

  8. C++11新标准:auto关键字

    一.auto意义 编程时常常需要把表达式的值赋给变量,这就要求在声明变量的时候清楚地知道表达式的类型,然后要做到这一点并非那么容易.为了解决这个问题,C++11新标准引入了auto类型说明符,用它就能 ...

  9. 用Pdg2.DLL解码PDG的境界

    作者:马健邮箱:stronghorse_mj@hotmail.com发布:2008.08.03 一.入门级原理:按照<用BCB实现超星格式转换为BMP格式>中说的方法调用Pdg2.DLL接 ...

  10. 从零开始搭建.NET Core 2.0 API(学习笔记一)

    从零开始搭建.NET Core 2.0 API(学习笔记一) 一. VS 2017 新建一个项目 选择ASP.NET Core Web应用程序,再选择Web API,选择ASP.NET Core 2. ...