2.缓存带来的潜在威胁“内存泄漏”


很多开发者喜欢用缓存来提升访问速度和减轻数据库压力,但是盲目的缓存存在内存泄漏的风险!你可知道吗?下面我举个例子说明下常见问题:

大多数人喜欢用Dictionary做缓存,那我也那这个说明问题吧!

//定义实体类woman

  public class Woman
    {
        //记录内存中Woman的数量
        public static int Count;
        public string Name { get; set; }
        public string Age { get; set; }
        public string[] Strs { get; set; }
        public Woman()
        {
            Strs = new string[500];
            Interlocked.Increment(ref Count);
        }
        ~Woman()
        {
            Interlocked.Decrement(ref Count);
        }
    }
  class Program
    {

        static  Dictionary<int, Woman> dics = new Dictionary<int, Woman>();
        static void Main(string[] args)
        {
       //模拟记录缓存数据
for (int i = 0; i < 100; i++) { if (dics.ContainsKey(i)) { continue; } else { Woman w = new Woman(); w.Name = "范冰冰"; dics.Add(i, w); } } Console.WriteLine(string.Format("缓存完成,按任意键GC回收内存中的woman")); Console.ReadKey(); GC.Collect(); Console.WriteLine(string.Format("回收完成,内存中woman对象数量是{0}个",Woman.Count)); Console.ReadKey(); } }

 继续增加缓存数量级从100改到1000000时就会出现“System.OutOfMemoryException"内存爆掉了

所以这个问题很常见啊,那怎么解决呢???

网上很多给出很多解决办法:

  • 删除一段时间未使用的缓存。
  • 限制缓存大小。
  • 使用WeakReference来保存缓存的对象。这依赖于垃圾收集器来决定何时清除缓存,但这可能不是一个坏主意。GC会将仍在使用的对象推广到更高的世代,以使它们的保存时间更长。这意味着经常使用的对象将在缓存中停留更长时间。

前2种我就不说了,重点用第3种,弱引用,来处理这个问题,为啥用它呢?

原因有一:弱引用对象(woman)即使被其他对象引用也可以GC回收掉;

原因有二:在我们没有限制缓存容器Dictionary的大小和定时删除一段时间未使用的缓存的情况下,整个应用程序的内存都是GC在自动管理,如当发现内存不足时,GC会自动清理,也就是我做了甩手掌柜了;

实践一下吧:运行下面的代码

 class Program
    {

        static Dictionary dics = new Dictionary();
        static void Main(string[] args)
        {
            for (int i = 0; i < 1000000; i++)
            {
                if (dics.ContainsKey(i))
                {
                    continue;
                }
                else
                {
                    Woman w = new Woman();
                    w.Name = "范冰冰";
                    dics.Add(i, new WeakReference(w));
                }
            
            }
            Console.WriteLine(string.Format("缓存完成,按任意键GC回收内存中的woman"));
            Console.ReadKey();
            GC.Collect();
            Console.WriteLine(string.Format("回收完成,内存中woman对象数量是{0}个",Woman.Count));
            Console.ReadKey();
        }
    }

 会发现GC自动清理了多于woman

 从缓存中取woman:

  foreach(var wm in dics)
            {
         //判断弱引用是否被GC清理掉
if(wm.Value.IsAlive) { Woman w = wm.Value.Target as Woman; if(w!=null) { Console.WriteLine(string.Format("woman key:{0}",wm.Key)); } } }

 另外,在处理“大对象”上可以关注下弱引用的应用,提升程序性能;

 

相关