C#者重建C++之路 - 运行机制的差异
运行机制的差异是大多数C++支持者唾弃C#的原因,这里并不去讨论这类说法对不对,我想说的是每种语言都有擅长和不擅长的使用场景,无需拿一种语言的长处去鄙视另外一种语言的短处。针对不同的问题,需要选择合适的方案(包括合适语言)去解决;就好比历史上常说的:“计谋并无好坏之分,好坏在于使用的人”。而且实际上,VS中,C#与C++都支持托管与非托管代码混合双打的使用方式。
一、C++的运行机制
C++的运行机制很简单,其实也是我们在学校里就学过的。这里总结的是针对非托管的C++程序的运行过程,托管C++的运行过程基本类似于C#的运行过程。使用VS2010中工程创建的C++工程,例如Win32,MFC,ATL开头的基本都是非托管的程序。
基本C++程序从编写,编译到执行的过程用下面一幅图就可以简单说明了:
在VS中,编译器使用的是cl.exe,连接器使用的是link.exe。
二、C#的运行机制
C#语言的运行机制其实就是所有托管语言编写的程序的运行过程。CLR是托管代码的运行环境,是托管语言运行机制的核心。还是从用一幅图说明情况:
需要注意几点:
- C#使用csc.exe编译源代码得到程序集。
- JIT只会编译首次执行的IL代码,以后再次执行的时候都从缓存中读取。
- JIT编译得到的本地代码在程序结束以后会销毁。程序再次运行的时候JIT再次编译IL为本地代码。
- 为了解决JIT多次编译的效率问题,微软提供了Pre-JIT的编译器(本机代码生成器:Native Image Generator,程序名因此是Ngen.exe)。Pre-JIT编译器在CLR之前被调用。在使用时,它会把全部assembly形式的MSIL编译为本机代码。这种本机代码随后存储在全局assembly缓存(Global Assembly Cache)的某一个特殊部分供以后使用,这样就完全绕过了JIT编译过程。
- 即使使用了Ngen预编译,托管代码的执行还是离不开.NET Framework。当然这也是不少人正在努力的方向:)。
- GC内存管理是托管的精髓。
- 程序集有强签名与弱签名之分,通常强签名的程序集可以避免程序集的升级问题。
- GAC是程序集云集之地,是程序集共享的统一途径。只有强签名的程序集才可以放到GAC中。
- 每个托管程序都是运行在独自的AppDomain中运行,每个托管程序之间互不干扰。
- 除去首次编译的时间,基本上C#程序与C++程序的执行时间差别不是太大,但是还是有优化的空间的。
运行机制还有很多的东西可讲,比如内存的分配算法,这里就简单说一下:
C与C++分配一个对象时,会查找内存中可用的连续空间,如果没有的话,会查找可用的分散空间,找到符合大小的空间后,就分配对象,如果找不到,就报错。
C#中主要是使用托管堆,基本是在连续的空间上分配对象,这种算法有一定的优越性,也充分利用了计算机系统的局部性原理,但是GC不断的回收空间与压缩也占据了一定的时间,一定程度影响了效率。