65.9K
CodeProject 正在变化。 阅读更多。
Home

Silverlight 应用程序内存泄漏检测器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.15/5 (10投票s)

2009年2月11日

CPOL

3分钟阅读

viewsIcon

56678

downloadIcon

800

Silverlight 应用程序的简单内存泄漏检测器。

目前没有用于 Silverlight 的内存分析器

如果您曾经用 Silverlight 编程,您可能想知道内存中的对象是否被 GC(垃圾回收器)回收。 如果您创建的对象在应用程序的整个生命周期内都停留在内存中,您会知道吗?

常规的 .NET 内存分析器不适用于 Silverlight 应用程序,因此,如果我们有内存问题并想检查它,解决问题的唯一方法是将我们的项目转换为 WPF 应用程序,这在最好的情况下也是很困难的,而在我的场景中,几乎是不可能的。

何时应该使用内存泄漏检测器?

我编写的内存泄漏检测器旨在用于您感觉自己编写的某个对象在应该被 GC 回收时没有被回收的情况。 您可以使用内存泄漏检测器来监视此行为。

如何使用 Silverlight 应用程序内存泄漏检测器?

要使用我的内存泄漏检测器,首先通过在对象的构造函数中调用检测器的 AddReference 方法来添加要监视的对象。 接下来,在对象应该已经被 GC 回收的点,调用 Check 方法来验证它是否确实被回收并且不再位于内存中。

如何欺骗 GC 回收对象,尽管内存泄漏检测器具有对它们的引用?

这个内存泄漏检测器的诀窍是使用 WeakReference 类。 WeakReference 是一个类,它提供引用内存中现有对象的能力,而不会阻止该对象被 GC 回收。 有关 WeakReference 类的更多信息,请访问 这里

代码 – 概述

内存泄漏检测器管理一个名为 elementsList 的主列表,该列表由 ObjectStruct 元素组成。 每个元素都包含对被监视对象的 WeakReference 和一些其他调试值。 您可以使用 StackTrace() 来识别被监视对象的创建者。 此字段可以帮助查找内存泄漏的来源。

代码分为三个主要的静态公共方法。 前两个必须被调用,而最后一个 SignalDisposed 用于进一步调试。

主要方法

  • AddReference - 将您要监视的对象添加到内存检测器。 您可以选择是否要在创建对象时创建“StackTrace”。 对象的构造函数是调用此方法的好地方。
  • [Conditional("DEBUG")]
    public static void AddReference(object obj)
    {
        lock (lockObject)
        {
            // avoiding dups - can happen when adding
            // an element and its base-class to the profiler
            if (elementsList.Any(p => p.Reference.IsAlive 
                         && p.Reference.Target == obj))
                return;
            elementsList.Add(new ObjectStruct(new WeakReference(obj), 
                             generationCounter, new StackTrace().ToString()));
            //elementsList.Add(new ObjectStruct(new WeakReference(obj), 
            //                 generationCounter, null));
        }
    }
  • Check – 使用此方法检查内存状况。 实现此调用的一个好地方是应用程序的空闲状态,其中大多数已创建的对象都可以被释放和回收。 每次调用 Check 方法都会将静态 generation 计数器增加 1,从而为我们提供了一种跟踪被监视对象何时实例化的方法。 Debugger.Break() 命令会在某个点停止应用程序,允许您检查内存中所有存活的对象。
  • [Conditional("DEBUG")]
    public static void Check()
    {
        lock (lockObject)
        {
            long memUsage = GC.GetTotalMemory(true);
            for (int i = 0; i < elementsList.Count; i++)
                if (!elementsList[i].Reference.IsAlive)
                    elementsList.RemoveAt(i--);
        }
        generationCounter++;
        Debugger.Break();
    }
  • SignalDisposed – 我在使用此检测器在我们的 语义 Web Firefox 插件 上工作时,发现了内存泄漏,这让我得出结论,有一个布尔变量指示被监视对象的 Dispose 方法是否被调用是有价值的。 要调试您的泄漏,请在 Dispose() 方法中调用此方法,以检测调用了 Dispose() 方法但对象仍留在内存中的情况。
  • [Conditional("DEBUG")]
    public static void SignalDisposed(object obj)
    {
        lock (lockObject)
        {
            var objectsToSignal = elementsList.Where(p => p.Reference.IsAlive 
                                  && p.Reference.Target.Equals(obj));
            if (objectsToSignal.Any() && objectsToSignal.Count() == 1)
            {
                var objectToSignal = objectsToSignal.First();
                objectToSignal.DisposedSignaled = true;
            }
        }
    }

注释

  1. 所有三个函数都具有 Conditional(“DEBUG”) 属性,允许在项目中自由使用内存泄漏检测器,而无需担心应用程序发布版本的性能。 有关 Conditional 属性的更多信息,请访问 这里
  2. 该类是线程安全的。

未来发展方向

了解哪个对象持有对内存中被监视对象的引用会很棒……

© . All rights reserved.