How does GC and IDispose work in C#?

雨燕双飞 提交于 2019-12-04 18:32:09
Marc Gravell

References are not smart pointers. Letting a reference-variable go out of scope, replacing it with another value, and/or setting it with null all do exactly nothing.

This is simply part of the CLI /GC design...

Gargage Collection (GC) will run when needed, and should clean up the managed memory used, and (if a finalizer is provided) any unmanaged resources too. But for deterministic cleanup: that is the entire purpose of IDisposable. It is your job to Dispose() such objects when you have finished with them - either via using, or by handing it to something else which assumes this responsibility (common, for example, with streams/readers etc).

using (StreamReader reader = new StreamReader(myfile)))
{
   ...
}

The GC kicks in when the runtime feels it is necessary.

The basic rule is: when you use an Disposable type (IDispose), then you (as the programmer) should release the resources used by that type as soon as possible, by calling Dispose when you do not longer need to use that type. For instance, when you read a file, you close that file as soon as you've done reading it. (Calling close will also call dispose in this case).

You must call Dispose explicity on any object implementing IDisposable, otherwise your unmanaged resources will not be disposed. If you don't want to call it explicity, then you must override the Finalize method to call the Dispose method - that is why you will see this frequently:

 class MyClass : IDisposable
 {
    ...

    ~MyClass()
    { 
       this.Dispose(false);
    }

    public void Dispose()
    {
       this.Dispose(true);
       GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        { /* dispose managed stuff also */ }

        /* but dispose unmanaged stuff always */
    }
 }

smart_ptr are reference counted. While this allows for deterministic release of their resources when they are no longer referenced by any code, they do have their problems of their own: assigning references always requires the counter to be updated, circular references fail to be released automatically causing memory leaks, the memory manager is invoked more often.

The GC in .NET is a sweeping collector. It starts at any time when it feels that memory should be released (usually triggered by some memory usage condition, but not deterministic) and starts by building a list of all live references in the system (including the ones in CPU registers, nested references etc.). This works since we are in a managed environment where you cannot do pointer arithmetic etc. - the system can track all references. After the list of live references has been built, it basically releases all memory not known to be used anymore. Of course, this is just the basic sketch, for efficiency and management of unmanaged resources there is more to it like object generations, finalizers, etc., but that is not important for the basic understanding of how it works.

The IDisposable interface is used to implement the disposable pattern, which helps when you are working with objects that should be disposed in a deterministic way. The pattern is so that Dispose() is called explicitly when the object is no longer needed, therefore releasing unmanaged resources or closing handles etc., but not releasing its memory. This will be done by the GC later on, but it does not matter that this happens later, because the deterministic release of resources has already been performed.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!