.NET中一个类(对象)的大小

21

如何确定.NET中的一个类是大型还是小型?是根据它有多少属性或字段、属性/字段的数据类型,还是方法的返回类型、方法的参数、方法的访问修饰符、虚方法等?谢谢...

 class A
{

  string x { get; set; }
}

class B 
{
  int x { get; set; }
}

在这个例子中,如果我像这样实例化类 A 和 B

 A objA = new A();
 B objB = new B();

因为objA拥有一个String属性而objB只有一个Int属性,所以objA是比较大的对象吗?即使我没有为它的属性设置任何值。谢谢。

编辑:澄清我的问题

假设我有一个类:

public class Member
{
    public string MainEmpId { get; set; }
    public string EmpId { get; set; }
}

还有另一个类

public class User
{
    public string AccessLevel { get; set; }
    public string DateActivated { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Mi { get; set; }
    public string Password { get; set; }
    public string UserId { get; set; }
    public string UserName { get; set; }
    public string Active { get; set; }
    public string ProviderName { get; set; }        
    public string ContactPerson { get; set; }
    public string Relation { get; set; }
    public string Landline { get; set; }
    public string MobileNo { get; set; }
    public string Complaint { get; set; }
    public string Remarks { get; set; }
    public string Reason { get; set; }
    public string RoomType { get; set; }
}

如果我像这样实例化它

  Member A = new Member();
  User B = new User()

对象A是否比对象B更大?我知道这是个奇怪的问题,但我相信每个对象的实例都会占用内存空间。


4
这是一个含糊的问题……当你说“类大小”时,你是指包括该类指针所指向的所有引用/对象所使用的内存,还是仅仅是该类本身和指针自身使用的内存? - Nick Craver
你是指内存空间吗?如果是的话,我认为你无法真正测量它,也不应该太在意。在我看来。 - Lucas Jones
@Nick Craver,我指的是对象在实例化或使用时在内存中的大小。 - CSharpNoob
@stakx:在这个问题经历了所有的编辑之后,我的回答变得毫无意义。抱歉删除了你的链接(-: - Oren A
听起来你应该重新考虑使用 struct - Steven Sudit
显示剩余2条评论
6个回答

46

一个类实例的大小由以下因素决定:

  • 实例中实际存储的数据量
  • 值之间所需的填充
  • 内存管理所使用的额外内部数据

因此,通常包含字符串属性的类需要(在32位系统上):

  • 8个字节的内部数据
  • 4个字节的字符串引用
  • 4个字节的未使用空间(用于达到内存管理器可以处理的最小16个字节)

通常包含整数属性的类需要:

  • 8个字节的内部数据
  • 4个字节的整数值
  • 4个字节的未使用空间(用于达到内存管理器可以处理的最小16个字节)

从这里可以看出,在类中,字符串和整数属性占用相同的空间,因此在第一个例子中它们将使用相同的内存。

当然,字符串属性的值是另一回事,因为它可能指向堆上的字符串对象,但那是一个单独的对象,不是指向它的类的一部分。

对于更复杂的类,填充会发挥作用。例如,包含布尔值和字符串属性的类将使用:

  • 8个字节的内部数据
  • 1个字节的布尔值
  • 3个字节的填充,以达到偶数4字节边界
  • 4个字节的字符串引用

请注意,这些只是类的内存布局示例。确切的布局因框架版本、CLR实现以及是32位还是64位应用程序而异。由于程序可以在32位或64位系统上运行,因此内存布局甚至对编译器来说都是未知的,它是在代码JIT之前在执行时决定的。


那么,这是否意味着一个空类(没有方法和数据成员)的大小为12个字节? - Manish Basantani
2
@Amby:不,它的大小将为16字节。在32位系统上,它将包含8字节的内部数据和8字节的未使用空间,在64位系统上,它将包含16字节的内部数据和没有未使用空间。 - Guffa
如果我没错的话,一个空类有16个字节,其中8个字节是同步对象(浪费的空间),另外8个字节是类型指针。 - Mihail Georgescu

10

通常情况下,一个类的大小与它有多少个实例(非静态)字段有关,而与它们的值无关;类的内存最小为12字节,在32位系统上,引用类型的字段为4字节,在64位系统上为8字节。其他字段可能会被填充到字边界上,因此一个有四个byte字段的类在内存中实际上可能占据了四倍的4字节空间。但这一切都取决于运行时。

不要忘记那些可能隐藏在自动属性声明中的字段。由于它们在内部由一个字段支持,所以它们会增加类的大小:

public string MyProperty
{ get; set; }
请注意,以下属性对类大小没有影响,因为它没有由字段支持:
public bool IsValid
{ get { return true; } }
为了了解类或结构实例在内存中的大小: 在类上应用[StructLayout(LayoutKind.Sequential)]属性,并在类型或实例上调用Marshal.SizeOf()
[StructLayout(LayoutKind.Sequential)]
public class MyClass
{
    public int myField0;
    public int myField1;
}

int sizeInBytes = Marshal.SizeOf(typeof(MyClass));

然而,由于运行时可以自行安排类在内存中的布局方式,除非应用 StructLayoutAttribute,否则实例所使用的实际内存可能会有所不同。


当字段为空时,在内存中甚至没有分配一个字节吗? - CSharpNoob
仅计算字段数量,不考虑它们的值。 - Daniel A.A. Pelsmaeker
1
@CSharpNoob:如果一个字段是引用类型并且被设置为null,它仍然会占用指向实例所需的字节数。只是此时它没有指向任何实例。在上面的示例中,属性没有后备字段,因此不需要额外的空间。 - Steven Sudit

2

有一个在Github上的项目叫做dotnetex,它使用一些魔法来显示类或对象的大小。

使用很简单:

GCex.SizeOf<Object>();    // size of the type
GCEx.SizeOf(someObject);  // size of the object;

在内部,它使用了一些神奇的技巧。
要计算类型大小,它将方法表指针强制转换为内部的MethodTableInfo结构,并像这样使用其Size属性:

public static unsafe Int32 SizeOf<T>()
{
    return ((MethodTableInfo *)(typeof(T).TypeHandle.Value.ToPointer()))->Size;
}

要计算对象的大小,它使用了相当难以获得的真正黑暗的魔法 :) 请查看代码

2

2

0

当我们说“类大小”时,我会认为它指的是类有多少成员以及类的复杂程度。

然而,你的问题是在创建类实例时所需的内存大小。我们无法确定确切的大小,因为 .Net 框架将底层内存管理保留并远离我们(这是一件好事)。即使我们现在有了正确的大小,这个值也可能永远不正确。无论如何,我们可以确定以下内容将占用一些内存空间:

  1. 实例变量。
  2. 自动属性。

因此,可以说 User 类将比 Member 类占用更多的内存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接