为什么没有引用计数+;C#中的垃圾收集? 我来自C++背景,我和C一直工作了一年左右。和其他许多人一样,我对语言中为什么没有内置确定性资源管理感到困惑。我们没有确定性析构函数,而是使用dispose模式。通过他们的代码传播可识别的癌症是否值得努力

为什么没有引用计数+;C#中的垃圾收集? 我来自C++背景,我和C一直工作了一年左右。和其他许多人一样,我对语言中为什么没有内置确定性资源管理感到困惑。我们没有确定性析构函数,而是使用dispose模式。通过他们的代码传播可识别的癌症是否值得努力,c#,garbage-collection,reference-counting,C#,Garbage Collection,Reference Counting,在我偏爱C++的大脑中,使用带有确定性析构函数的引用计数智能指针似乎是垃圾收集器的一大进步,垃圾收集器要求您实现IDisposable和调用dispose来清理非内存资源。不可否认,我不是很聪明。。。所以我问这个纯粹是为了更好地理解为什么事情是这样的 如果C#被修改为: 对象被引用计数。当对象的引用计数变为零时,将确定地对该对象调用资源清理方法,然后将该对象标记为垃圾收集。垃圾收集发生在将来某个不确定的时间,此时内存被回收。在这种情况下,您不必实现IDisposable或记住调用Dispose。

在我偏爱C++的大脑中,使用带有确定性析构函数的引用计数智能指针似乎是垃圾收集器的一大进步,垃圾收集器要求您实现IDisposable和调用dispose来清理非内存资源。不可否认,我不是很聪明。。。所以我问这个纯粹是为了更好地理解为什么事情是这样的

如果C#被修改为:

对象被引用计数。当对象的引用计数变为零时,将确定地对该对象调用资源清理方法,然后将该对象标记为垃圾收集。垃圾收集发生在将来某个不确定的时间,此时内存被回收。在这种情况下,您不必实现IDisposable或记住调用Dispose。如果要释放非内存资源,只需实现资源清理功能

  • 为什么这是个坏主意
  • 这会破坏垃圾收集器的用途吗
  • 实施这样的事情可行吗
编辑: 从目前的评论来看,这是一个坏主意,因为

  • GC在没有引用计数的情况下速度更快
  • 对象图中循环的处理问题
  • 我认为第一个是正确的,但第二个是使用弱引用很容易处理的

    因此,速度优化是否超过了您的缺点:

  • 可能无法及时释放非内存资源
  • 可能会过早释放非内存资源

  • 如果您的资源清理机制是确定性的,并且内置于语言中,则可以消除这些可能性。

    垃圾收集器不要求您为定义的每个类/类型编写Dispose方法。当您需要显式地进行清理时,您只定义一个;当您明确分配了本机资源时。大多数情况下,即使只对对象执行new()之类的操作,GC也只是回收内存

    GC进行引用计数-但是它通过查找哪些对象是“可访问的”(
    Ref Count>0
    以不同的方式进行计数。。。它只是不以整数计数器的方式进行。收集无法访问的对象(
    Ref Count=0
    )。这样,运行时就不必每次分配或释放对象时都进行内务管理/更新表。。。应该更快

    < C++(确定性)和C(非确定性)之间的唯一主要区别是对象将被清理。你无法预测一个物体在C#中被收集的确切时刻


    第无数个插件:如果你真的对GC的工作方式感兴趣,我建议你阅读Jeffrey Richter关于GC的独立章节。

    引用计数在C#中尝试过。我相信,发布Rotor(CLR的一个参考实现,其源代码可用)的人做了基于引用计数的GC,只是为了看看它与下一代的GC相比会如何。结果令人惊讶——“股票”GC速度快得多,甚至一点都不好笑。我不记得是在哪里听到的,我想那是汉斯蒙特的播客之一。如果你想看到C++的基本上与C语言的性能比较被压碎——谷歌Raymond Chen的中文词典应用程序。他做了C++版本,然后Rico Mariani做了一个C版本。我认为雷蒙德需要6次迭代来最终打败C版本,但是到那时,他必须放弃所有优秀的C++对象,并进入Win32 API级别。整件事都变成了一场表演秀。同时,C#程序只优化了一次,最终看起来还是一个不错的OO项目

    我对垃圾收集有所了解。这里是一个简短的总结,因为完整的解释超出了这个问题的范围

    .NET使用复制和压缩分代垃圾收集器。这比引用计数更高级,并且能够收集直接或通过链引用自身的对象


    引用计数不会收集周期。引用计数也具有较低的吞吐量(总体较慢),但与跟踪收集器相比,具有更快的暂停(最大暂停较小)的好处。

    当用户不显式调用Dispose时,实现IDisposable的对象还必须实现GC调用的终结器-请参阅

    IDisposable的全部要点是GC在某个不确定的时间运行,而您实现IDisposable是因为您拥有一个有价值的资源,并且希望在确定的时间释放它

    所以你的建议在IDisposable方面不会有任何改变

    编辑:

    对不起。没有正确阅读您的提案:-(


    Wikipedia对

    有一个简单的解释,这里有很多问题。首先,你需要区分释放托管内存和清理其他资源。前者可能非常快,而后者可能非常慢。在.NET中,两者是分开的,这允许更快地清理托管内存。这也是这意味着,只有在需要清理托管内存以外的内容时,才应该实现Dispose/Finalizer

    NET采用了标记和扫描技术,它遍历堆寻找对象的根。根实例在垃圾收集中幸存下来。其他一切都可以通过回收内存来清理。GC必须时不时地压缩内存,但要分开
    public abstract class BaseCriticalResource : IDiposable {
        ~ BaseCriticalResource () {
            Dispose(false);
        }
    
        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this); // No need to call finalizer now
        }
    
        protected virtual void Dispose(bool disposing) { }
    }
    
    public class ComFileCritical : BaseCriticalResource {
    
        private IntPtr nativeResource;
    
        protected override Dispose(bool disposing) {
            // free native resources if there are any.
            if (nativeResource != IntPtr.Zero) {
                ComCallToFreeUnmangedPointer(nativeResource);
                nativeResource = IntPtr.Zero;
            }
        }
    }
      
    
    using (ComFileCritical fileResource = new ComFileCritical()) {
        // Some actions on fileResource
    }
    
    // fileResource's critical resources freed at this point
    
    // Initialize some resource.
    try {
        // Use the resource.
    }
    finally {
        // Clean-up.
        // This code will always run, whether there was an exception or not.
    }
    
    using (Foo foo = new Foo()) {
        // Do something with foo.
    }
    // foo.Dispose() will be called afterwards, even if there
    // was an exception.