1.           CLR C#.NET 平台下,代码是怎么运行的

源代码——》托管模块——》程序集—JIT—》编程CPU指令

1.1     在.NET框架下,首先将源代码编译为托管模块

CLR编译过程:C#源码文件——(C#编译器编译)——》托管模块

托管模块是一个需要CLR环境才能执行的标准windows PE文件,包含IL和元数据以及PE表头和CLR表头。

iL:又叫托管代码,是编译器编译原文件后产生的指令,CLR会在运行时将IL编译成本地CPU指令。

元数据实际上是一个数据表集合,用来描述托管模块中所定义和引用的内容。VS能够智能感知就得益于元数据的描述

内容: 1.描述了模块中定义的内容,比如类及其成员
             2.指出了托管模块引用的内容,比如导入的类及其成员
             3.清单manifest,描述了构成Assembly的文件,由Assembly中的文件实现的公共导出类型,与Assembly相关联的资源/数据文件
元数据总是嵌入到与代码相同的EXE/DLL中,始终与IL保持同步

用途:

1.消除了对头/库文件的依赖,直接从托管模块中读取
              2.智能感知,从元数据中解析
              3.代码验证,使用元数据确保代码只执行安全操作
             4.正反序列化
             5.垃圾收集器跟踪对象的生存期以及对象的类型

PE表头:标准Windows PE文件表头,包含文件类型(如GUI、CUI等),以及文件创建时间等信息,在32位还是64位上运行

CLR表头:包含标识托管模块的一些信息。如CLR版本号,托管模块入口点方法(main方法)以及MethodDef元数据等等

1.2托管模块组合为程序集

程序集:一个或多个托管模块/资源文件的逻辑分组,是最小的重用,安全性以及版本控制单元。
        既可以生成单文件程序集,也可以生成多文件程序集,这由编译器工具决定。
        CLR是和程序集一起工作的,而不是和托管模块

一般编译器会默认将生成的托管模块生成一个程序集,CLR直接打交道的是程序集(assembly),程序集包含一个或多个托管模块,以及资源文件的逻辑组合。组合过程如下:

左侧为一些托管模块,在经过一些工具的处理后,生成了一个PE文件,也就是程序集。程序集中有一个清单(manifest)数据块,用来描述组成程序集的所有文件。此外,程序集还包含它所引用的程序集的信息,这就使得程序集可以实现自描述。这样CLR就直接知道了程序集所需要的所有内容,因此程序集的部署比非托管组件要容易。

1.2     加载 CLR

CLRVer命令,查看机器上所有CLR版本
    csc的 /plattform开关,决定生成什么样的程序集:AnyCPU,x86,x64,Itanium

程序要运行,首先确定及其安装了.NET框架.然后,加载并初始化CLR

1.4 程序集执行

(ILAsm命令,将IL编译成Assembly;ILDasm将Assembly编译成IL)

IL代码要通过及时编译器JITter转化成CPU指令

方法第一次调用:

(     在方法首次执行时,CLR检测出Main的代码引用的所有类型,于是CLR分配一个内部数据结构,用于管理对引用类型的访问

在这个内部结构中,每个方法都有一条对应的纪录以及地址

对此结构进行初始化时,CLR将每条纪录都设置为CLR内部包含的一个未文档化的函数,即JITCompiler函数。
        JITCompiler函数被调用时,找到相应方法的IL,编译成本地CPU指令,并保存到一个动态内存块中,将该内存地址存入内部结构中,最后JITCompiler函数会跳转到内存块中的代码

)

1. 当程序第一次运行时,会调用JITCompiler函数,它可以知道调用了那些方法,以及定义该方法的类。

2. 然后JITCompiler函数在元数据中搜索该IL代码的位置,验证后转换成本地CPU指令。将指令保存在动态分配的内存中

3. JITCompiler将被调用方法地址改为第2步的内存地址

4. 跳转到上述代码块上执行代码

