.NET基础 (02).NET运行机制
.NET运行机制
1 .NET程序被编译成什么形式的代码
2 JIT是如何工作的
3 简述程序集的加载机制
4 如何配置程序集的版本策略
1 .NET程序被编译成什么形式的代码
.NET程序在编写完成后,会经过第一次编译。对于C#而言,无论是VS IDE还是其他任何间接方式,本质上都是执行编译器cse.exe来编译C#代码。在这次编译之后,程序会被编译成中间代码(IL),并且所有必须的元数据和程序集会被一起打包加载到文件头上。编译后的文件是一个标准的PE/COFF应用文件,该文件的最开始的部分包含了PE/COFF头。紧接着,就是.NET特有的头部信息,其中包括程序集版本号、文件名和模块版本号,也包含一些诸如强签名等可选项,这部分内容通常称为CLR头。CLR头之后就是文件的元数据,元数据包含了所有类型的定义、所有的引用以及程序集的清单。再之后,就是中间代码IL。
用.NET自带的ILDasm工具可以查看元数据和IL代码。在VS的命令窗口输入:
ILDasm xxx.exe
中间代码的形式上有点类似于汇编语言,但它是无法被直接运行的。在CLR运行程序集的时候,根据需要会对IL代码进行二次编译,这个过程称为即时编译(JIT,Just in time)。即时编译概念上更加符合传统意义上的编译,CLR会查看元数据来判断要加载那些中间代码,并且实时地对需要的程序集的中间代码进行编译,而这次编译的结果就是最终在CLR上可以执行的机器码。
即时编译是最常见的执行方法,但并不是唯一一种。.NET 提供了另外一种部署方法,即在部署的时候就立即编译中间代码来生成机器码,并存储在缓存中,当CLR需要加载该程序集时,就可直接加载已经编译好的机器码。但无论如何,第二次编译的本质是不变的。
.NET程序在第一次编译后,形成CLR头、元数据和中间代码。在实施运行和部署时,将经过二次编译,编译的结果是在CLR中可执行的机器代码。
2 JIT是如何工作的
在编译IL代码时,.NET提供了两种可选方式:
- 利用JIT引擎进行实时编译
- 在组件部署时就生成机器代码形式的缓存,以供CLR调用。
CLR的加载顺序,当CLR需要执行某个方法时,本地缓存,并且确定本地缓存是最新的版本。如果缓存的版本不是最新的,CLR将忽略缓存的机器代码而去寻找最新版本的中间代码。另外,如果CLR没有发现任何缓存代码,也会去寻找适合的中间代码并且用实时编译(JIT)的方式来编译中间代码。
实时编译不仅需要编译被调用的方法,并且需要编译或在本地缓存中寻找所有被该方法使用的类型。
在实时编译过程中,JIT引擎会查找一个包含该类型所有方法存根的数据结构,对于未编译成机器代码的方法,存根会包含一个调用JIT的简单命令,当该方法的实时编译结束后,存根的命令会被替换成一条简单的Jmp指令,使得代码跳转到该方法的机器代码位置。
在实际环境下,不同类型的方法JIT的原理机制是有所不同的,例如虚方法、委托方法、抽象方法等。
JIT引擎在编译中间代码之前,会寻找方法的本机机器代码缓存并且判断其是否可用,如果可用则直接加载,如果不可用,JIT引擎会查找类型中的方法存根,找到该中间代码并且进行编译。
3 简述程序集的加载机制
1.程序集的主动加载方式
程序集的加载可以完全忽略任何策略,而由程序员在程序中显式地通过位置进行加载。System.Reflection.Assembly.LoadFrom(string assemblyfile)就提供了这样的功能。这个方法接受一个CODEBASE风格的字符串,用以指定要加载的程序集的位置。
CODEBASE是.NET中指定路径的一种方式,其值记录了模块所在的位置,可以是一个URL,也可以通过file://来指定到本地位置。
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection; namespace NET.MST.Second
{
class LoadFrom
{
static void Main(string[] args)
{
//需要针对不同的主机目录结构进行修改
String codebase = @"..\..\..\..\2-4 Compile\Compile\Compile.dll"; //从指定位置加载程序集
Assembly compiledll = Assembly.LoadFrom(codebase); //创建对象
Object compile = compiledll.CreateInstance("NET.MST.Second.Compile"); Console.WriteLine(compile);
Console.Read();
}
}
}
在代码中,程序主动加载了位于Compile程序集目录下的Compile.dll。然后通过CreateInstance方法创建了一个Compile的类型。注意这里调用的是无参数的构造方法。
通过位置加载程序集确实非常方便,但它却带有以下一些弊端:
- 如果已加载一个具有相同标识的程序集,则即使指定了不同的路径,LoadFrom仍返回已加载的程序集。
- 如果用LoadFrom加载一个程序集,随后加载上下文中的一个程序集尝试加载具有相同显示名称的程序集,则加载尝试将失败。对程序集进行反序列化时,可能发生这种情况。
- 如果用LoadFrom加载一个程序集,并且探测路径包括具有相同标识但位置不同的程序集,则发生InvalidCastException、MissingMethodException或其他意外行为。
- LoadFrom要求指定路径中包含FileIOPermissionAccess.Read和 FileIOPermissionAccess. PathDiscovery或WebPermission。
- 如果assemblyFile存在本机图像,则不使用它。程序集不能加载为非特定域。
2.通过名称、版本、文化和公钥来加载程序集
除了通过位置来加载程序集,.NET也提供了通过程序集的名称、版本、文化和公钥来加载程序集的机制。首先要做的事是通过这四个元素来找到唯一的程序集。
当一个程序集被按照名称、版本、文化和公钥来加载时,首先会被应用版本策略来确定所有能够胜任的版本,接着CLR会在CODEBASE指定的位置进行寻找,如果失败则接着在应用程序域目录下进行寻找,然后会在应用程序目录下进行寻找,最后把得到的程序集传递给加载器,进行加载和编译。
图中的应用程序域目录是指AppDomain.BaseDirectory返回的目录,而应用程序目录是指包含了应用程序配置文件的目录。
CLR提供了程序接口,允许程序员主动地按照程序集的名称、版本、文化和公钥来加载程序集。System.Reflection.Assembly.Load方法就是这个接口。
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection; namespace Load
{
class Load
{
static void Main(string[] args)
{
//需要针对不同的主机安装情况进行修改
String assemblyname = @"Compile, " +
"Version=0.0.0.0, " +
"Culture=Neutral, " +
"PublicKeyToken=60c29e5f0af3e9bb"; //根据程序集的四要素加载程序集
Assembly compiledll = Assembly.Load(assemblyname); //创建对象
Object compile = compiledll.CreateInstance ("NET.MST.Second.Compile"); Console.WriteLine(compile);
Console.Read();
}
}
}
CLR通过System.Reflection.Assembly.LoadFrom和System.Reflection.Assemblty.Load来主动地加载程序集。前者通过位置而后者则通过唯一标识强命名程序集的4个元素来标识程序集。CLR的加载机制和Load方法一致,其内在策略是依次通过版本策略、CODEBASE位置、应用程序域位置和应用程序位置来查找程序集。
4 如何配置程序集的版本策略
当一个程序集通过名字、版本、文化和公钥进行加载时,CLR允许程序员指定该程序集的哪些版本可以代替目前版本进行加载。这些都是通过版本策略来实现的。所谓的版本策略,就是一个程序集版本的重定向,把加载当前这个版本定向到加载可替代的版本。版本策略可以在以下三个级别上进行配置:
- 应用程序策略
- 发行者策略
- 计算机策略
这三个级别的版本策略都可以通过XML文件来进行配置。
1.应用程序策略
应用程序策略可以在应用程序配置文件中进行配置,应用程序配置文件位于应用程序目录下。对于.EXE应用程序,其配置文件由exe文件名加上.config后缀名构成,例如一个test.exe的应用程序,其配置文件就是test.exe.config。而对于任何一个Web应用程序来说,其配置文件的文件名都是web.config。
版本策略都记录在配置文件的assemblyBinding节点下。如下代码是一个应用程序的配置文件例子,这里只选取了本节所关心的版本策略部分内容。
Web.Config
<?xml version="1.0"?>
<configuration>
<runtime>
<assemblyBinding>
<!--对这个程序集进行版本重定向-->
<dependentAssembly>
<assemblyIdentity
name="NET.MST.Second.Compile"
publicKeyToken="60c29e5f0af3e9bb">
</assemblyIdentity>
</dependentAssembly>
<!--重定向的策略-->
<bindingRedirect oldversion="0.0.0.0-12.2.2.2" newversion="12.3.0.0">
</bindingRedirect>
</assemblyBinding>
</runtime>
</configuration>
在这个配置文件中,指定了NET.MST.Second.Compile,60c29e5f0af3e9bb这个组件的版本策略,这个策略将0.0.0.0到12.2.2.2之间的所有版本重定向到12.3.0.0版本上。
2.发行者策略
发行者策略是针对那些被放入全局程序集缓存(GAC)中的程序集。发行者策略配置文件的文件名非常古怪,它是这样的一个字符串:主版本号.次版本号.程序集名.dll。正因为如此,一个程序集的每个主版本/次版本号只能有一个发行者策略。
3.计算机策略
同样地,计算机策略同样由一个配置文件表示,它的格式也和代码2-7基本类似。计算机级版本策略配置文件的文件名为:machine.config,它被存储在%SystemRoot%\ Microsoft.NET\ Framework\v****\CONFIG\目录下。
读到这里,读者可能会有这样的疑问:版本策略可以在3个级别进行配置,那这些策略是如何协作的呢?按照.NET的机制,3个级别的版本策略将会按照顺序依次执行,而上一级别的执行结果将会被作为下一级别的执行输入。
如图,3个级别版本策略被依照:应用程序、发行者、计算机的顺序依次执行。而其中,发行者策略是可选的,在以下两种情况下发行者策略将不会被执行。
- 程序集没有被加入到GAC中。
- 应用程序策略制定忽略发行者策略。
在第一种情况下,根本就不存在发行者策略配置文件,当然CLR也就不会执行发行者策略。而第二种情况,是程序员在应用程序策略中指定忽略发行者策略,具体做法是在应用程序配置文件中加入publisherPolicy节点,并且把apply属性值设置为no。
Web.Config
<?xml version="1.0"?>
<configuration>
<runtime>
<assemblyBinding>
<!--对这个程序集进行版本重定向-->
<dependentAssembly>
<assemblyIdentity
name="NET.MST.Second.Compile"
publicKeyToken="60c29e5f0af3e9bb">
</assemblyIdentity>
</dependentAssembly> <!--重定向的策略-->
<bindingRedirect oldversion="0.0.0.0-12.2.2.2" newversion="12.3.0.0">
</bindingRedirect> <!--指定忽略发行者策略-->
<publisherPolicy apply="no"> </publisherPolicy>
</assemblyBinding>
</runtime>
</configuration>
CLR支持在3个级别上设定版本策略,依次是:应用程序策略、发行者策略和计算机策略。所有策略的设置都是通过修改配置文件来实现。3个级别的策略依次会被CLR执行,而上一个策略的执行结果将被作为下一个策略的输入。发行者策略仅仅针对那些放入GAC的程序集,并且可以在应用程序策略中被指定忽略。
转载请注明出处:
作者:JesseLZJ
出处:http://jesselzj.cnblogs.com
.NET基础 (02).NET运行机制的更多相关文章
- JSP基础总结(运行机制、脚本元素、指令元素、动作元素)
JSP的运行机制: 1.转译阶段:JSP页面转换成Servlet类: 2.请求阶段:Servlet类执行,将相应结果发送至客户端. 流程解释: 1.用户访问某个JSP页面 2.服务器找到相应的JSP页 ...
- 02. JVM运行机制
JVM运行机制 JVM启动流程 JVM基本结构 内存模型 编译和解释运行的概念 一.JVM启动流程
- 02 基础 卸载JDK 安装JDK Java程序运行机制
基础 JDK:Java Development Kit(Java开发者工具 包含JRE和JVM) JRE:Java Runtime Environment(java运行时环境,包含JVM) JVM:J ...
- java的运行机制(基础)
1:高级语言的运行机制: 我们编程都是用的高级语言(写汇编和机器语言的大牛们除外),计算机不能直接理解高级语言,只能理解和运行机器语言,所以必须要把高级语言翻译成机器语言,计算机才能运行高级语言所编写 ...
- Java基础介绍运行机制笔记
1. 基础知识点图解 编程语言核心结构:变量.基本语法.分支.循环.数组.…… Java面向对象的核心逻辑:OOP.封装.继承.多态.接口…… 开发Java SE高级应用程序:异常.集合.I/O.多线 ...
- 零基础怎么学Java?Java的运行机制是什么?Java入门基础!
Java语言是当前流行的一种程序设计语言,因其安全性.平台无关性.性能优异等特点,受到广大编程爱好者的喜爱. 想学习Java语言的同学对于Java的运行机制是必须要了解的!! 计算机高级语言的类型主要 ...
- 基础知识《零》---Java程序运行机制及运行过程
Java运行机制 Java虚拟机(Java Virtual Machine):Java虚拟机可以理解成一个以字节码为机器指令的CPU:对于不同的运行平台,有不同的虚拟机:Java虚拟机机制屏蔽了底层运 ...
- javaSE基础02
javaSE基础02 一.javac命令和java命令做什么事情? javac:负责编译,当执行javac时,会启动java的编译程序,对指定扩展名的.java文件进行编译,生成了jvm可以识别的字节 ...
- Windows程序内部运行机制 转自http://www.cnblogs.com/zhili/p/WinMain.html
一.引言 要想熟练掌握Windows应用程序的开发,首先需要理解Windows平台下程序运行的内部机制,然而在.NET平台下,创建一个Windows桌面程序,只需要简单地选择Windows窗体应用程序 ...
随机推荐
- 使用 Windows 运行时中异步性来始终保持应用程序能够快速流畅地运行
转自:http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/03/26/windows.aspx 人类的思维方式在本质上不是同步的,这直接影响着我 ...
- dede_CMS模板的基础安装
今天来给大家讲一讲dede_CMS的基础使用方法 那么什么是CMS呢 cms (content manage system 内容管理系统): 比如 新闻/电子商务/电影网/公司宣传网站/软件/文章) ...
- Varnish,Nginx搭建缓存服务器
Varnish,Nginx搭建缓存服务器 一. varnish 1.安装pcre库,兼容正则表达式 # tar -zxvf pcre-8.10.tar.gz # cd pcre-8.10 # ./co ...
- python写个Hack Scan
前言: 之前逛SAFEING极客社区的时候 发现一款黑市卖2000多的软件,后面下载了 打不开.发现config文件里面有些不错的东西.总结了一下 有了以下的脚本. 脚本用处: [1]探测CMS(不敢 ...
- Java中UTC时间转换
import java.text.SimpleDateFormat; import java.util.Date; import java util.Calendar; public class Te ...
- 关于windows系统里locale、code page、ANSI编码的问题
最近把公司代码库里的代码同步下来之后编译了下,竟然出问题.问下同事说代码库肯定没问题,而我啥也没改,那到底那里出问题了呢? VS2018报的错误是:error RC2001: newline in c ...
- C# 提取方法重构
引用:https://msdn.microsoft.com/zh-CN/library/0s21cwxk.aspx “提取方法”是一项重构操作,提供了一种从现有成员中的代码段创建新方法的便捷方式. 使 ...
- leetcode357
public class Solution { public int CountNumbersWithUniqueDigits(int n) { ) { ; } ; ; ; && av ...
- Win7 Wifi 老断线
在cmd命令窗口 netsh wlan set autoconfig enabled=no interface="无线网络连接" 此时你再来查看Win7系统任务栏处的网络菜单中查找 ...
- 利用CopyOnWriteArrayList解决并发修改异常问题
一.需求 多个线程再获取同一个集合里面的数据同时,修改集合中的数据. 二.有问题的写法 package com.duchong.juc; import java.util.ArrayList; imp ...