(转载)Metadata.NET平台的核心灵魂

July 7th, 2010 jzli Leave a comment Go to comments

网友来信:李老师,您好!我参加过你去年到我们公司做的.NET深度培训,也拜读过你的译作:《.NET框架程序设计(修订版)》和 《Effective C#》,受益匪浅,非常佩服你这样优秀的.NET技术专家。前几天在博客园上的C#大论战,不知道您看过吗?特别是其中一个网友firelong所写的几篇轰动的帖子,对.NET的性能提 出了许多批评。这个话题在我们项目组(大多数都参加过你去年的培训)也引起了很多争论,很想听听李老师对这些观点的看法?……….

本来是以email的形式回复这位朋友的,但是写着写着发现写得长了,最后考虑将其以博文的形式登出。

其实个人无意参与这样的论战,因为很多口水帖、情绪贴. 我简单浏览了一下,发现论战的各方(firelongjeffreyzhao,以及.NET社区众网友),最后都情绪激动了,情绪激 动之下讲的话大多都丢失了技术最扎实、最基本的原理。但是这些文章在口水之外,确实有一些深层次的技术问题暴露出来——有些甚至跨越.NET,而延伸到更 广泛的领域。但是,它们却被口水淹没了,没有好好深入讨论下去,非常可惜。这便是我最后决定写这篇文章的原因。我希望能够对其中被忽视的一些技术问题,做 一些有意义的探讨,与大家共享。

首先我想就《C# 会重蹈覆辙吗?系列之2:反射及元数据的性能问题》一文中的主要技术点来谈谈。这篇文章主要观点如下(并且给了相应的分析):
1. 使用反射会有很高的性能成本
2. 即使不用反射,为支持反射而产生的metadata也有很高的性能成本

因此,作者才得出来在《C 与C++社区混战,C#会重蹈覆辙吗?》一文中“删除反射”的结论。关于反射和metadata带来的性能问题,我想没有人会否认,只是其性能问 题到底有多糟?我后面有空的话也许会另文讨论(firelong文章中有部分含糊其辞)。

本文中,我想讨论的是《C# 会重蹈覆辙吗?系列之2:反射及元数据的性能问题》一文的整个推理过程中的一个基本论点:.NET平台中metadata的目的是为了支持反射。

在这个论点的基础上,文中才有“反射并不常用,为了支持反射所创建的metadata却带来很多性能问题。因此删除反射,也就不需要创建 metadata,这样.NET就不会为此再付出性能损失(就像C、C++那样)”。

很不幸,这个基本论点是对.NET平台的极大误解。换言之,在.NET平台中metadata的存在绝对不仅仅只是支持反射。实际上,支持反射只是 metadata一个很小的用武之地,metadata是整个.NET平台非常核心的基础支撑设施,它在.NET平台中有着广泛的应用,是.NET平台的 灵魂。

为什么这么讲?这要从.NET创建时的整个软件时代背景来谈起,我们才能深刻理解这一点。.NET创建之前,Windows平台的软件技术经历了以 下几个阶段:DDE、OLE、ActiveX/COM。这些技术所努力解决的核心目标是:软件组件的复用性——这是软件开发的核心命题,是软件平台厂商竞逐的焦点。“软件组件的复用性”有以下几个含义:

1. 强调二进制级的复用(黑盒复用),而不是源代码级的复用(白盒复用)。
例如:我想在我的软件中集成PDF阅读器的能力,我不需要找Adobe要PDF阅读器的源代码,而是去找Adobe要一个支持PDF阅读的二进制组件—— 然后通过接口的方式来使用它。
2. 强调多语言创建的组件之间的复用,而不是单一语言创建的组件之间的复用。
例如:我在C++中写一个Email收发组件,可以在VB中直接使用。

DDE、OLE、ActiveX/COM无一不是围绕这个目标。COM是这些技术发展进程中的一个高峰。但是COM技术本身在发展过程中暴露出了很 多问题。Don Box在《Essential .NET》一书的第一章“The CLR as a Better COM”对COM的问题有非常深刻的解剖。

