• .NET程序的运行与内存管理


    为什么CLR

    在我看来,CLR诞生时,定位就是JVM的竞争者,回顾历史,当MS被Sun要求不能给Windows上的Java实现塞私货的时候,MS必须有一个东西能够与JVM抗衡。因此,CLR诞生的最重要的历史原因是--“市场竞争”。所以,从技术的角度来看,我们探究JVM的诞生,似乎能更好地理解CLR – 与JVM极度类似的平台出现的原因。

    那么为什么会诞生JVM呢?

    JVM是随着Java语言一起诞生的,当年Sun推广Java的口号就是“Write once, run everywhere”,也即所谓的“跨平台”。为了实现跨平台,Java被编译为bytecode,而不是native code,直到运行时,才通过JIT-Compiler编译为本地代码。也就是说,Java通过给操作系统加了一层JVM,由JVM来解释(JIT编译)首次编译产生的bytecode,从而实现了“跨平台”。(加层是解决问题的万能钥匙)。而由于有了JVM,就是可以实现一些更多的东西,比如说,代码的运行时检查,垃圾回收,多语言支持等等,都是实现了最主要目的时附送的意外之喜。

    回到CLR,MS既然不能改Java,那就搞个自己能随便改的东西吧,新的语言C#诞生。为了跟Java区分,MS把运行时称为 CLR,把编译出来的东西称为 IL,当然,MS不会强调它“跨平台”--尽管事实上完全可以-- 就在市场策略上强调CLR的多语言支持。

    扯淡完毕。

    JIT编译过程

    CLR需要在运行时将IL转换为本地机器代码(JIT 编译),那CLR怎么能知道IL中的哪个部分应该转换为什么呢?我们的代码里有:属性,方法,类,字段等等,这些都需要正确的转换才能正确运行。

    通常,要实现两个东西之间的转换,可以通过两种方式让来告诉转换器怎么转:

    1) 配置

    2) 约定

    配置和约定有什么不同?

    配置:相当于给转换器发了一份手册,手册中写着什么东西应该转换另一个什么东西,怎么转。

    举个栗子:

    如果你玩过积木,一套积木通常会附带一份说明书,说明书上会写一些积木的堆积方式和完成效果。这里的说明书就是所谓的“配置”,写明了多少堆积方式,就是多少不同的“配置”,你按照这个配置,就可以完成从积木到“宫殿”,“城堡”等的转换。

    那什么是约定?

    约定:相当于咱说好一个转换规则,每次转换就按照这个转换规则转。

    举个栗子:

    “你我约定 一争吵很快要喊停
    也说好没有秘密彼此很透明”

    这里的约定就是: 状态A(争吵) –> 转换规则(喊停) -> 状态B(友好)

    配置更灵活;约定更简单

    回归主题,CLR采取了“配置”的方式告诉转换器IL的几乎全部信息,比如说这个文件里有哪些类型,每个类型有哪些方法、属性、字段,这些信息都在文件的什么地方存放着,是否已经被转换为本地机器代码了等等,我们把这样的“配置”信息成为“元数据”。而这些“配置”信息就在首次编译的时候与编译出来的IL一起放在了编译后生成的文件里,这个文件的格式与原生代码(比如C/C++)编译出来的文件格式是相同的,我们把具有这种格式的文件称为“PE(Portable Executable)文件”,这样,即使没有安装CLR的操作系统,也可以识别这样的文件。

    ----------插播:PE文件格式-----------

    下面是PE文件的格式示意图

    clip_image001

    我们从上往下看 ,第一部分和第二部分都是关于DOS的,都是为了兼容DOS而背上的沉重的历史包袱。就像我们党的前核心一样,虽然已经不需要了,还总是要坐在主席台的最前排。还好这个包袱不算太大。紧接着就是PE文件头。PE加载器就是读取这部分来决定如何将文件映射到虚拟内存的。

    在这里我们不准备深入每个细节,我们只挑选我们最感兴趣的部分:PE是如何执行起来的。PE文件头里有一项叫: AddressOfEntryPoint,从名字我们就能猜到它的作用,它标识了应用程序的入口点。当PE加载器加载我们的应用程序后,就将这个入口点地址告诉EIP。联想到我们第一部分说到的旅行团,这个地址就是领队的房间号码。

    接下来是一些section, 这些section中有一个名字是.text,这个section通常存放的是程序的指令,不过在.NET程序中,它还存放IL和元数据。其他的section也各有用处,此处从略。

    -----------插播结束------------

    对于.NET 生成的PE文件而言,AddressOfEntryPoint和原生程序是不一样的,它指向了一个函数名为:_CorExeMain,这个函数是mscoree.dll中的一个函数 。换句话说,当一个.NET程序启动的时候,第一件事情就是调用mscoree.dll中的_CorExeMain函数,这个函数接管了接下来的运行过程。这个函数做的事情很简单,它根据我们的.NET程序中的信息和本机安装的.NET Framework版本,选择一个合适文件来执行,比如说在Work station上安装的CLR对应的文件是mscorwks.dll而在服务器上安装的CLR对应的文件时mscorsvr.dll;所以mscoree.dll也被称为shim。

    以工作站版本的CLR为例,剩下的事情就是mscorwks.dll将在PE文件中找到元数据和IL,将其编译为本地机器代码,然后执行。

  • 相关阅读:
    EF5+MVC4系列(5) 删除的方法 1:系统推荐的先查询后remove删除的方法 2:自己new一个包含主键的类,然后 attach附加 remove删除;3:使用db.Entry 修改状态删除4:EntityState的几种状态
    指定webapi 返回 json 格式 ; GlobalConfiguration.Configuration.Formatters.Clear()
    远程桌面连接工具 Remote Desktop Manager 9.1.2.0 Enterprise 多国语言绿色版附注册码 简单使用
    .NET WebAPI 正确抛出错误详细信息
    观察者模式 发布订阅者模式
    7月目标 socket , 一致性哈希算法 ; mongodb分片; 分布式消息队列; 中间件的使用场景
    在js中 把 json对象转化为String对象的方法
    mvc4中的 webapi 的使用方式
    Uncaught TypeError: TableInit is not a constructor
    Jquery复选框操作
  • 原文地址:https://www.cnblogs.com/lbsong/p/3134905.html
Copyright © 2020-2023  润新知