5. 执行完成后返回

IL是基于堆栈的语言,而且是无类型的。

再次调用方法

在一个程序中,我们经常反复调用同一个方法,当再次调用该方法时就不需要重复进行验证了,可以直接调用内存块中已有的本地代码,完全跳过JITCompile函数的验证和编译过程。所以同一方法只有在第一次调用时会产生一些性能损失,后续调用就可以全速进行了。

关闭程序

由于编译器将本地代码保存在动态内存中,所以关闭程序时本地代码将发生丢失。当再次启动程序或者同时运行程序的两个实例时,JIT编译器将再次将IL代码编译为本地指令。

谈谈IL安全问题

IL是基于堆栈的。所有指令都是:将操作数压栈,结果则从栈中弹出
IL有安全验证机制,保证每一行IL代码是正确的,不会非法访问内存,每个托管EXE都在独自的AppDomain中运行。

不安全代码:允许C#直接操作内存字节,在COM互操作时使用,csc以/unsafe开关标记包含不安全代码,其中所有方法都使用unsafe关键字。
PEVerify命令检查程序集所有方法,指出其中的不安全代码方法

1.5 本地代码生成器NGEN.exe

NGEN.exe将IL预先编译到硬盘文件中,可以加快程序的启动速度,减小程序的工作集(所有加载该程序集的AppDomain不再copy其副本,因为该程序集已经与编译到文件中,是代码共享的)。
缺点是:
    不能保护IL外泄
    生成的文件可能失去同步
    因为在文件中要计算首选基地址,而NGEN是静态计算好的,所以要修改基地址,速度会慢下来
    较差的执行性能,NGEN生成的代码没有JIT好。
如果不能使用NGEN生成的文件,会自动加载JITCompiler。

2.           认识CLR

基本术语:

CLR :Common Language Runtime 公共语言运行期,有多种不同编程语言使用的运行库
                托管模块:Managed Module,一个标准的MS Window可移植执行体文件(32位PE32或64位PE32+)
                IL:Intermediate Language 中间语言,又叫托管代码(由CLR管理它的执行)
                元数据:metadata,一系列特殊的数据表
                程序集:Assembly,抽象的
                JIT:just-in-time 即时编译,将IL编译成本地CPU指令(本地代码)
                FCL:Framework Class Library,Framework 类库
                CTS:Common Type System,通用类型系统,描述了类型的定义及其行为方式
                CLI:Common Language Infrastructure,公共语言基础结构,这是MS提交给ECMA的一个标准,由CTS和其他Framwork组件构成   (CTS、CLS、 CR)
                CLS:Common Language Specfication,公共语言规范,详细规定了一个最小特性集

跨语言的方法CTS

CTS
    CTS的一些规定:
        1.一个类型可以包含0个或多个成员
        2.类型可视化以及类型成员的访问规则
        3.定义了继承,虚方法,对象生成期的管理规则
        4.所有类型最终都从预定义的System.Object继承

例如:  如果在C#中定义的类型及其方法,同样可以在VB中使用。

那么,就不能在C#中定义CLS外的任何public/protected特性,privated的类型及其成员不受限制

C#可以有仅大小写不同的两个方法——不符合CLS,所以不能是public的

(这儿说的真实纠结)

举例:

using System;

[assembly: CLSCompliant(true)]   // 使用[assembly:CLSComplant(true)]标志程序集,告诉编译器检查该程序集的CLS相容性

namespace ClassLibrary2
{
    public class Class1
    {

        public void A()
        {

        }

        public void a()
        {

        }
    }

定义了两个不同方法A和a,编译器会有警告,说这样的语法不兼容CLS;

解决办法:

(1)如果去掉[assembly:CLSComplant(true)]声明,那么不会有这个警告;

(2)如果将a方法改为private,则不会有警告

接下来,我们在VB中,使用这个dll

Imports ClassLibrary2

Module Module1Module Module1

