.NET 内存管理必备知识
小型对象如何处理
小型对象是被分配在小型对象堆 SOH 上的。SOH 有 3 代,分别是:第 0 代,第 1 代,第 2 代。对象根据寿命向上移动。将新对象放在 Gen 0 上。当第 0 代充满时,.NET 垃圾收集器会处理不需要的对象,并将其它内容移至第 1 代上,如果第 1 代充满了那么垃圾回收会再次运行处理不需要的对象,并将其它内容移至第 2 代上。那么当第 2 代充满时会发生垃圾回收完全运行。将清除不需要的第 2 代对象,并将第 1 代对象移动到第 2 代上,然后将第 0 代对象移动到第 1 代上,最后清除所有未引用内容。每次运行垃圾回收后会压缩受影响的堆,将仍然在使用的内存放置在一起。这种方法可以确保高效运行,并且耗时的压缩过程只在必要时发生。
>Tip:如果在第 2 代中看到大量内存,表明内存被保留了很长时间,可能存在内存问题,我们需要使用内存分析工具去分析它。
较大对象如何处理
大于 85KB 的对象被分配在大对象堆 LOH。由于复制大块内存的开销,不会进行被压缩。发生完整垃圾回收时未使用的对象的地址范围将记录在可用空间分配表中。当分配了新对象后会在此可用空间表中检查足以容纳这个对象的地址范围。如果存在就将对象分配到那里,如果不存在就将对象分配到下一个可用空间中。由于对象不可能知道地址范围的确切大小,所以对象与对象之间总是留着小块内存这样就出现了碎片。那么如果这些块小于 85KB 就根本没有可重用性。所以随着分配需求的增加,即使碎片空间可用也会保留新段。当需要分配大对象时.NET 会倾向将对象添加到末尾,而不是运行第二代垃圾回收,这样做虽然对性能有好处,但是会导致内存碎片。
垃圾收集器可以在不同的模式下运行以优化性能
.NET 通过为垃圾回收提供多种模式来解决权衡性能与堆效率的问题。模式如下:
1. 工作站模式:提供了最大响应速减少由于 GC 造成的暂停。可以作为并发和非并发运行,默认为并发,为垃圾回收使用单独线程,因此应用程序可以垃圾回收时继续运行;
2. 服务器模式:服务器环境提供最大吞吐、可伸缩性和性能。服务器模式下段大小和阈值比工作站模式要大很多,反映了对服务器更高的要求。它在多个线程上并行运行垃圾回收,为每个处理器分配一个独立的 SOH 和 LOH 防止线程相互干扰。
.NET 框架提供了一种交叉引用机制,对象之间仍然可以在堆之间相互引用。但由于应用程序响应能力不是服务器模式的直接目标,所以在垃圾回收期间所有应用程序线程都会被挂起。
引用不足会在性能和内存效率之间折衷
弱对象引用可以保留对象,同时在垃圾回收需要时可以收集对象。是代码性能和内存效率之间的折衷。创建对象需要占用 CPU 时间,但保持加载状态需要占用内存。
对象固定可以在托管和非托管代码之间传递引用
.NET 使用 GCHandle 结构来跟踪堆对象。GCHandle 可用于在托管域和非托管域之间传递对象引用,.NET 维护一个 GCHandles 表以实现此目的。CHandle 有四种类型,其中固定类型用于将对象固定在内存中特定地址。但是对象固定的主要问题是可能导致 SOH 碎片化。如果将对象固定在垃圾回收期间,那么该对象将无法重定位。使用固定的方式会降低压缩效率并在堆中留下间隙。避免这种情况的最佳策略是在短时间内锁定然后释放。
评论