代码改变世界

对于Global Assembly Cache与Native Image Cache的一点探索

2008-12-08 13:32  GUO Xingwang  阅读(2865)  评论(10编辑  收藏  举报

     有过.Net平台上开发经验的朋友,很多人对Global Assembly Cache(文中简称GAC)都不陌生,但是很少有人听过Native Image Cache(文中简称NIC,这是自己起的简称)。在这篇文章里我将对GAC和NIC做一个稍稍深入的探讨,包括一些使用上的小技巧,部分是本人对于一些概念的理解和一些微软的MSDN中没有记载的或回避的内容,希望这篇文章能给大家带来一点收获。

     对于.Net中"程序集"的认识

     "程序集"的概念就不再多说了。我这里想说的是我个人对程序集的几点认识:

1.程序集是一个物理结构,不同于命名空间是一种逻辑结构。请注意程序集与命名空间的区别;

2.程序集不等于.exe和.dll,.exe和.dll只是.Net程序集在Windows系统中的一种包装形式。

3.程序集分为专用程序集和共享程序集,共享程序集就是放在GAC中的程序集。GAC中对程序集的要求更严格。

     在Windows环境中,.Net类库中常用的程序集一般存储在:

1.%SystemRoot%\Microsoft.NET\Framework\.Net的版本,这些程序集一般是在开发过程中引用使用的,可以说是为应用程序的设计时使用的。

2.Global Assembly Cache,一般是为程序的运行时使用的,当然也可以作为设计时使用。实际上对于大多数程序集的运行时代码是在NIC中获得的,下文会做详细的讲述。
对于一些最常用的程序集一般在这两个地方各存储一份。例如:System.Web.dll等。

     GAC与NIC

     对于GAC这里也不多说了,我们只需要知道GAC可以解决DLL版本问题,GAC中的程序集需要强命名,GAC中的程序集具有Full Trusted权限,.Net类库的程序集一般存储在GAC中,可以使用gacutil.exe工具将自己开发的强命名的程序集放到GAC中。

     一般.Net的应用程序在第一次执行时会将MSIL代码动态编译成本地代码,这些本地代码通过机器码管理器进行管理,第二次再执行时,就直接执行缓存中的本地代码,因此第二次以后的执行速度会非常快,当然如果要事先将程序集编译成本地代码也行,这样可以避免第一次执行比较慢的问题,通过在VS的命令提示符下执行以下命令就可以将程序集安装到NIC中来存储程序集的本地代码:
ngen 程序集文件全名,例如:ngen d:\test.exe

     GAC和NIC被存储在什么位置

     我们在安装完.Net Framework以后发现在系统根目录下多了一个叫assembly的文件夹,这实际上就是存储GAC和NIC的地方,

Windows操作系统对这个目录做了一个特殊的处理来避免用户的任意访问,我们看到的无非也就是下图:

不过没关系,我们可以通过命令提示符输入dir命令来查看一下这个存储目录的文件到底是如果组织的:

再进入每个目录我们会发现GAC和GAC_32,GAC_MSIL是和程序集的GAC有关,内部按照版本号存储,还有一个.ini文件用于解决配置问题。NativeImages加版本号这个目录主要是存储NIC的本地代码,我们还会发现每一个注入NIC的exe和dll(这已经不是程序集了,应该是本地代码。不过直接运行会报错,虽然文件扩展名没有改变,可能存储代码的结构改变或没有main函数等)都是在文件名后面加一个.ni来命名的。

     为了确定我们在本机运行的托管的应用程序是从NIC中加载的代码,我们可以使用一些工具查看一下进程的映象文件:

从图中我们可以注意到TxtConvertHelper.exe(托管的程序)的空间中加载了System.Drawing.ni.dll镜像,System.Drawing.ni.dll中包含了System.Drawing.dll包装的程序集的本地代码。从NIC的目录中还可以看出对于.Net类库的多数程序集都做了本地镜像处理,本地镜像才是真正的运行时代码。

     .Net对常用类库程序集作这样的处理也是为了减少JIT编译带来的性能开销(虽然只有一次,对于桌面应用程序却很重要,对于Web应用似乎不是很重要),要不每次都JIT所有类库,速度可想而知。所以我们在部署.Net的桌面应用时也可以采用NIC处理,最简单的方式就是在安装时写个批处理执行一下ngen命令,估计ClickOnce也可以作这样的处理(很可能是默认),不过我没有研究过,感兴趣的朋友可以研究一下。

     还有就是对于做过NIC处理的程序的更新与同步问题,.Net框架中对于NIC程序集的更新是通过一个Windows服务来同步的,详细资料请参照MSDN文档,这已经超出本文的范围,不再多说。

     如何导出GAC中的程序集

     对于GAC中的程序的导出,我总结了三种方式(本质上是一种):

1.通过点击VS项目的某一个引用,在属性对话框中设置Copy Local=true,然后到项目的bin或obj/Debug等目录获得引用GAC中程序集;

2.同第一种,获得属性对话框中的Path属性的值,之后在“运行”中打开GAC程序集的路径,将程序集复制出来;

3.使用命令提示符的Copy命令将需要的程序集拷贝出来。

     总结

     关于GAC和NIC的管理是很复杂的,这篇文章只是略作一点分析,希望能给您带来一点收获,对于C++的开发者可以使用Debug工具获得更多的调试信息。

     概念依旧是概念