前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.NET GC 精要(七)

.NET GC 精要(七)

作者头像
用户2615200
发布2020-02-17 11:53:46
4610
发布2020-02-17 11:53:46
举报

本文讲述了 .NET GC 的一些细节知识,内容大部分来自于书籍 Under the Hood of .NET Memory Management (注:本文假设你了解 .NET 的基础知识,譬如值类型,引用类型等)

深入
并发执行模式(工作站模式下)的一点细节

之前讲到工作站模式分为 并发非并发 两种执行模式,其中非并发 执行模式比较容易理解,即在整个 GC 流程中应用线程(application thread)是暂停的(非并发执行模式一般适用于单核运行环境).

而对于并发执行模式,细节上则会复杂一些:

并发执行模式下, Gen 0 回收 和 Gen 1 回收 仍然会暂停应用线程,只有在 Full GC(即 Gen 2 回收)时才会有并发行为,并且在整个 GC 流程中一般只会造成应用线程 2 次(短期)暂停.

相关实现上,由于 Full GC 发生时,需要检查回收的内存范围(称为 GC domain)是确定的,所以应用线程可以在 Full GC 的同时于当前 GC domain 以外的内存范围中 申请对象,当然由于内存段的大小限制, 并发执行 GC 时,内存段上还会被设置特殊的内存区域(称为 No Go Zone),如果应用线程的对象申请达到了这个区域,则应用线程仍然会被暂停.

示意图如下:

(可以看到,Full GC 过程中,应用线程仍然可以申请对象(Object L, M, N 和 O))

并发执行模式虽然允许应用线程在 Full GC 过程中继续申请对象,但仍然有不少限制(申请对象不能触及 No Go Zone 区域;申请的对象即使不被引用也不能(被本次 GC )回收(譬如上面示意图中的 Object M)),为了解决这个问题, .NET 4.0 引入了 Background Workstation GC, .NET 4.5 甚至引入了 Background Server GC,有兴趣的朋友可以继续了解.

弱引用(Weak References)

对于一些大内存对象,如果每次使用时都进行创建和释放,则程序效率不高,但如果(创建之后)一直保留引用的话,内存消耗又比较大,使用弱引用可以缓解这个问题:

代码语言:javascript
复制
// load a big data structure
var bigDataObject = new BigDataStructure();

// get a weak reference to it
var weakRef = new WeakReference(bigDataObject, false);
// destroy the strong reference, keeping the weak reference
bigDataObject = null;

// ...

// some time later try and get a strong reference back
bigDataObject = (BigDataStructure)weakRef.Target;
// recreate if weak ref was reclaimed
if (bigDataObject == null)
{
    bigDataObject = new BigDataStructure();
}

.NET 中, 弱引用被分为两类:

  • short weak references

对于 short weak references, GC 如果发现其引用对象没有被遍历流程标记,即会清理其引用对象.

创建方式:

代码语言:javascript
复制
// pass false to WeakReference's constructor
var shortWeakRef = new WeakReference(object, false);
  • long weak references

对于 long weak references, GC 如果发现其引用对象没有被遍历流程标记并且不在 Finalization Queue 中,即会清理其引用对象.

创建方式:

代码语言:javascript
复制
// pass true to WeakReference's constructor
var longWeakRef = new WeakReference(object, true);
GCHandle

GCHandle 可以用于追踪对象堆上的 Object ,一大用处就是支持托管程序和非托管程序之间的互操作.

GCHandle 的类型分为 4 种:

  • Normal 用于追踪一般对象
  • Weak 用于追踪 short weak references
  • Weak 用于追踪 long weak references
  • Pinned 用于固定对象的内存地址

以下是互操作的一段示例代码:

代码语言:javascript
复制
var buffer = new byte[512];
var h = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var ptr = h.AddrOfPinnedObject();

// Call native API and pass buffer
// ...

if (h.IsAllocated) 
{
    h.Free();
}

由于非托管程序一般需要保证对象的内存地址不变,所以我们使用 GCHandleType.Pinned 来固定对象的内存地址,值得一提的是,使用 fixed 语句块也会固定对应的对象内存地址:

代码语言:javascript
复制
unsafe static void Main()
{
    Person p = new Person();
    p.age = 25;
    // Pin p
    fixed (int* a = &p.age)
    {
        // Do something
    }
    // p unpinned
}

之前提到 SOH 为了解决内存碎片问题会进行内存压缩,但是由于其不能调整固定内存地址的对象,所以使用 GCHandleType.Pinned 会对 SOH 的内存压缩流程造成影响,使用时应尽量缩短对象的固定时间.

系列文章完

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-02-09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 深入
    • 并发执行模式(工作站模式下)的一点细节
      • 弱引用(Weak References)
        • GCHandle
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档