• CLR学习总结之C#程序的编译与执行


    1.代码的编译

      CLR(Common Language Runtime),公共语言运行时,顾名思义,就是可以由多种语言使用的运行时,当我们用vs新建一个源码文件,写好代码,然后编译的时候,实际会生成一个托管模块,这个托管模块是一个标准的windows PE32文件,或者是一个标准的windows PE32+文件,该模块的组成部分为:

    组成部分

    说明

    PE32 或PE32+头

    标准windows PE文件头;如果这个头使用PE32格式,文件能在windows的32位或者64位版本上运行,如果这个头使用的PE32+格式,文件只能在windows 64位版本上运行

    CLR头

    包含使这个模块成为一个托管模块的信息

    元数据

    每个托管模块都包含元数据表,主要有两种类型的表:一种是描述源代码中定义的类型和成员,另一种是藐视源代码引用的类型和成员

    IL代码

    编译器编译源代码生成的代码,在运行时,CLR会将IL代码编译成本地CPU指令

    默认情况下,编译器会将托管模块转换为程序集。

    2.代码的执行

         托管程序集中同时包含元数据和IL,IL是一种与CPU无关的机器语言,当我们的程序首次执行时,会把要执行的IL代码编译成本地CPU指令;这是CLR的JIT编译器的职责。

    举个例子,如下代码第一次执行:

    demo

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

      Main方法第一次执行并调用Console.WriteLine方法时,JITCompiler函数被调用时,它知道要调用的是哪个方法,然后就会找到该方法的IL代码,验证并将其转换为本地CPU指令,指令会被存到一个动态分配的内存块中,然后JITCompiler返回CLR为类型创建的内部数据结构,找到与被调用方法对应的记录项,修改对JITCompiler的引用为刚转换的CPU指令内存块的地址;最后JITCompiler会跳转到内存块中的代码,执行完毕后就返回并继续执行Main中下面的代码。

      当Main中第二次调用Console.WriteLine相同的方法时,将会直接执行内存块中的代码,完全跳过JITCompiler。

      所以,一个方法只有在首次调用的时候才会造成一些性能损失,以后的调用都是以本地代码的形式执行,无需验证和编译。

      JIT编译器将本地CPU指令存储到动态内存中,所以一旦程序终止编译好的代码也将被丢弃,再次运行程序或同时启动两个应用程序,JIT编译器将再次将IL编译成本地CPU指令。

    3.IL的验证

       将IL编译成本地CPU指令时,CLR会执行一个名为验证的过程,这个过程会检测高级IL代码,确保代码所做的一切都是安全的,这也确保我们的应用程序的健壮性和安全性。

    令:有一点我没有弄清楚的是,当一个类型的一个方法第一次被调用时会被编译成CPU指令,并记录内存地址,那么如果一个方法第一次同时有两个地方调用,比如两个线程,那么会不会被编译两次呢?

    鄙人走在求知的道路中,写博客过程中如有不对或者理解错误之处希望大家不吝赐教,吾感激不尽。

     
    分类: CLR
    标签: CLR
  • 相关阅读:
    SQL SERVER 存储过程或触发器等优化SET NOCOUNT ON
    C#以16进制接收串口数据
    DevExpress中的RichEditControl(富文本)控件,如何把滚动条移动至最后!
    DevExpress中XtraReport的XRRichText在打印时,打印不出内容问题
    DevExpress控件GridControl如何在页脚进行汇总
    创建虚拟机SQL server
    接口,抽象类
    Linq 优化
    sql 优化
    C#:Hashtable和Dictionary
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2550212.html
Copyright © 2020-2023  润新知