10

编程小知识之 GC.KeepAlive

 3 years ago
source link: https://blog.csdn.net/tkokof1/article/details/92073033
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

编程小知识之 GC.KeepAlive

本文简述了 C# 中 GC.KeepAlive 函数的实际作用

一直以为 GC.KeepAlive 可以用于使某个托管对象永久的不被垃圾回收(调用该函数后需要主动进行 Free 之类的操作,类似于 GCHandle),但事实证明自己还是犯了望文生义的错误, GC.KeepAlive 虽然确实用于阻止托管对象的垃圾回收,但是在方式方法上和我之前的理解大相径庭.

首先我们来看下 GC.KeepAlive 的代码实现:

// some method attributes here
public static void KeepAlive(object obj)
{
}

你没有看错, GC.KeepAlive 其实是一个 空方法! 他不做任何的实际操作,那么该方法是如何做到阻止托管对象垃圾回收的呢?

我们来看个(简略)代码示例:

class SomeClass 
{
    // disposable data which is inited in SomeClass's constructor
    public SomeOtherClass Value;
    
    ...
}

...

void Method()
{
    var obj = new SomeClass();
    OtherMethod(obj.Value);
}

代码中 Method 方法创建了一个 SomeClass 实例,然后调用了另一个方法 OtherMethod 去处理该实例的 Value 成员.

代码比较简单,逻辑上也没有什么问题,但是如果 SomeClass 定义了终结器,并在终结器中 Dispose 了 Value 成员(关于 Dispose 可以看看之前的一篇相关文章),那就有问题了:

class SomeClass 
{
    // disposable data which is inited in SomeClass's constructor
    public SomeOtherClass Value;
    
    ~SomeClass()
    {
        // NOTE after Dispose, "Value" can not be used anymore
        Value.Dispose();
    }
    
    ...
}

...

void Method()
{
    var obj = new SomeClass();
    OtherMethod(obj.Value);
}

注意一下 Method 方法中对 SomeClass 实例(obj)的使用: OtherMethod(obj.Value), 这里我们是将 obj 的 Value 传递给 OtherMethod 方法,而不是将 obj 传递给 OtherMethod 方法,也就是说在 obj 的 Value 入栈之后(IL调用方法之前的一种操作),调用 OtherMethod 方法之前, obj 已经不可达了(obj.Value 还是可达的,因为栈上还有对他的引用,但是 obj 本身已经不可达了),编译器完全可以在 OtherMethod 方法调用前(或者返回前)执行 obj 的终结器,又由于 obj 的终结器 Dispose 了 obj.Value,于是便会导致 OtherMethod 方法中使用 obj.Value 出错(虽然 obj.Value 引用依旧有效,但是已经被 obj 的终结器 Dispose 了)

怎么办呢? 方法就是使用 GC.KeepAlive :

class SomeClass 
{
    // disposable data which is inited in SomeClass's constructor
    public SomeOtherClass Value;
    
    ~SomeClass()
    {
        // NOTE after Dispose, "Value" can not be used anymore
        Value.Dispose();
    }
    
    ...
}

...

void Method()
{
    var obj = new SomeClass();
    OtherMethod(obj.Value);
    GC.KeepAlive(obj);
}

由于使用了 GC.KeepAlive(obj),编译器便发现 obj 的引用在 OtherMethod 方法调用之后依然可达(因为 GC.KeepAlive 引用了他,尽管 GC.KeepAlive 本身只是个空方法),于是便不会提前释放 obj 了.

总结来说, GC.KeepAlive 只是给编译器的提示(hint),抑制其过早的释放(垃圾回收)某些不可达的托管对象,和 GCHandle 抑制垃圾回收的方式还是大有不同的(GC.KeepAlive 面向编译器,GCHandle 面向程序员).


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK