(转载)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. 【IOS开发笔记01】学生管理系统(上)

    端到端的机会 虽然现在身处大公司,但是因为是内部创业团队,产品.native.前端.服务器端全部坐在一起开发,大家很容易做零距离交流,也因为最近内部有一个前端要转岗过来,于是手里的前端任务好像可以抛一 ...

  2. 蜗牛—苍茫IT文章大学的路(十)

    昨晚,有个叫***培训机构鼓吹我们学校.起初我还以为是介绍这个游戏吧.谁知道.它原来是一个培训结构.去年我买的表啊 我知道这会不会去,我也浪费了时间审查.因为今天下午和晚上来测试啊.我没有审查,. 当 ...

  3. eclipse+Java2WSDL+WSDL2Java 2012-12-06 12:32:43| 分类: j2ee |报道|字体大小 认购 一、eclipse如何使用低axis生成wsdl 可以使用

    eclipse+Java2WSDL+WSDL2Java 一.eclipse下怎样用axis生成wsdl 能够使用axis提供的Java2WSDL功能生成所要公布类的 WSDL,过程例如以下:  1.在 ...

  4. 设计模式之享元模式(Flyweight)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  5. C#格式化字符串中转义大括号“{}”

    原文:C#格式化字符串中转义大括号"{}" 今天,用C#写程序操作Excel,读取单元格内容根据所需格式生成字符串,使用String.Format(string format,ob ...

  6. Mybatis 构造resultMap 搜sql

    映射配置文件 <!-- type:映射数据类型的实体类 id:resultMap的唯一标识 --> <resultMap type="person" id=&qu ...

  7. shell变量赋值进阶

    首先,要理解shell中变量的3种赋值情况: unset 例子. unset a 空字符串, null 例子. a='' 非空,即不是unset,并且不是空字符串 例子: a=1 or a=b等 然后 ...

  8. Contoso 大学 - 使用 EF Code First 创建 MVC 应用,实例演练

    Contoso 大学 Web 示例应用演示了如何使用 EF 技术创建 ASP.NET MVC 应用.示例中的 Contoso 大学是虚构的.应用包括了类似学生注册.课程创建以及教师分配等功能. 这个系 ...

  9. Cgroup maintainer丽泽范:解剖Linux核心容器技术

    摘要:Cgroup和namespace等内核特性如何出现,在社区处于如何的开发状况?Docker如火如荼.内核社区是否会因此加紧完好容器技术的隔离性安全性?华为Linux内核高级project师李泽帆 ...

  10. 出现localStorage错误Link解决方案(组态)

    属性-链接-进入-附加依赖-加入sqlite3.lib cocos2d-x-2.2.2\Debug.win32添加的文件夹sqlite3.dll.sqlite3.lib 版权声明:本文博客原创文章.博 ...