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 Dictionarydics = 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)); } } }
另外,在处理“大对象”上可以关注下弱引用的应用,提升程序性能;