李建忠


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

导航

关于

标签

每月存档

最新留言

广告

 

最近在讲授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,并不代表托管对象没有大小。那么,托管对象的大小如何计算呢?

 

 

预知详情,下回分解:)

 

打印 | 张贴于 2005-07-01 17:52:00 | Tag:C++/CLI

留言反馈

#回复: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
to 李建忠
"由于alignment的作用,最后两个Byte字段后面要再填充两个bytes的空间",这句我没有明白,alignment是为了什么?那填充又为什么是填2个bytes呢?
2007-11-21 11:58:00 | [匿名:安羽]
#回复: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
写的不错!!!写的不错!!!写的不错!!!写的不错!!!写的不错!!!
2007-08-31 23:04:00 | [匿名:http://www.1ju.org]
#回复: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
写的不错!!!
2007-08-31 23:03:00 | [匿名:http://www.1ju.org]
#回复: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
sizeof(*s) 汗死,这样都可以
唉,我看不懂楼主写的得到对象大小的..谁能解释解释
2007-04-29 14:42:00 | [匿名:Need]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
楼上路过的大虾:你完全不懂编程。赶紧好好学习吧。
2006-09-13 17:27:00 | [匿名:BS]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
如果把sizeof(s) 改为 sizeof(*s)就知道堆中的大小....
2006-08-02 12:16:00 | [匿名:路过]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
在g++中,可以这样知道堆中对象的大小,但在C++\CLI中,的托管堆上得到大小不知有没有简便的方法,刚才看到有位仁兄给了很长的代码.......

#include <cstdio>
struct MyValue
{
short data1;

int data2;

char* data3;

char data4;
};

int main()
{
MyValue *s = new MyValue;
printf("%d\n",sizeof(s));
delete[] s;
return 0;
}
2006-08-02 12:13:00 | [匿名:路过]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
如何计算???如何计算??很期待呢!
2006-05-26 14:26:00 | [匿名:yesoce]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
To Ninputer,

完全同意!高级语言存在的正是其“抽象”意义:) 但是探讨“under the hood”是一部分程序员的责任,否则我们就成了金字塔下永远享受“高级抽象”的程序员了:)

To Flier Lu
你们inside IL & CLR小组的文章真的很不错,希望今后看到更多。