    Public Class TClass T
        Public Function A()Function A() As Integer

            Dim c1 As Class1 = New Class1()

        End Function
    End Class

End Module

如果我们使用方法1,去掉[assembly:CLSComplant(true)]声明

结果是:在c1.后面不会有A或a方法的智能感知,说明VB不能识别不符合CLS的语法

如果我们是使用方法2,解决问题

可以在VB钟只能感知A方法

.NET 是微软研发出来的扩语言解决方案。他的核心是CLR,这是微软发布的CLI规范的一个实现。

CLI 包括:CIL(公共中间语言)、CTS (公共类型系统)

基于CTS公共类型系统,.net就可以把其下的各种语言中的数据类型翻译为公共数据类型,再将其翻译为公共中间语言,就可以实现跨语言的互通。本来程序就基本等于数据+流程逻辑,两部分都使用了公共规范进行约束后,实现互通性就有可能了。

CLI(Common Language Infrastructure)

CLI是CLR的一个子集,也就是.NET中最终对编译成MSIL代码的应用程序的运行环境进行管理的那一部分。

在CLR结构图中CLI位于下半部分,主要包括类加载器(Class Loader)
、实时编译器(IL To Native Compilers)和一个运行时环境的垃圾收集器(Garbage Col
lector)。

CLI是.Net和CLR的灵魂,CLI为IL代码提供运行的环境,你可以将使用任何语言编写的代码通过其特定的编译器转换为MSIL代码之后运行其上,甚至还可以自己写MSIL代码在CLI上面运行。

CIL(Common Intermediate Language)

通用中间语言,曾经被称为微软中间语言或MSIL

它是一种类似于JAVA字节码的语言。在微软语言平台中,不管程序员使用C#、VB.NET或者J#等语言编写的程序进行编译的时候,编译器将这几种语言编写的源代码编译为CIL(微软中间语言)语言,此时再通过JIL(Just In Time实时编译器)编译为针对各种不同CPU的指令(注意因为是实时的编译器,所以它运行的时候是只运行需要编译的CIL语言段

Eg: CIL语言:

利用ILDASMI工具界面如下:

点击默认构造函数.ctor:void()我们可以看到这个构造函数的CIL语言如下

.method public hidebysig specialname rtspecialname
            //.method表示对方法
        instance void  .ctor() cil managed
        {
          // 代码大小       7 (0x7)
          .maxstack  8
          IL_0000:  ldarg.0
          IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
          IL_0006:  ret
        } // end of method Program::.ctor

.method private hidebysig staticvoid  Main(string[] args) cil managed

{

  .entrypoint           //程序进入点

  // 代码大小       15 (0xf)

  .maxstack  1          //堆栈分配

  .locals init ([0] string a)              

  IL_0000:  nop                            

  IL_0001:  ldstr      "Hello World!"//压入字符串,堆栈压操作

  IL_0006:  stloc.0     //从计算堆栈的顶部弹出当前值并将其存储到索引 0 处的局部变量列表中。           

  IL_0007:  ldloc.0     //将索引 0 处的局部变量加载到计算堆栈上。

  IL_0008:  call       void [mscorlib]System.Console::WriteLine(string)

                        //呼叫WriteLine函数打印Hello World

  IL_000d:  nop

  IL_000e:  ret                             //返回

} // end of method Program::Main

3 CLR垃圾收集

基本概念

对象的生成过程(newobj指令)

1:计算类型(包括基类)所有字段的字节总数

2: 字节总数再加上对象开销字段字节数(相加为:对象所需的字节数)。每个对象包含2个开销字段:类型对象指针以及同步块索引。WIN32中,各占32位,WIN64中,各占64位。

3:CLR检测托管堆中是否有足够的空间满足对象所需的字节数。如果满足,对象将被分配在NextObjPtr指针指示的地方,实例构造器被调用,(new操作)返回对象的内存地址。指针NextObjPtr越过对象所在的区域,指示下一个新建对象在托管堆中的地址。如果不满足,进行垃圾收

根:

每一个应用程序都有一组根Root。一个根是一个存储地址,包含一个指向类型对象的指针。

该指针有2种形式:(1)指向托管堆中的一个对象。(2)设为null。

根包括静态字段,方法参数,局部变量,CPU寄存器。

对象的代

托管堆中,对象的代大概为0代,1代,2代,相应的内存容量为256K,2M,10M。当然,垃圾收集器也会自动调整预算容量

终结操作和释放模式

终结操作(Finalize()方法)可以确保托管对象在释放内存的同时不会泄露本地资源,但是不能确定它在何时被调用。

释放模式(Dispose()方法):当对象不再被使用的时候显示的释放掉它所占有的资源

垃圾回收算法

垃圾收集算法的任务就是将活动的对象和已经死掉的对象分别处理啊,然后将死掉的对象的内存回收。

(1) 引用计数(Reference Counting)

引用计数,顾名思义,就是每个对象上有个计数器,当添加了一个对它的引用时它的计数器就会加1,当不再使用这个引用时它的计数器就会递减1。当计数器为0的时候则认为该对象是垃圾,可以被回收了,如果该对象被回收则该对象所引用的所有对象的计数器会跟着递减。这样有可能会有很多对象的引用都减为0。

(2)跟踪 (trace)

使用跟踪对象的关系图,然后进行收集。使用跟踪方式的垃圾收集算法主要有以下几种:

1)        标记清扫(Mark -Sweep)

