zoukankan      html  css  js  c++  java
  • 使用.Net中的WeakDictionary — ConditionalWeakTable

    有的时候,我们需要给某些数据添加一些附加信息,一种常用的做法是使用一个Dictionary在填充这些附加信息如:

        var data = new Data();
        var tag = new Tag();

        var dictionary = new Dictionary<Data, Tag>();
        dictionary[data] = tag;

    这么做本身没有什么问题,但是却又一个不小的隐患,那就是在dictionary中保存着了data和tag的引用。当data不再使用的时候,需要将其从dictionary中移除,否则data和tag得不到释放。我们可以用如下代码说明这个问题:(注意,由于Debug模式有时会影响GC,本文代码需行在Release模式下)

        class Tag
        {
            public Tag()
            {
                Console.WriteLine("Create Tag");
            }

            ~Tag()
            {
                Console.WriteLine("Release Tag");
            }
        }

        class Data
        {
            public Data()
            {
                Console.WriteLine("Create Data");
            }

            ~Data()
            {
                Console.WriteLine("Release Data");
            }
        }

        static void Main(string[] args)
        {
            var data = new Data();
            var tag = new Tag();

            var dictionary = new Dictionary<Data, Tag>();
            dictionary[data] = tag;

            data = null;
            GC.Collect();

            Console.WriteLine("After GC");
            Console.ReadLine();
            Console.WriteLine(dictionary);
        }

    从运行结果中可以看出,只有创建的输出,而没有释放的输出。这个就属于资源泄漏了。虽然可以通过手动在dictionary中删除data来实现资源的释放,但是这样就要求我们手动管理对象的生命周期了,而这往往不是一个比较容易做到的事情。

    究其原因,是由于dictionary中保持着强引用、导致GC不会对其进行回收。找到了这个原因后,那就有相应的对策了,那就是改用弱引用来建立关联,这样数据就会被GC释放了。这种观念关系我们通常称为弱字典——WeakDictionary。弱字典也是保存着Key和Value的键值对,它满足如下需求:

    1. 字典中保存着Key的弱引用,即使不释放Key值,也可以被GC回收。
    2. 字典中保存的Value的强引用,Key没有被GC回收前,Value不会被GC回收。
    3. 当Key被GC回收时,关联关系从字典中移除,Value也能被GC回收。

    知道了需求后,接下来就可以对Dictionary进行简单的封装,将其改造成弱字典了。

        static void Main(string[] args)
        {
            var data = new Data();
            var tag = new Tag();

            var dictionary = new Dictionary<WeakReference<Data>, Tag>();
            var key = new WeakReference<Data>(data);
            dictionary[key] = tag;

            data = null;
            GC.Collect();

            Console.WriteLine("After GC");
            Console.ReadLine();
            Console.WriteLine(dictionary);
        }

    运行这段代码后,我们就会发现,Data数据能释放了,但是并不完善,具体体现在如下方面:

    1. Tag保存的仍然是强引用,得不到释放
    2. Key数据并不是Data类型了,存在一个检索的问题,否则无法CRUD。

    对于第一个问题,可以通过一个Timer来定时清理已经释放了的Key来解决;对于第二个问题,则需要在内部通过key来建立Hash表来解决。具体的实现还有点麻烦,也会引入一些新的问题,这里就不继续列举了。

    之所以不继续改造下去了,是因为这里我是在造重复轮子,.Net的BCL中本身就已经提供了一个弱字典——ConditionalWeakTable,通过ConditionalWeakTable改造上述代码如下:

        static void Main(string[] args)
        {
            var data = new Data();
            var tag = new Tag();

            var dictionary = new ConditionalWeakTable<Data, Tag>();
            dictionary.Add(data, tag);

            data = null;
            GC.Collect();

            Console.WriteLine("After GC");
            Console.ReadLine();
            Console.WriteLine(dictionary);
        }

    从运行结果来看,GC结束后,Key和Value都被GC回收掉了(再次强调,需要运行在Release版本下)。

    这个类放置在System.Runtime.CompilerServices下,也很少见到有书里面介绍到它。这里我就简单的介绍一下其接口吧:

        dictionary.Add(data, tag);    //添加    
        dictionary.TryGetValue(data, out tag);    //
    查询
        dictionary.Remove(data);
        //
    删除

    这三个是它比较常见的接口,另外还有两个不大用的接口,这里就不多介绍了。

    最后,简单的试了它的性能,基本上和Dictionary差不多,查询效率还是非常高的,内部应该也是一个Hash表,。

  • 相关阅读:
    springmvc视图解析
    mysql外键是多个id组成的字符串,查询方法
    mysql服务无法启动(1067错误)时数据备份的经验
    springboot(5) freemarker
    springboot(4) Log之Logbak
    springboot(3) junit单元测试
    集合类基础知识
    springboot(2) 数据库操作
    springboot(1)
    linux命令
  • 原文地址:https://www.cnblogs.com/TianFang/p/3546556.html
Copyright © 2011-2022 走看看