自从在cnblogs和csdn写了几篇批评C#/.NET的博文后,便受到了多方.NET粉丝的轮番群殴:http://www.cnblogs.com/topic/53/。这段时间正好出差,没有及时回复,便被某些朋友视作理屈词穷。其实,我在第一篇博文中就说过,我既然列出这些论点,一定有支持这些论点的实践证据和技术原因——也许有些观点错误,但是我总有我的道理。说出来,和大家分享讨论而已——没有及时回帖,只是工作原因,绝不会理屈词穷,请大家放心,我还会继续战斗下去,呵呵:)
我仔细看了目前为止参与“群殴”的博文,说实话,大部分都是没有技术含量、错误百出的。比如如下观点:《说透 事件和接口,两者怎能互相替代》:http://www.cnblogs.com/waitrabbit/archive/2010/07/02/1769948.html 博文中直接说“事件是特殊的委托”? 就这水平,还能“说透?”。先搞清楚什么叫“委托”,什么叫“接口”,什么叫“事件”吧! 再比如:《完全水文系列之0xFA:函数背后的臃肿设计哲学》:http://www.cnblogs.com/winter-cn/archive/2010/07/05/1771098.html果然是水的厉害,自己都承认水了。我就不回应了。
不过,可喜的是,在众多水贴之中,确实还出来了几篇高质量的帖子,这是让我非常高兴的,也算是对我写系列博文的一个回报吧。目前看到的主要有以下几个帖子我列举如下,并且加上我的回应(如果有朋友认为自己的帖子质量很高,技术水平很深,我遗漏掉了,请在下面回帖,我会认真拜读并回应),与大家一起讨论、共勉:
1. 李建忠《Metadata是.NET平台的核心灵魂》:http://techvery.com/jzli/2010/07/07/metadata是-net平台的核心灵魂/
这篇博文是目前我看到技术分析最深入的一篇博文,让我很受益。李建忠是我尊敬的一位.NET技术专家,我承认学习.NET就是从拜读李建忠翻译的《.NET框架程序设计》(Jeffrey Richter著,后改名为CLR via C#)一书开始,我也参加过李先生公司去年举办的.NET技术大会,才有前文提到的和Jeffrey Richter大牛的对话。
李建忠在这篇博文中批判了我之前博文中的一个论据“.NET平台中metadata的目的是为了支持反射”。我必须坦白地承认,我之前确实一直认为“metadata就是为了支持反射才创建的。” 经过李建忠博文的分析,我承认在这个问题上认识不到位,观点偏颇。并且非常同意李建忠博文的观点“.NET使用metadata的主要目的是实现组件的高度复用性”。
李建忠博文中还有很多很有价值的技术分析,比如COM的缺点,.NET为什么使用metadata?metadata都在.NET中有哪些具体的应用?特别是“基于逻辑语义的组件复用”和“基于物理实现细节的组件复用”的对比,受益匪浅。
拜读过这篇文章之后,我也承认“基于逻辑语义的组件复用”对于软件开发平台非常重要,不过却有一点要和李建忠商榷,难道“性能就不重要了吗?”,“.NET为了基于逻辑语义的组件复用,就可以大幅度地牺牲软件的性能吗?”,非常希望听到李建忠和网友的继续分析。
2. mikelij《关于.net反射和metadata加载--致Jeffray Zhao等几位和firelong》:http://www.cnblogs.com/mikelij/archive/2010/07/04/1770897.html
mikelij这篇博文涉及到两个观点:
a. 我在前面分析反射时的观点:程序(EXE/DLL)最后都是要加载到内存中运行的,不是光放在硬盘上的——这也是为什么.NET程序占用内存都超多。
b. Jeffray Zhao在前文中反驳我的观点是 "不JIT的话,是不会把整个dll加载到内存中的,而是用多少加载多少,这点已经讨论了很多遍了"
mikelij这篇博文非常细致的分析和数据说理,以及其后面的网友跟帖讨论已经清楚表明了,我在这个问题上的观点是正确的。不过后面有很多跟帖一直在说“任务管理器里面的内存占用很小,所以说明老赵的观点是正确的,即调用哪个方法,就加载哪个方法和它的元数据。用不到的方法,不加载”。
这个里面触及到了一个较深入的话题,即Windows操作系统的内存加载规则和Windows任务管理器的数字含义。 我对这个问题的回应如下:
Windows任务管理器显示的内存大小指的是“工作集working set所使用的内存大小”,也就是当前运行期加载到CPU缓存里面的内存大小。并不是程序运行期间所有加载到内存中的数据。这些数据有可能停留在主存,或者虚拟内存即硬盘上——这就是性能成本,因为这很快会因为调用而遇到page fault, cache missing的问题。只是因为它们当前不被调用,所以没有放在working set缓存中,但是并不表明它们不被加载到主存或者虚拟内存中。
而程序在运行期间,EXE/DLL整个程序集是整体被加载,而不是部分加载——不会因为你只用一个程序集中的方法A,就只加载方法A。而是会将程序集中其他类、方法、元数据都加载进去:mikelij这篇博文充分证明了这一点。
JIT编译是按照方法为单位——即用哪个方法,编译哪个方法。但是加载是按照程序集为单位,不是以方法为单位——老赵在这个问题上的认识让人失望。
谢谢mikelij这篇数据翔实的博文!
3. 老赵《我支持托管运行时(虚拟机):http://blog.zhaojie.me/2010/07/why-i-support-managed-runtime-virtual-machine-based-program.html
实话实话老赵对.NET的很多分析还是有益的,至少让我可以深入再思考一些问题,虽然老赵经常犯一些幼稚的错误(比如前面的将程序集的加载问题,混淆成了JIT问题)。 这篇文章中,老赵说了好几个虚拟机的优点,我整体比较同意,比如实现跨语言,“跨执行环境”等。
但是说虚拟机完全可以实现比native代码更高的性能,则有点过于他信.NET了。特别是举出了一个虚函数的内联优化的天方夜谭的神话,一个虚函数即便循环调用,你也绝对无法去预先判断下一次的调用地址——如果可以判断,这个判断的成本,要远远高于内联所节省的性能成本(而且是每一次都要判断!)。老赵这个真的实现了吗?能不能给出参考地址?
虚拟机可以实现的优化,native代码都可以,而且优化力度要大得多。但是很多native可以做的优化,虚拟机就不行。虚拟机,顾名思义要经过一层间接,我无论如何都想象不出来,间接层可以提升性能?希望老赵能够深入分析一下原理或者给出案例。
【文章原创作者:韩国机房 http://www.558idc.com/kt.html欢迎留下您的宝贵建议】