【深入理解CLR 二】CLR的执行模型(下)

简介: 【深入理解CLR 二】CLR的执行模型(下)

这里提一下我的猜测,不一定准确:

  • 一个应用程序就是一个程序集,一个进程可以运行多个程序集(应用程序),一个应用程序也可以被多个进程同时运行。(果然不是很准确,一个应用不一定是一个程序集,一个程序集可以被多个应用使用—2018-5-17)
  • IL代码是进程隔离的,不同进程即使运行同一个应用程序也会导致重新编译。因为不同进程互相不可见对方代码。但是本机CPU指令是各个进程共享的,所以之后提到的NGen.exe对于提高性能很有用
    ####CLR对代码的优化
    上小节介绍了执行流程,大概有个了解之后,我们发现Jit编译可能会造成性能损耗,那么就有两种手段来给其执行加速:

1,给调试器进行对应设置。

2,提前编译为本机代码(NGen.exe会提到),该方法不推荐

我们常见的编译器开关有两个,一个是optimize(对应IL代码质量优化),一个是debug(对应于本机代码质量优化)。

猜测:第二行表示optimize-并且 debug:full也就是后边提到的Debug状态,第三行表示optimize+并且 debug:pdonly也就是后边提到的Release状态

#####optimize开关

/optimize- 表示不优化IL代码,也就是不优化以下两部分内容:

  • IL代码包含许多NOP(空操作)指令以及许多分支指令。利用这些指令,VS在调试期间才能提供“编辑并继续”功能。“编辑并继续”是一种省时的功能,能够在程序处于中断模式时更改源代码。 当通过选择一条类似 Continue 或 Step 的执行命令继续执行程序时,“编辑并继续”有限制地自动应用代码更改。 这允许在调试会话期间更改代码,而不是停止程序,重新编译整个程序,再重新启动调试会话

设置详见这篇博客:https://blog.csdn.net/xiaoxian8023/article/details/7220590

  • 利用以上额外指令,还可在控制流程(for,while,do,if,else,try,catch,finally)指令上设置断点。只有这样,VS在代码调试时就可以对代码进行单步调试,如果被优化掉,一些函数求值可能无法执行。

当然优化后(optimize+),IL代码会变的更小,结果EXE/DLL文件也变小。但不能编辑并继续和单步调试还是很痛苦的,所以调试阶段最好关了,发布阶段可以打开。

#####debug开关

/debug(+/full/pdbonly)表示一定生成PDB文件,full(附加到进程),pdbonly(不附加到进程)

PDB文件帮助调试器查找局部变量并将IL指令映射到源代码,也就是IL指令和源代码之间的映射文件。

PDB 文件的全称是 Program Database,用于存放一些 IL 和 二进制文件之间的映射,主要为对应的文件名、行号等一些符号。该文件由 Visual Studio 自动生成,主要为调试程序提供便利。VS默认是打开的,当然也可以关闭,关闭之后就不能从异常定位错误了

PDB相关博客http://blog.chenxu.me/post/detail?id=f4301bf3-3709-4c8f-8a45-9b9015909ce2

debug:full表示记录IL指令与本机代码的联系。这样就能将调试器附加到进程调试

总结如下:如果没有PDB,就没有源代码与IL代码联系,无法定位错误,有PDB,但只是pdbOnly,只能看堆栈信息,不能附加到进程,debug:full,可以附加到进程调试。

VS启动时默认debug:fullReleas模式下,optimize+, debug:pdbonly, 也就是尽可能优化,不调试了,Debug模式下,optimize-,debug:full,也就是性能降到最低,但可以附加到进程调试。

若想修改启动时默认debug:full,下图中位置不打勾就是了。

####JIT编译器的优势

其实也就是介绍托管代码的优势。因为只有托管代码运行在CLR之上,非托管代码是针对一些具体CPU平台编译的,一旦调用,就能执行。这也是为什么虽然大多数时候我们选择anycpu,有时候也要指定cpu架构。

  • JIT可以适配运行本机,做一些提升性能的优化
  • JIT能判断一个特定测试是否总会失败,如果是,那么只执行一遍,不傻瓜式执行
  • JIT能进行分支预测,CLR自己评估每次的代码执行(厉害了,有点儿像AI)
  • 还提供NGen.exe,进行预编译,避免运行时编译(作用有限,之后提到)

#####IL和验证

首先IL是基于栈的无类型的,然后CLR可以通过一定方法对IL进行验证,验证后可确保代码不会不正确的访问内存。此前为了防止代码出错,一个应用程序会启一个进程,因为进程隔离,所以不会互相影响,但会启动很多进程,占用资源,现在有了验证功能,一个进程可以同时执行很多应用程序了!

#####不安全的代码

其实不安全的代码就是非托管代码:

  1. 未经CLR验证安全性
  2. 它可以直接操作内存地址