首先,要达致“组件复用”这一目标,必须有合同来规范组件与执行环境、组件与组件之间的各种约定。COM使用的是IDL或TLB作为组件合同,但它 们有几个根深蒂固的缺点:

1. IDL/TLB规范的是物理语义(例如v-table偏移,栈帧,参数,数据结构,对齐等内存细节),且使用的是C++语言的一个子集约定。
2. IDL/TLB规范描述与组件代码本身分离。
3. IDL/TLB规范较为混乱,没有达成业界统一的标准(第三方难以扩展开发)

其中第3点问题是微软对COM没有前后一致的系统规划导致——这一点如果假以时日是可以解决的。但是前面1、2点的问题是COM根深蒂固的问题,导 致了COM组件后期出现的各种难以解决的问题——不同语言实现面向COM的编译很难,因为满足了COM的复用模型,往往要打破该语言本身模型所约定的复 用。同时在一个语言中维护两套编译模型、和复用机制,步履维艰(在C++、VB、Delphi等语言中开发COM组件的痛苦相信很多人深有体会)。

而这个时候,95-98年间名声大噪的Java给了微软相当大的启发。特别是其中的metadata,微软的技术精英发现metadata是最好的 组件合同定义体。Metadata有以下几个非常好的特点(这些正好克服了COM组件描述IDL/TLB的缺点):

1. Metadata描述的是逻辑结构,不涉及任何物理细节(例如v-table偏移,栈帧,参数,数据结构,对齐,方法地址等物理细节,直到在具体平台上加 载、执行时,才被确定,比如IL代码中经常看到的CALL指令后面的 MyClass:MyMethod就是逻辑上的元数据,而不是具体方法的物理地址)
2. Metadata本身与组件代码合并在一个文件中(即程序集),包含了组件依赖,版本等信息
3. Metadata从一开始就经过系统、精心的设计,是CLI业界标准的一部分(便于第三方开发相关应用)

这些特点使得metadata非常适合作为.NET组件与执行环境(CLR)、组件与组件之间(不用语言开发的组件)理想的合同规范。特别是第1 点,“Metadata虚拟的逻辑属性”使得组件合同专注于“逻辑语义层面”,而非“物理实现细节”。简单解释一下:
1. 组件与组件的复用从此集中于“逻辑语义层面”,比如C1的方法M1中调用MyClass的方法MyMethod,用语义表达为:call MyClass:MyMethod。或者C1类继承C2类:用语义表达为:class C1 extends C2。或者调用虚方法:callvirt MyClass:VirtualMethod….
2. 组件与组件的复用不再纠缠于“物理实现细节”,例如:调用某个方法,必须知道它的入口点地址如jmp 0×000688;继承某个类,必须清楚它的字段layout等等,调用虚方法,必须清楚v-table偏移规定…..

那为什么“专注于逻辑语义层面的合同”要优于“专注于物理实现细节的合同”呢?

这就又要回到前面说的“软件组件的复用性”这一目标上来。简单来说,如果要实现“软件组件的复用性(注意二进制、多语言两个属性)”,“专注于物理 实现细节”对于各个语言的编译器厂商,要求太高了,各编译器厂商要协调一个执行体各个方面的物理细节(v-table偏移,栈帧,参数,数据结构,对齐, 方法地址等等…..不一而足)——这么多细节的要求,使得大家极难协调——COM后期的弊端丛生就源于此。

而“专注于逻辑语义层面”来实现组件的复用性,各编译器厂商生成的执行体(即.NET里面的程序集)只需要“用metadata表达逻辑语义上的复 用规范”即可——这对于各编译器厂商需要协调的规范的量级大大减少,实现起来相对容易得多。也不容易在各种细节上出现漏洞——因为大量的物理细节全由 CLR执行时来确定。

