托管堆和C++使用的堆不同,它在垃圾回收器的控制下工作,与传统的堆相比有很显著的优势。
void DoWork()
{
Customer arabel;
arabel = new Customer();
Customer otherCustomer2 = new EnhancedCustomer();
}
arabel = new Customer();
首先,它分配堆上的内存,以存储Customer对象(一个真正的对象,不只是一个地址)。
然后把变量arabel的值设置为分配给新Customer对象的内存地址(它还调用合适的Customer()构造函数初始化类实例中的字段,但此处我们不必担心这部分)。
值类型和引用数据类型
垃圾回收
:::info 托管堆的工作方式非常类似于栈,对象会在内存中一个挨一个地放置,这样就很容易使用指向下一个空闲存储单元的堆指针来确定下一个对象的位置。 :::
在垃圾回收器运行时,它会从堆中删除不再引用的所有对象。
- 垃圾回收器在引用的根表中找到所有引用的对象,接着在引用的对象树中查找。
- 在完成删除操作后,堆会立即把对象分散开来,与已经释放的内存混合在一起
垃圾回收器的特性
只要它释放了能释放的所有对象,就会把其他对象移动回堆的端部,再次形成一个连续的内存块。
堆可以继续像栈那样确定在什么地方存储新对象。
在移动对象时,这些对象的所有引用都需要用正确的新地址来更新,但垃圾回收器也会处理更新问题。
堆的移动
堆的第一部分称为第0代。
- 创建新的对象时,会把他们移动到堆的第0代(存放了最新的对象)。
- 对象会继续放在这个部分,直到垃圾回收过程第一次进行回收。
- 这个清理过程之后仍保留的对象会被压缩,然后移动到堆的下一部分上——第1代对应的部分。
- 此时,第0代对应的部分为空,所有的新对象都再次放在这一部分上。
- 在垃圾回收过程中遗留下来的旧对象放在第1代对应的部分上。
- 老对象的这种移动会再次发生。接着重复下一次回收过程。
:::info 这意味着,第1代中在垃圾回收过程中遗留下来的对象会移动到堆的第2代,位于第0代的对象会移动到第1代,第0代仍用于放置新对象。
在给对象分配内存空间时,如果超出了第0代对应的部分的容量,或者调用了GC.Collect()方法,就会进行垃圾回收。 :::
System.GC类
垃圾回收器在.NET运行库确定需要进行垃圾回收时运行。可以调用System.GC.Collect()方法,强迫垃圾回收器在代码的某个地方运行。
System.GC类:是一个表示垃圾回收器的.NET类,Collect()方法启动一个垃圾回收过程。
使用范围:代码中有大量的对象刚刚取消引用,就适合调用垃圾回收器。
垃圾回收器的逻辑不能保证在一次垃圾收集过程中,所有未引用的对象都从堆中删除。
在测试过程中运行GC是很有用的。这样,就可以看到应该回收的对象仍然未回收而导致的内存泄漏。
为了利用包含大量内存的硬件,垃圾回收过程添加了GCSettings.LatencyMode属性。 把这个属性设置为GCLatencyMode枚举的一个值,可以控制垃圾回收器进行回收的方式。
成员 | 说明 |
---|---|
Batch | 禁用并发设置,把垃圾回收设置为最大吞吐量。这 会重写配置设置 |
Interact ive | 工作站的默认行为。它使用垃圾回收并发设置,平 衡吞吐量和响应 |
LowLatency | 保守的垃圾回收。只有系统存在内存压力时,才进 行完整的回收。只应用于较短时间,执行特定的操作 |
SustainedLowLatency | 只有系统存在内存压力时,才进行完整的内存块回 收 |
NoGCRegion | .NET 4.6新增成员。对于GCSettings,这是一个只读属性。可以在代码块中调用GC.TryStartNoGC-Region和EndNoGCRegion来设置它。 调用TryStartNoGCRegion,定义需要可用的、GC试图访问的内存大小。成功调用TryStartNoGCRegion后,指定不应运行的垃圾回收器,直到调用EndNoGCRegion为止 |