李建忠


C#/.NET, C++/CLI 培训/咨询
随笔 - 13, 评论 - 266, 引用 - 26

导航

关于

标签

每月存档

最新留言

广告

Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言)

最近在讲授C#培训课程中谈到托管对象内存的布局(Layout)和托管对象大小的问题时,一位学员问了这样一个问题 “既然每一个对象都是有大小的,为什么C#语言没有提供sizeof操作符?”

 

当然这个问题准确的问法应该是“为什么C#语言的sizeof操作符不能应用于class上?”,因为实际上C#是提供有sizeof操作符的,只不过只能在unsafe代码中对struct类型使用。而更进一步的问题便是“如何获取托管对象的大小?”

 

 

这个问题表面看起来并不大,但是仔细思考起来却很有意思,如果真的深究下去,恐怕需要把.NET挖个底朝天。正好我在SSCLIShared Source Common Language Infrastructure)上有过一段研究心得,于是便打算写一组文章来探讨这个问题,希望能够满足那些喜欢 Under The Hood”的朋友的胃口。

 

有关“Under The Hood”的解释,可以参见孙展波先生这里的一篇bloghttp://blog.joycode.com/zhanbos/archive/2004/06/10/24208.aspx.当然也可以到这里http://blogs.msdn.com/matt_pietrek/ 寻找“Under The Hood”的创始人Matt Pietrek

 

 

在回答这些问题之前,首先要谈谈“既然每一个对象都是有大小的”这句话,因为我发现并不是每一个程序员对此都有非常清晰的理解。C++程序员一般对此有比较好的观念,但是很多C#程序员、VB.NET程序员,Java程序员对这样的观念很淡薄,甚至许多“搞了很长时间面向对象”的程序员压根就不知道对象还有所谓大小这回事,这也是我在讲授C#/VB.NET培训课程时要着力强调它的原因。

 

因为要搞清楚对象的大小,必须搞清楚对象的Layout。而只有对对象的Layout非常清楚,才能彻底理清栈、托管堆、值类型、引用类型、参数传递,虚方法调用(多态)、垃圾收集。。。。等等这些编程中的核心问题。我以前在面试C#/.NET开发人员时,就把“一个class中定义有一个int、一个byte、和一个string,那么这个class的实例对象有多大”这样的问题作为对一个.NET程序员的“终极测试题”——如果这个问题回答得令人满意,基本上再问其他问题都显得多余。我甚至偏执地认为,只有“清楚了解对象Layout的程序员”才是“真正懂面向对象的程序员”。

 

言归正传,现在开始具体谈谈如何来获取一个托管对象的大小——注意这里说的是“一个托管对象的大小”而不是“一个类型的大小”,后面我会解释为什么要这样说。在这之前,先来看看C#C++/CLI目前的sizeof操作符在值类型上的行为,它对后面探讨托管对象的sizeof多少有一点帮助。

 

 

 C#C++/CLI中的sizeof操作符

 

C#C++/CLI都提供了sizeof操作符,比如在C++/CLIC#不支持在一个含有引用类型字段的值类型上使用sizeof操作符)中,我们可以这样来计算一个值类型的大小:

 

value struct MyValue { Byte data1; int data2; String^ data3; Byte data4; }; int main() { int size=sizeof(MyValue); Console::WriteLine(size); }

 

这里的输出结果为12,单位为bytes。和ISO-C++class的结果不同。其原因为CLR对值类型的Layout做了优化调整,事实上对于上面的声明顺序,CLR在内存中调整如下:

 

value struct MyValue { String^ data3; // 4bytes 指针 int data2; // 4bytes 整数 Byte data1; //1byte 整数 Byte data4;   //1byte 整数 };

由于alignment的作用,最后两个Byte字段后面要再填充两个bytes的空间,因此结果为4+4+1+1+2=12——这种做法显然是比较优化的算法,能够最大程度地节省一个栈对象的内存开销(ISO-C++按字段声明顺序来排列Layout,结果为16)。

 

顺便提一句,从编译出的元数据来看,编译器自动在MyValue上应用了如下Attribute

[StructLayout(LayoutKind::Sequential)]   // 顺序排列Layout

 

但是CLR并没有理会这一Attribute,而是自我主张使用了如下Attribute

[StructLayout(LayoutKind:: Auto)]     // 自动排列Layout,优化方案

 

 

不过sizeof只能在值类型上使用,而不能在引用类型上使用,计算的也是这些值类型在栈上分配的“裸对象”的成本——而非box到托管堆上的多态对象。

 

 

C#C++/CLI不允许在托管类型上使用sizeof,并不代表托管对象没有大小。那么,托管对象的大小如何计算呢?

 

 

预知详情,下回分解:)

 

posted on 2005-07-01 17:52:00 by lijianzhong  评论(21) 阅读(5841)

Powered by: Joycode.MVC引擎 0.5.2.0