综上所述,metadata的根本性的作用是支持基于逻辑语义的组件复用”——这是.NET致力于打造软件开发平台的核心目标。支持反射仅仅是metadata一个很小的衍生功能,而非主要功能。

事实上除了支持“基于逻辑语义的组件复用”这一核心目标外,metadata在.NET平台中还起着其他重要作用(有些是组件复用的延伸需求,比如 JIT与跨平台):

1. Metadata是JIT编译、亦即实现.NET跨平台的基础
说明:IL代码中大量引用着Metadata。在MethodDef元数据表里面存储着一个方法的各种语义信息,许多IL指令就直接内嵌了 Metadata Tokens。没有Metadata,JIT也无法实现。

2. Metadata是.NET支持垃圾收集GC的基础
说明:metadata标记了对象与对象间的引用关系,这是GC遍历对象图(判断对象是否可以收集)的关键依据。没有metadata,GC将不知道 0×000688是一个指针(需要继续遍历)?还是一个整数(不需要继续遍历)?

3. Metadata是描述类型契约、标识、规范等的基本信息
说明:强命名程序集(使用metadata描述)唯一标识了编译器当初编译时的组件,不至于导致运行期仿冒、或者版本偷换(即DLL 地狱问题)

4. Metadata是.NET管理组件安全的基础(运行时类型检查,组件下载)
说明:metadata会告诉CLR执行环境一个组件的安全边界,从而可以管理那些恶意代码。

5. Metadata是.NET管理组件引用关系,加载的基础
说明:metadata会告诉一个组件需要引用哪些组件(哪些版本)?这是组件加载的基础。

从这里大家可以看出来,Metadata是.NET平台实实在在的基石。Metadata之于.NET平台,就像维他命至于生物体一样。换言之,删 除反射,并不能消除metadata。如果要消除metadata,.NET平台的整个设计基础(组件复用,以及上述的其他种种功能)将不复存在。

最后,指出《C# 会重蹈覆辙吗?系列之2:反射及元数据的性能问题》一文中的核心论据错误,并非为了追求驳倒firelong。建议 firelong,jeffreyzhao以及这场辩论的众多技术领域的网友(不管是firelong,jeffreyzhao支持者还是反对者),抛掉 “非要辩个胜负,分个高低”的怪诞氛围,而是来一些扎扎实实的技术说理过程,相信会更有意义——如是,则国内技术社区成长可待!

Metadata是.NET平台的核心灵魂--(转载)的更多相关文章

  1. 2014 Container技术大会:未来Linux Container会是PaaS平台的核心

    不应错过2014 Container技术大会的九大理由. 一.Docker官方人员再次来到北京,首次向中国布道Docker技术.2013年Docker高级软件工程师Jerome Petazzoni,曾 ...

  2. 简要分析武汉一起好P2P平台的核心功能

    写作背景 加入武汉一起好,正式工作40天了,对公司的核心业务有了更多的了解,想梳理下自己对于P2P平台的认识. 武汉一起好,自己运营的yiqihao.com,是用PHP实现的,同时也帮助若干P2P平台 ...

  3. 基于xilinx Zynq UltraScale MPSoC平台的核心板及开发板介绍-米尔科技

    近日,米尔科技推出国内首款基于xilinx Zynq UltraScale+MPSoC 平台的核心板及开发板.其优势主要有:采用16纳米制程,相比Znyq7000系列每瓦性能提升5倍,且单芯片融合4核 ...

  4. C#开发微信公众平台-就这么简单(转载)(附原文链接)

    一直使用的是一百八的诺鸡鸭,没有想去接触看起来风风火火的移动互联网:但因工作需要维护一个微信公众订阅号,考虑以前有做网站的基础,就想着做个简单的微信后台管理:看了官方的开发文档,比狗哥地图的短许多,又 ...

  5. Maven学习总结(四)——Maven核心概念--转载

    一.Maven坐标 1.1.什么是坐标? 在平面几何中坐标(x,y)可以标识平面中唯一的一点. 1.2.Maven坐标主要组成 groupId:组织标识(包名) artifactId:项目名称 ver ...

  6. Maven学习总结(四)——Maven核心概念——转载

    一.Maven坐标 1.1.什么是坐标? 在平面几何中坐标(x,y)可以标识平面中唯一的一点. 1.2.Maven坐标主要组成 groupId:组织标识(包名) artifactId:项目名称 ver ...

  7. SonarQube代码质量管理平台安装与使用--转载

    原文:http://blog.csdn.net/hunterno4/article/details/11687269 Sonar简介 Sonar是一个用于代码质量管理的开源平台,用于管理源代码的质量, ...

  8. Android平台及其架构(部分转载)

    一.Android的系统架构 1.      应用程序 同Android系统一起发布的核心应用程序,如email 客户端,SMS 短消息程序,日历,地图,浏览器,联系人管理程序等. 这些应用程序都是用 ...

  9. windows平台上运行Flink_转载于CSDN

    Flink安装部署-window 本地部署原创冰上浮云 发布于2019-08-17 15:56:06 阅读数 633 收藏分类专栏: flink版权声明:本文为博主原创文章,遵循 CC 4.0 BY- ...