这么做是相当危险的,那么为什么还要有不安全代码呢?在如下情况可以:

  1. 与非托管模块进行互操作(注意,只有C++编译器才能将非托管代码和托管代码生成到一个模块中)
  2. 提升对效率要求极高的一个算法性能时候,需要用到此类代码

体现在VS里就是:

编译器打勾之后,所有包含不安全代码的方法还都要用unsafe来修饰。如果遇到不安全代码,就会报错,我们常常看到的包含不安全代码执行的应用程序警告都来源于此,通常是从Internet下载一些程序之类的时候

#周边工具及相关概念

##本机代码生成器NGen.exe

这个工具的工作非常简单,**就是预编译IL为本机代码,防止运行时编译。**通常有以下两方面作用:

  1. 提高应用程序的启动速度,这个好理解,都编译好了,当然快。
  2. 减小应用程序工作集,这个如前所述,**本机代码是进程不隔离的,这样提前编译好可以给各个进程复用啦。**当然小。

就像之前我提到的鸡肋,这个功能一般不用,那为什么鸡肋呢?

  1. 因为没有原始IL代码,所以不能享受IL的知识产权保护
  2. NGen生成的文件可能失去同步,之前生成的环境和当前运行环境可能不匹配,例如操作系统版本,CLR版本,CPU类型
  3. 较差的执行性能,JIT编译器的三个优点(平台适配,判断,分支预测)一个都使不上,因为没有使用JIT编译器,有时候甚至比JIT现场编译更慢。

##FCL类库

类库在上一篇博文里有过介绍,这里不再赘述,贴一个常用命名空间表:

##CTS系统

为了使一种编程语言写的代码能与用另一种编程语言写的代码沟通,CLR必须能支持,而类型是CLR的根本,那么类型必须有个正式规范,也就是CTS。定义代码的行为

还有六种访问类型:

private---------------------------同一个类

family and assembly--------自己和同一个程序集里的子类,这个C#没有

family---------------------------自己和自己的子类,C#(protected)

assembly----------------------自己和同一程序集里的,C#(internal) 这个java也没有哦

family or assembly----------自己和自己的子类和同一程序集里的 C#(protected internal)

public---------------------------任何程序集里任何代码

举例说,CTS对待继承的态度是单继承,那么C++里多继承的部分就不适用。感觉这系统很鸡肋,可能没到那个层次吧。微软自家语言在上边互相通信,一般还用不上多语言功能

##CLS规范

CLS 定义了公共语言的最小集合:

要想使用其它语言类型,就不要用public和protected修饰,这两个一修饰,整个方法就暴露到外边,其它语言看到了方法中有不适合自己的类型,就要报警告。当然如果代码仅仅是自己内部使用,就没必要关CLS了。

##与非托管代码的互操作性

  • 托管代码能调用DLL中非托管函数
  • 托管代码可以使用现有COM组件
  • 非托管代码可以使用托管类型

#技术分享实例

可以就以下方面重点分享:

1, CLR整体执行流程介绍(主线):编译为IL-------加载CLR-----执行IL过程

2, 挑选一些实例来分享,解释原理:

  • win32和win64不同平台
  • JIT优化,对应于VS面板:调试开关,平台开关,安全代码开关

从学习到汇总用时3天,总算将CLR的大致执行流程汇总为本文,自我感觉还是很不错的。因为有毕设的事情,所以时间用的比较琐碎,感觉理解的还算透彻,再接再厉!

相关文章
|
XML 开发框架 JSON
成功实现C++调用C#写的库(CLR),我的个人心得与总结
成功实现C++调用C#写的库(CLR),我的个人心得与总结
1599 0
|
11月前
|
存储 开发框架 搜索推荐
【深入理解CLR 二】CLR的执行模型(上)
【深入理解CLR 二】CLR的执行模型(上)
112 0
【深入理解CLR 二】CLR的执行模型(上)
|
11月前
|
开发框架 安全 .NET
【深入理解CLR 五】类型基础
【深入理解CLR 五】类型基础
129 0
|
开发框架 .NET C++
|
.NET 开发框架 Windows
|
.NET C# 编译器
[CLR via C#]8. 方法
原文:[CLR via C#]8. 方法 一、实例构造器和类(引用类型)   类实例构造器是允许将类型的实例初始化为良好状态的一种特殊的方法。   类实例构造器方法在"方法定义元数据表"中始终叫.ctor(代表constructor)。
768 0
|
.NET C# 编译器
[CLR via C#]9. 参数
原文:[CLR via C#]9. 参数 一、可选参数和命名参数   在设计一个方法的参数时,可为部分或全部参数分配默认值。然后,调用这些方法的代码时可以选择不指定部分实参,接受默认值。此外,调用方法时,还可以通过指定参数名称的方式为其传递实参。
796 0