C# 内存管理篇---引用数据类型

虽然栈有非常高的性能,但它还没有灵活到可以用于所有的变量。 变量的生存期必须嵌套,在许多情况下,这种要求都过于苛刻。通常我们希望使用一个方法分配内存, 来存储一些数据, 并在方法退出后的很长一段时间内数据仍然可用。 只要是用 new 运算符来请求分配存储空间,就存在这种可能性, 例如,对于所有的引用类型。 此时就要用到托管堆。

托管堆简称堆是处理器的可用内存中的另一个内存区域

要了解堆的工作原理和如何为引用数据类型分配内存, 看看下面的代码

void DoWork()
{
    Customer arabel;
    arabel = new Customer();
    Customer otherCustomer2 = new EnhancedCustomer();
}

在这段代码中,假设存在两个类 Customer 和 EnhancedCustomer 。 EnhancedCustomer类扩展了 Customer 类。

首先,声明一个 Customer 引用 arabel , 在栈上给这个引用分配存储空间, 但这仅仅是一个引用, 而不是实际的 Customer 对象。 arabel 引用占用 4个字节的空间, 足够包含 Customer 对象的存储地址。

然后看下一行代码:

 arabel = new Customer();

这行代码完成了以下操作: 首先,它分配堆上的内存,来存储 Customer 对象(一个真正的对象, 不只是一个地址)。 然后把变量 arabel 的值设置为分配给新 Customer 对象的内存地址。

Customer 实例没有放在栈中,而是在堆中,在这个例子中, 现在还不知道一个 Customer 对象占用多少字节, 但为了方便讨论,假定是 32 个字节。 这32个字节包含了 Customer 的实例字段,和 .NET 用于识别和管理其他类实例的一些信息。

为了在堆上找到存储新 Customer 对象的一个存储位置, .NET 运行库在堆中搜索,选取第一个未使用的且包含 32个字节的连续块。 为了方便讨论, 假定其地址是 200000, arabel 引用占用栈中的 799996~799999位置。 这表示在实例化 arabel 对象前,内存的内容应该是这样的:

给 Customer 对象分配空间后, 内存的内容应该是这样的:

注意:与栈不同, 堆上的内存时向上分配的, 所以空闲空间在已用空间的上面。

下一行代码声明了一个 Customer 引用, 并实例化了一个 Customer 对象。 在这个例子中,用一行代码在栈上为 otherCustomer2 引用分配空间, 同时在堆上为对象分配空间:

Customer otherCustomer2 = new EnhancedCustomer();

该行把栈上的 4个字节分配给 otherCustomer2 引用, 它存储在 799992~799995 位置上, 而otherCustomer2 对象在堆上从 200032 开始向上分配空间。

从这个列子可以看出,建立引用变量的过程要比建立变量的过程更复杂,且不能避免性能上的开销。

实际上,我们对这个例子进行了过分的简化,因为.NET运行库需要保持堆的状态信息,在堆中添加新数据时,这些信息页需要更新。 尽管有这些性能开销,但仍有一种机制,在给变量分配内存时,不会受到栈的限制,把一个引用变量的值赋予另一个相同类型的变量,就有两个变量引用内存中的同一个对象了。 当一个引用变量超出作用域时, 它会从栈中删除,但引用对象的数据仍保留在堆中, 直到程序终止,或垃圾回收器删除它为止, 而只有在该数据不再被任何变量引用时,它才会删除。

这就是引用数据类型的强大之处, 在C#代码中广泛使用了这个特性。 这说明,我们可以对数据的生存期进行非常强大的控制,因为只要保持对数据的引用, 该数据就肯定存在于堆上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值