对象在内存中是如何存储的?

5
我将尝试解释一下使用.NET框架时对象在内存中的存储方式。以下是一个名为person的类:
```html

public class person{ public string name; public int age; }

```
public class Person
{
    public string name { get; set; }

    public int age { get; set; }
}

我认为,类型为Person的已初始化变量在内存中应该具有以下结构: Person Memory Structure 问题如下:
  • 首先,我对此的理解是否存在任何主要/明显的缺陷?(我几乎可以确定存在缺陷,因为按照我描述的方式处理对象似乎非常低效;特别是name指针指向字符串成员的字符集合的方式)

  • 其次,对于类的值类型成员(例如Age),它们是存储在对象本身内部(与对象相同的内存地址内),还是被分配自己的地址,并且对象随后指向它?(例如我的图表所示)

  • 与上述问题类似,但对于引用类型成员,对象是否持有指向指针的指针?(例如我的图表中的名称指针引用字符集合)

  • 最后,如果我的Person类成员是字段而不是属性,会有什么区别吗?

更新: 根据Sweeper和Tim的答案更新了图表,我认为现在是正确的。 Correct Diagram 注:由于这是托管代码,因此将指针更改为引用。
3个回答

4
在 .net 的世界中,指针指向未经管理的内存,而对象引用指向受管理的对象。对象可能随时被垃圾收集器移动,因此对它们的指针将会在没有警告的情况下被销毁。概念相同,但存在差异,因为您确实可以在不安全的 C# 代码中使用指针。如果您抓取一个指向对象的指针,并且该对象移动了,那么您的指针就指向任意内存空间。然而,对象引用是被保留的。
至于年龄问题。它存储在对象本身中,在结构中占用32位,而不是 int32 的引用/指针。
对于参考类型成员,它保存一个引用,这在概念上与指针相同,占用64位的空间(或32位,具体取决于架构),但也有一些不同,正如我上面提到的。
最后,匿名属性实际上创建了隐藏字段和自动编写的设置和返回该字段的代码。存储相同,但通过属性访问它比直接访问字段有非常小的成本。

4
有一些错误。您对int成员的理解有误,它是值类型,因此没有指针,它包含在Person对象的存储中,并占用4个字节。而name对象引用看起来也不太对,它指向一个String对象。字符串中的字符包含在字符串的存储中,就像int一样。换句话说,String对象的存储大小是可变的,可以根据需要存储字符。
因此,访问Person对象的成员只需要解引用两个指针,这样效率更高。
是的,类型为Person的变量只存储指针。GC在压缩堆时会更新它。
属性在运行时不存在,它是编译器提供的抽象。通常,编译器会为其分配一个字段,除非您自己提供getter和setter方法。因此,您绘图中的“name”和“age”实际上是字段,它们将有不同的名称。

2

我将从最容易回答的问题开始:

如果我的Person类成员是字段而不是属性,会有区别吗?

不会有区别。您代码中的属性只是语法糖。在编译代码时,这些带有 {get; set;} 的属性将被转换为具有getter和setter的字段。

我的理解中是否存在任何重大/明显的缺陷?

是的,我将在回答下面的问题时提到它们。

其次,对于类的值类型成员(例如年龄),它们是存储在对象本身内部(因此在与对象相同的内存地址中),还是被分配自己的地址,然后对象指向它?(就像我的图表所示)

您的第一个说法是正确的。值类型存储在对象内部。在Person对象中没有指向 int的指针。换句话说,你的图表是错误的。

但对于引用类型成员,对象是否保存指向指针的指针?(例如,我的图表中的名称指针引用字符集合)

在这种情况下,char[]是一个引用类型,正如您所注意到的。但它实际上保存了一堆值类型的 char。因此,字符与Age存储在Person中的方式相同。另一方面,如果这是一个string[],则会有指针指向包含指向字符串对象的指针的数组。这也意味着你的图表是错误的。


谢谢您的解释。我已经根据您和Tim的回答添加了一个更新后的图表,我相信现在是正确的了? - KidCode

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