.net 底层运行机制
1. CLR C#.NET 平台下,代码是怎么运行的
源代码——》托管模块——》程序集—JIT—》编程CPU指令
1.1 在.NET框架下,首先将源代码编译为托管模块
CLR编译过程:C#源码文件——(C#编译器编译)——》托管模块
托管模块是一个需要CLR环境才能执行的标准windows PE文件,包含IL和元数据以及PE表头和CLR表头。
iL:又叫托管代码,是编译器编译原文件后产生的指令,CLR会在运行时将IL编译成本地CPU指令。
元数据实际上是一个数据表集合,用来描述托管模块中所定义和引用的内容。VS能够智能感知就得益于元数据的描述
内容:
2.指出了托管模块引用的内容,比如导入的类及其成员
3.清单
元数据总是嵌入到与代码相同的
用途:
2.智能感知,从元数据中解析
3.代码验证,使用元数据确保代码只执行安全操作
4.正反序列化
5.垃圾收集器跟踪对象的生存期以及对象的类型
PE表头:标准Windows PE文件表头,包含文件类型(如GUI、CUI等),以及文件创建时间等信息,在32位还是64位上运行
CLR表头:包含标识托管模块的一些信息。如CLR版本号,托管模块入口点方法(main方法)以及MethodDef元数据等等
1.2托管模块组合为程序集
程序集:一个或多个托管模块
既可以生成单文件程序集,也可以生成多文件程序集,这由编译器工具决定。
CLR是和程序集一起工作的,而不是和托管模块
一般编译器会默认将生成的托管模块生成一个程序集,CLR直接打交道的是程序集(assembly),程序集包含一个或多个托管模块,以及资源文件的逻辑组合。组合过程如下:
左侧为一些托管模块,在经过一些工具的处理后,生成了一个
1.2 加载 CLR
csc的
程序要运行,首先确定及其安装了.NET框架.然后,加载并初始化CLR
1.4 程序集执行
(
IL代码要通过及时编译器JITter转化成CPU指令
方法第一次调用:
(
在这个内部结构中,每个方法都有一条对应的纪录以及地址
JITCompiler函数被调用时,找到相应方法的
)
1. 当程序第一次运行时,会调用JITCompiler函数,它可以知道调用了那些方法,以及定义该方法的类。
2. 然后JITCompiler函数在元数据中搜索该IL代码的位置,验证后转换成本地CPU指令。将指令保存在动态分配的内存中
3. JITCompiler将被调用方法地址改为第2步的内存地址
4. 跳转到上述代码块上执行代码
5. 执行完成后返回
IL是基于堆栈的语言,而且是无类型的。
再次调用方法
在一个程序中,我们经常反复调用同一个方法,当再次调用该方法时就不需要重复进行验证了,可以直接调用内存块中已有的本地代码,完全跳过JITCompile函数的验证和编译过程。所以同一方法只有在第一次调用时会产生一些性能损失,后续调用就可以全速进行了。
关闭程序
由于编译器将本地代码保存在动态内存中,所以关闭程序时本地代码将发生丢失。当再次启动程序或者同时运行程序的两个实例时,JIT编译器将再次将IL代码编译为本地指令。
谈谈IL安全问题
IL是基于堆栈的。所有指令都是:将操作数压栈,结果则从栈中弹出
IL有安全验证机制,保证每一行
不安全代码:允许
PEVerify命令检查程序集所有方法,指出其中的不安全代码方法
1.5 本地代码生成器NGEN.exe
缺点是:
不能保护
生成的文件可能失去同步
因为在文件中要计算首选基地址,而
较差的执行性能,
如果不能使用
2. 认识CLR
基本术语:
托管模块:
IL:
元数据:
程序集:
JIT:
FCL:
CTS:
CLI:
CLS:
跨语言的方法CTS
CTS的一些规定:
1.一个类型可以包含
2.类型可视化以及类型成员的访问规则
3.定义了继承,虚方法,对象生成期的管理规则
4.所有类型最终都从预定义的
例如:
那么,就不能在
(这儿说的真实纠结)
举例:
[assembly: CLSCompliant(
}
}
}
解决办法:
(1)如果去掉
(2)如果将
接下来,我们在VB中,使用这个dll
如果我们使用方法1,去掉
结果是:在
如果我们是使用方法
.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
它是一种类似于
Eg: CIL语言:
利用ILDASMI工具界面如下:
点击默认构造函数
{
IL_0000: ldarg.
IL_0001: call instance
IL_0006: ret
}
.method private hidebysig static void 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指令)
根:
每一个应用程序都有一组根Root。一个根是一个存储地址,包含一个指向类型对象的指针。
该指针有2种形式:(1)指向托管堆中的一个对象。(2)设为null。
根包括静态字段,方法参数,局部变量,CPU寄存器。
对象的代
托管堆中,对象的代大概为
终结操作和释放模式
终结操作(
释放模式(Dispose()方法):当对象不再被使用的时候显示的释放掉它所占有的资源
垃圾回收算法
垃圾收集算法的任务就是将活动的对象和已经死掉的对象分别处理啊,然后将死掉的对象的内存回收。
(1) 引用计数(Reference Counting)
引用计数,顾名思义,就是每个对象上有个计数器,当添加了一个对它的引用时它的计数器就会加
(2)跟踪 (trace)
使用跟踪对象的关系图,然后进行收集。使用跟踪方式的垃圾收集算法主要有以下几种:
1)
2)
3)
跟踪方式的其他问题
原文:https://www.cnblogs.com/Jennie/archive/2011/08/24/2152065.html
.net 底层运行机制
原文:https://www.cnblogs.com/cdaniu/p/15265555.html