To zz,
抱歉,一段时间spam太多,可能没有仔细分拣--我仔细找了。希望今后发信到jianzhong.lee (#) gmail.com。您的问题我在下一篇帖子里仔细回答。谢谢!
2005-09-23 22:11:00 | [匿名:李建忠]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
To sunmast,
Marshal.SizeOf不是用来做这个的,返回的是marshal后的unmanaged type的大小——前提是这个类型能够被marshal到unmanaged世界中,否则还会报告错误。

To KevinLee,
您的做法真是非常好,干净利落,也是我即将在下文中谈的其中一个做法。

To rIPPER,
.NET抽象目标带来的后果是“系统自身对对象的size了如指掌,但是做基于.NET的应用开发则无须了解任何对象的size”。 但是就像“理解操作系统、内存管理等原理,能够让我们比平常的使用者更好地使用软件”一样,“理解对象的size,进而理解对象的layout,理解对象各种行为的成本”可以让我们更好地驾驭CLR,从而写出更正确、更快的程序来。

2005-09-23 21:41:00 | [匿名:李建忠]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
李老建你好,我发给你的邮件里有一些问想请教你,你没有回信,我就把问题写在这里希望你解答一下吧!
如果各位网友看见了,也不要见笑,不耻下问嘛!!
李老师您好!
我是C#的初学者,我以前是搞C++的,自从NET发布以来C#语言以语法简洁,功能强大,有一流的IDE环境的
支持让我写C#代码很容易,特别是在VS2005中写代码与VBNET一样方便。基于对C#的热爱同时也对C#寄以很
大的希望.可能是对C#的希望太大了,由此产生了许多许多疑问,这些疑问作为一个初学者来说希望得到一
个权威和肯定的回答!!以下是我的问题
先看在安装VS.NET2003中对C#的简介:
"Microsoft C# .NET 是一种现代的、面向对象的类型安全语言。程序员可以使用它快速生成用于新的
Microsoft .NET 平台的各种应用程序。C#(读作“C sharp”)旨在帮助 C++ 程序员快速进行开发,
同时又保留了 C 和 C++ 所见长的功能和控制力。"(有些网友问C++的控制力有那些?我想可能会举出许多!)
还有:在MS的宣传及其它资料及书上都把C#说是为NET定身量作的,开发NET程序C#是首选,
当然我不怀疑这些说法。我在WEBCAST中,听你讲的C++/CLI中,他说C++/CLI是NET中功能最强大的语
言,它对NET的操纵能力比其它语言要强的多,如对元数据的操作等等,我也是这么认为.每个程序员都希望
自已使用的语言工具有很强大的功能,一个团队为了大家交流都认同大家用同一种语言,这就意味着一个团队
要用一种语言实现几乎所有的开发工作。当然以前的C++能满足我们团队的要求,因为C++功能太强大了,覆盖面
太多了,我们都喜欢。再从C#的宣传中,说C#是具有C++所见长的控制力和行动力的,这正合我们的意,又有
C++所具有的功能,又有这么简洁(不是简单)的语法,又有这么好的IDE帮助我们快速编写代码,何乐而不
为呢?对C++/CLI的了解后,对比一下现在C#的功能,这就让我们举其不
定了,我热爱的C#难道MS对它失去了信心?精力是不是将要放在C++/CLI上面?有了VBNET为何又有C#(功能
是一样的,为什么要分散精力?)?为什么C#的进步或功能的加强不是很大(从C#1到C#2)?在MS,C#有没有
一下发展计划,MS要把C#打造成一个什么样的语言?如果C#发展为大而全的开发语言有何不可??进入64位时代后是不是C#也要
发展成象32时代中的VB,只是作点简单,快速,集成软件等的应用,关键的应用还得用C++???.....很多的疑问了!!
我现在在用C++我有必要转向C#吗?。。。。
李老师帮助一下!!
谢谢!
祝工作顺利!
2005-07-21 09:30:00 | [匿名:zz]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
你可以搜一下我以前贴过的一篇文章:《CLR 中类型字段的运行时内存布局 (Layout) 原理浅析》
2005-07-08 21:56:00 | [匿名:Flier Lu]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
在.NET中,抽象的力量超过底层存在的力量。对于虚函数,其在设计中的含义远重要于其实现的原理。这也是语言高级的一大特点。
2005-07-06 09:01:00 | [匿名:Ninputer]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
To rIPPER:
在C++中我们是会经常用到Sizeof的,当然.Net我不熟悉。

比如:
MyStruct Foo;
ZeroyMemory(&Foo, sizeof(MyStruct));

当然,基本上,涉及到sizeof的操作基本上都会牵扯到指针和内存操作。
2005-07-03 22:00:00 | [匿名:KevinLee]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
通常在开发中什么情况下面需要知道对象的大小呢?可以给一两个场景么?
2005-07-03 15:14:00 | [匿名:rIPPER]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
inline DWORD Object::GetSiz()
{
// mask the alignment bits because this methos is called during GC
MethodTable* mT = (MethodTable*)((size_t)GetMethodTable()&~3);
return mT->GetBaseSize() + (GetNumComponents() * mT->GetComponentSize());
}

HRESULT ProfToEEInterfaceImpl::GetObjectSize(ObjectID objectId, ULONG *pcSize)
{
// Get the object pointer
Object *pObj = reinterpret_cast<Object *>(objectId);

// Get the size
if (pcSize)
*pcSize = (ULONG) pObj->GetSize();

// Indicate success
return (S_OK);
}

COM_METHOD CorProfInfo::GetObjectSize(
/* [in] */ ObjectID objectId,
/* [out] */ ULONG *pcSize)
{
if (objectId == NULL)
return (E_INVALIDARG);

return (g_pProfToEEInterface->GetObjectSize(objectId, pcSize));
}
2005-07-02 14:42:00 | [匿名:KevinLee]
#re: Under The Hood in .NET——使用C++/CLI实现托管版的sizeof (引言) 编辑
>>> 那么,托管对象的大小如何计算呢?
Marshal.SizeOf...
2005-07-01 20:34:00 | [匿名:sunmast]
对不起,目前本随笔不允许发表新评论.

Powered by: Joycode.MVC引擎 0.5.2.0