2)        标记压缩(mark -Compact)

3)        标记拷贝(Mark-Copy)

跟踪方式的其他问题

1.需要暂停当前程序的运行。因为在垃圾收集过程中,如果当前程序还在运行,则会继续分配和使用内存,会带来更复杂的问题,为了避免这些问题大部分垃圾收集器的实现都会或多或少的暂停所有线程(只会暂停执行托管代码的线程)。这对于实时性很高的应用有可能是不可接受的。

2、有些垃圾收集器在某些阶段,垃圾收集的线程可以与应用程序的线程并发执行,但是垃圾收集线程也会占用系统资源,会降低应用程序的执行性能。

3、所有的线程都在同一个堆上分配,就有可能造成数据不一致的情况,这就需要锁定来做到线程的同步,这样会降低内存分配的效率,可以将内存划分为很多区域,给每个线程一个区域,做到不需要同步的情况。

原文:https://www.cnblogs.com/Jennie/archive/2011/08/24/2152065.html

.net 底层运行机制的更多相关文章

  1. <转>ASP.NET学习笔记之理解MVC底层运行机制

    ASP.NET MVC架构与实战系列之一:理解MVC底层运行机制 今天,我将开启一个崭新的话题:ASP.NET MVC框架的探讨.首先,我们回顾一下ASP.NET Web Form技术与ASP.NET ...

  2. .net core系列之《从源码对Configuration的底层运行机制进行分析》

    通过对Configuration源代码的分析从而来自定义一个配置数据源 1.用反编译工具来看看AddJsonFile()这个方法究竟干了什么,源代码如下: public static IConfigu ...

  3. 【Spark 深入学习 04】再说Spark底层运行机制

    本节内容 · spark底层执行机制 · 细说RDD构建过程 · Job Stage的划分算法 · Task最佳计算位置算法 一.spark底层执行机制 对于Spark底层的运行原理,找到了一副很好的 ...

  4. PHP底层运行机制与原理

    PHP的设计理念及特点 多进程模型:由于PHP是多进程模型,不同请求间互不干涉,这样保证了一个请求挂掉不会对全盘服务造成影响,当然,时代发展,PHP也早已支持多线程模型. 弱类型语言:和C/C++.J ...

  5. 从GC的SuppressFinalize方法带你深刻认识Finalize底层运行机制

    如果你经常看开源项目的源码,你会发现很多Dispose方法中都有这么一句代码: GC.SuppressFinalize(this); ,看过一两次可能无所谓,看多了就来了兴趣,这篇就跟大家聊一聊. 一 ...

  6. JavaScript闭包的底层运行机制

    转自:http://blog.leapoahead.com/2015/09/15/js-closure/ 我研究JavaScript闭包(closure)已经有一段时间了.我之前只是学会了如何使用它们 ...

  7. PHP的运行机制与原理(底层) [转]

    说到php的运行机制还要先给大家介绍php的模块,PHP总共有三个模块:内核.Zend引擎.以及扩展层:PHP内核用来处理请求.文件流.错误处理等相关操作:Zend引擎(ZE)用以将源文件转换成机器语 ...

  8. PHP的运行机制与原理(底层)

    原文:http://www.jb51.net/article/74907.htm 说到php的运行机制还要先给大家介绍php的模块,PHP总共有三个模块:内核.Zend引擎.以及扩展层:PHP内核用来 ...

  9. 微信小程序底层原理与运行机制类文章学习

    参考文档 小程序底层实现原理及一些思考 为了安全和管控, 双线程执行 Web Worker执行用户的代码; UI线程执行大部分的功能. 微信小程序架构原理 只通过mvvm模板语法动态改变页面, 不支持 ...