随机推荐

  1. ThreadLocal是否会引发内存泄露的分析(转)

    这篇文章,主要解决一下疑惑: 1. ThreadLocal.ThreadLocalMap中提到的弱引用,弱引用究竟会不会被回收? 2. 弱引用什么情况下回收? 3. JAVA的ThreadLocal和 ...

  2. Angular规范

     只记录一些自己未曾用过,但觉得对以后的项目有帮助的规范 一 Javascript闭包 把Angular组件包装到一个立即调用函数表达式中(IIFE). 为什么?:把变量从全局作用域中删除了,这有助于 ...

  3. Swift学习——Swift解释特定的基础(七)

    Implicitly Unwrapped Optionals    隐式解析选项 如上所述.可选意味着常数或变量"没有值".通过可选if声明来推断是否存在值,假设有值析值. 有时候 ...

  4. 一个非常不错的gridview 风格

    <style type="text/css"> <!-- .datable {background-color: #9FD6FF; color:#333333;  ...

  5. 不能交换到解决jenkins用户的问题

    su - jenkins始终有效,今centos无效,因为 /etc/password在文档/bin/bash是yum当安装到/bin/false. 之后可以改变. ubuntu安装包和yum安装包的 ...

  6. Saving HDU (贪心)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2111 好久不刷题,拿到水题切了切,,,,,题意刚开始都没有理解,,,,真是弱了,,,, 简单贪心,,, ...

  7. 阅读小记3(《C编程专家》)

    gets()不检查缓冲区空间.多余的字符将覆盖原来的栈的内容. fgets()的第二个參数说明最大读入的字符数. 假设这个參数值为n,那么fgets()就会读取最多n-1个字符或读完一个换行符为止.两 ...

  8. mac_Mac item2常用快捷键

    整理使用 iTerm 2 过程中得常用快捷键,Mac 原来自带的终端工具 Terminal 不好用是出了名的,虽然最近几个版本苹果稍微做了些优化,功能上,可用性方面增强不少,无奈有个更好用的 Iter ...

  9. KMP算法之从next[]到nextVal[]

    前些日子写了一篇KMP算法的博文,浅谈数据结构之KMP(串中的模式匹配算法),在这片文章中,谈到了一个模式串K值的记录数组 next[],详细可看那篇文章,其实,前面定义的next[]数组是有一定缺陷 ...

  10. 访问Ice-Pick Lodge:假设公众筹款网站Kickstarter在成功

    Xsolla非常高兴採訪了来自莫斯科的工作室 Ice-Pick Lodge的Golubeva.数天前,该公司已成功在Kickstarter上募集资金,创造出最知名的游戏"Pathologic ...