随机推荐

  1. 数组的sort()排序

    1.sort() 方法用于对数组的元素进行排序,并返回数组.默认排序顺序是根据字符串Unicode码点,也就是你不传参进去的话,默认按字符串Unicode码点来排序,而不是按数字大小来排序 2.arr ...

  2. 集合框架-Map集合练习-记录字母次数思路及代码

    1 package cn.itcast.p10.map.test; 2 3 import java.util.Iterator; 4 import java.util.Map; 5 import ja ...

  3. 多线程-创建线程第二种方式-实现Runnable接口-细节和好处

    1 package multithread2; 2 3 /* 4 * 创建线程的第一种方法:继承Thread类 5 * 6 * 创建线程的第二种方式:实现Runnable接口 7 * 8 * 1,定义 ...

  4. 【Vue源码学习】响应式原理探秘

    最近准备开启Vue的源码学习,并且每一个Vue的重要知识点都会记录下来.我们知道Vue的核心理念是数据驱动视图,所有操作都只需要在数据层做处理,不必关心视图层的操作.这里先来学习Vue的响应式原理,V ...

  5. Python的开发环境

    Python的开发环境 在 Python 开发环境中我们能看到很多工具,如 pip .conda .poetry . virtualenv . venv . pyenv 等等.他们是什么,都有什么作用 ...

  6. Core 3.1 MVC 抛异常“InvalidOperationException: No service for type 'Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionaryFactory' has been registered.”

    .NET Core 的版本是 3.1遇到的问题是 Action 中 return View() 的时候报错 An unhandled exception occurred while processi ...

  7. 人工智能与智能系统1->机器人学1 | 位置与姿态描述

    寒假有几项学习计划,其中有一些是为了一些任务而学,最主要的任务是我要在2021_v4的基础上编写2022_v1的大援代码,为此顺便学习一下机器人学的知识(下学期也有这方面的老黄的课程),看看能不能在结 ...

  8. Java协变、逆变、类型擦除

    协变.逆变 定义 Java中String类型是继承自Object的,姑且记做String ≦ Object,表示String是Object的子类型,String的对象可以赋给Object的对象.而Ob ...

  9. C语言之清空缓存区

    感谢大佬:https://blog.csdn.net/qq_26768741/article/details/50933598 在C语言中,我们常常需要去清空缓存区,对于缓存区清空的重要性,接下来我们 ...

  10. JavaScript与java语法区别

    网页中各种技术的作用 感谢大佬:https://blog.csdn.net/RookiexiaoMu_a/article/details/89052768 HTML 制作网页的结构 CSS 美化网页 ...