JavaScript

管理 Windows 应用商店应用程序的内存

David Tepper

 

Windows 8 为了感受流体和活着,让用户在多个应用程序来完成各项任务和活动之间迅速切换。 用户期望快速流行和不同的经验,他们从来没有想要感觉他们有等待应用程序,当他们需要使用它。 在此模型中,应用程序由用户 ; 很少终止 相反他们经常是国的执行和暂停之间切换。 应用程序被带到使用前景色,然后在当用户切换到另一个应用程序移动到后台 — — 和用户,同时期望他们的机器不能减慢或感觉迟钝,即使在他们打开更多和更多的应用程序。

在 Microsoft Windows 应用程序体验团队调查中,我们已经看到一些 Windows 存储应用程序开始在长时间使用过程中遇到资源问题。 在应用程序中的内存管理错误可以随着时间,从而导致不必要的内存使用情况并产生不利影响整体机复合。 在我们努力在我们自己的产品壁球这些 bug,我们已经确定了一些重复问题的模式,以及共同的修复和技术来逃脱出来。 在本文中,我将讨论如何考虑内存管理在您 Windows 存储的应用程序,以及如何识别潜在的内存泄漏。 我也将提供共同的问题,小组注意到,一些编纂的解决方案。

内存泄漏是什么?

在应用程序中的资源,可以将回收既使用会导致任何方案被认为是内存泄漏。 换句话说,如果在 app 持有一块内存系统的其余部分将永远无法使用,直到终止该应用程序,则应用程序本身没有使用它,就有问题。 这是一个更广泛的定义,比典型的诠释的内存泄漏,"动态分配的内存中不可访问,在代码中,"但这也是更有用的因为它包含可以对用户和系统产生负面影响的其他的类似的资源利用问题。 例如,如果 app 存储来自各地的代码可访问的数据,但只有一次并且不释放以后使用数据,它是根据这一定义的泄漏。

它是重要的是要记住,有时数据存储在内存中将永远不会仅仅因为用户的操作在该特定实例中使用。 只要可能可用在该应用程序的整个生存期内或被释放时不再需要此信息,它没有考虑到泄漏,尽管从未使用。

影响是什么?

当机器都在资源可用性的天空到比赛的日子一去不复返了。 电脑越来越小,更便携,较少的可用资源,比他们的前辈。 这从根本上是与日益普遍涉及切换之间多方面的经验迅速,快点的 UI 并立即可用的所有内容,预期的使用模式。 今天,长时间的应用程序是众多和活着。 与此同时,机器有较少的内存,支持他们,和性能的用户期望从来没有更高。

但不会泄漏几兆字节真的使那么大的差别呢? 嗯,这个问题不是几兆字节泄露一次,那就是作为继续使用该应用程序的内存泄漏在随着时间的推移往往复合的代码中。 如果方案导致无法恢复的资源,无法恢复的资源的数量将增长,通常没有边界,随着用户不断重复这种情况。 这迅速降低整个系统的可用性,因为较少的内存可供其他进程,它导致特性差系统性能对您的应用程序的用户。 内存泄漏是最严重的当他们出现在:

  • 常见任务 (如解码下一帧的视频)
  • 不需要用户交互来启动的任务 (例如,自动-定期保存文档)
  • 对于长时间 (例如后台任务) 运行的方案

在这些情况下 (和一般) 泄漏可以极大地提高应用程序占用的内存量。 不仅可以此铅为整个系统的资源利用危机,它还可以使您的应用程序更有可能终止而不是在不使用时暂停使用。 终止应用程序以较长时间才能重新激活比暂停应用程序,减少用户可以体验您的方案的易用性。 Windows 如何使用进程生存期管理器以回收未使用的应用程序中的内存的全部详细信息,请参阅在建筑 Windows 8 的博客 bit.ly/JAqexg

因此,内存泄漏是坏 — — 但你如何找到他们吗? 在何处以及如何,我将会在接下来的几节中寻找这些问题,然后看一看为什么会发生并你可以做他们的。

不同种类的内存

并非所有的位是平等地分配。 Windows 跟踪的不同相符或将应用程序的内存使用,方便进行性能分析任务的视图。 更好地了解如何检测内存泄漏,是有用来了解这些不同的内存分类。 (本节假定操作系统内存管理通过分页的一些知识)。

私人工作集页面集当前正在使用您的应用程序存储其自己唯一的数据。 当你认为的"我的应用程序的内存使用情况"时,这可能是你在想什么。

共享工作集的一组页您的应用程序是利用但不是拥有您的进程。 如果您的应用程序使用共享的运行时或框架,常见的 Dll 或其他多进程的资源,这些资源将一些的内存量。 共享的工作集是这些共享资源的措施。

共工作设置 (TWS) 有时简称为"一套工作",这是私人的工作集和共享的工作集的总和。

TWS 表示应用程序的全面影响的系统上,所以我将描述的测量技术将使用此号码。 然而时追踪潜在的问题,您可能会发现它有用,调查私营或共享分开,工作集,因为这可以告诉你是否正在泄漏的应用程序或应用程序使用的资源。

发现内存泄漏

发现您的应用程序使用每个类别中多少内存的最简单方法是使用内置的 Windows 任务管理器。

  1. 通过按下 Ctrl + Shift + Esc,启动任务管理器,单击底部附近的更多详细信息。
  2. 单击选项菜单项,请确保选中了"总在最前"。 这可以防止您从背景去和暂停时您要查找在任务管理器中的应用程序。
  3. 启动您的应用程序。 一旦应用程序将显示在任务管理器中,右键单击它并单击"转到详细信息"。
  4. 顶部附近,右键单击任何列,去"选择列"。
  5. 你就会看到这里的选项为共享和专用工作集 (及其他),但暂时只是请确保选中了"工作集 (内存)"和单击确定 (见图 1)。
  6. 您将看到的值是 TWS 为您的应用程序。

Checking the Total Working Set in Windows Task Manager
检查工作集在 Windows 任务管理器中的总图 1

快速发现潜在的内存泄漏,留下您的应用程序和任务管理器打开并写下您的应用程序 TWS。 现在您要测试的应用程序中选择方案。 方案由典型的用户会经常执行通常涉及不超过四个步骤 (页面之间导航,执行搜索等等) 的行动。 作为用户,并注意到任何增加 TWS 执行该方案。 然后,而无需关闭该应用程序,再通过该方案,从开头开始。 做这 10 次,并记录 TWS 之后的每一步。 它是正常的 TWS 增加首几次迭代,然后高原。

您的应用程序内存使用情况是否增加每次执行了该方案,无需不断重置到原来的水平吗? 如果这样,有可能在该方案中存在内存泄漏,要看一看下面的建议。 如果不,太棒了 ! 但是,请确保检查您的应用程序,特别是那些很常见,或使用大量资源,如图像的其他方案。 然而 ; 避免执行这一进程的虚拟机上或通过远程桌面 这些环境可以导致误报,寻找泄漏时,增加您的内存使用率号码超出其实际价值。

使用 Pre-Windows 8 内存泄漏检测工具

您可能想知道是否你可以使用现有内存泄漏检测工具来确定与您的 Windows 存储应用程序问题。 这些工具将更新以与 Windows 8 的工作,除非它是很有可能他们会感到"困惑"正常关机 (这已被暂停所取代) 应用程序的缺乏。 要解决此问题,可以使用 AppObject"的退出"功能直接关闭在井然有序地,而不是强行关闭它通过外部终止应用程序:

  • C++—CoreApplication::Exit()
  • C#—Application.Current.Exit()
  • JavaScript—window.close()

当使用这种技术,请确保不要船您与此代码中位置的产品。 您的应用程序不会调用任何中止和意志上的触发器需要重新激活的代码 (而不是续会) 每次打开时。 这种技术应仅用于调试目的和之前提交到 Windows 存储应用程序中删除。

常见的内存泄漏来源

在本节中,我将讨论一些常见的陷阱,我们看到开发人员运行到跨所有类型的应用程序和语言,以及如何能够在您的应用程序处理这些问题。

事件处理程序事件处理程序是最常见的我们已经看到在 Windows 存储应用程序中的内存泄漏来源。 根本的问题是缺乏了解有关事件处理程序的工作原理。 事件处理程序并不只是代码获取执行 ; 他们分配的数据对象。 它们保存引用的其他内容,与他们保持对引用可能不明显。 从概念上讲,实例化和注册事件处理程序包括三个部分:

  1. 事件源
  2. 事件处理程序方法 (执行)
  3. 承载该方法的对象

作为一个例子,让我们看看 LeakyApp,显示在一个叫做图 2

图 2 LeakyApp

public sealed partial class ItemDetailPage : 
  LeakyApp.Common.LayoutAwarePage
{
  public ItemDetailPage()
  {
    this.InitializeComponent();
  }
  Protected override void OnNavigatedTo(NavigationEventArgs e)
  {
    Window.Current.SizeChanged += WindowSizeChanged;
  }
  private void WindowSizeChanged(object sender,
    Windows.UI.Core.WindowSizeChangedEventArgs e)
  {
    // Respond to size change
  }
  // Other code
}

LeakyApp 代码显示一个事件处理程序的三个部分:

  • Window.Current 是源自 (火灾) 的对象的事件。
    • 实例是接收的对象 ItemDetailPage (汇) 事件。
  • WindowSizeChanged 是 ItemDetailPage 实例中的事件处理程序方法。

后注册的事件通知,当前窗口对象已在一个 ItemDetailPage 对象中的事件处理程序的引用中所示图 3。 此引用导致 ItemDetailPage 对象仍然活着的只要当前窗口对象仍然活着,或直到当前窗口对象降到项目的引用­DetailPage 实例 (现在,忽略这些对象的其他外部引用)。

A Reference to the Event Handler
图 3 引用的事件处理程序

请注意对于 ItemDetailPage 实例都能正常运行,虽然它是活着 Windows 运行库 (WinRT) 间接让该实例使用的所有资源活下去。 实例应包含对大量分配如数组或图像的引用,这些拨款就能活下去,实例的生存期。 实际上,注册事件处理程序扩展包含事件处理程序中,和所有其依赖项,以匹配的事件源的生存期的对象实例的生存期。 当然,到目前为止,这不是资源泄漏。 这也是只需订阅事件的结果。

ItemDetailPage 是类似于一个应用程序中的所有页面。 它用于当用户导航至页,但不再需要时他们会导航到另一页。 当用户导航回 ItemDetailPage 时,应用程序通常会创建页面的一个新实例,并与当前窗口接收 SizeChanged 事件注册的新实例。 不过,在此示例中,bug 是当用户离开 ItemDetailPage,页面无法注销其从当前窗口 SizeChanged 事件的事件处理程序。 当用户导航从 ItemDetailPage 时,当前窗口仍有前一页的引用和当前窗口继续火到页的 SizeChanged 事件。 当用户导航回 ItemDetailPage 时,此新的实例也注册当前窗口,如中所示图 4

A Second Instance Registered with the Current Window
图 4 第二个实例的当前窗口中注册

五个导航后,五个 ItemDetailPage 对象注册的当前窗口 (见图 5) 和及其依赖的资源将保持活动状态。

Five Objects Registered with the Current Window
图 5 5 个对象当前窗口中注册

这些无-长使用 ItemDetailPage 实例是资源,它们永远不会使用或回收 ; 他们有效地泄露出去。 如果你把这篇文章的一件事,请确保它是预防最常见的内存泄漏的最佳方法是注销事件处理程序,当不再需要他们。

要在 LeakyApp 中修复的问题,我们需要先删除从当前窗口的 SizeChanged 事件处理程序的引用。 这可以通过取消从事件处理程序,当页面视图,就像这样:

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
  Window.Current.SizeChanged -= WindowSizeChanged;
}

后向 ItemDetailPage 类中添加此重写,ItemDetailPage 实例不再积累和泄漏固定的。

注意此类问题可能出现的任何对象 — — 长寿命的任何对象保持活着它引用的一切。 我喊这里的事件处理程序,因为它们是迄今为止最常见的来源,这一问题的 — — 但会介绍,他们就不再需要清理的对象是避免大内存泄漏的最佳办法。

循环引用的事件处理程序中,跨选区创建一个特定的事件处理程序,当您启动通过指定时触发该事件时,将调用一个函数,然后将该处理程序附加到将收到有关该事件的对象。 当实际上会触发该事件时,处理函数有参数,表示的对象的最初收到的事件,称为"事件源"。该按钮的单击事件处理程序,如下,"发件人"参数是事件源:

private void Button_Click(object sender, RoutedEventArgs e)
{
}

根据定义,事件源已对该事件处理程序的引用,否则源无法触发该事件。 如果捕获到该事件处理程序内源的引用,该处理程序现在有回源的引用,您已经创建循环引用。 让我们看看这行动中相当常见模式:

// gl is declared at a scope where it will be accessible to multiple methods
Geolocator gl = new Geolocator();
public void CreateLeak()
{           
  // Handle the PositionChanged event with an inline function
  gl.PositionChanged += (sender, args) =>
    {
      // Referencing gl here creates a circular reference
      gl.DesiredAccuracy = PositionAccuracy.Default;
    };
}

在此示例、 gl 和发件人是相同的。 引用 gl lambda 函数中的创建循环引用,因为源引用的处理程序,反之亦然。 通常这种循环引用不是问题,因为 (Gc) 的 JavaScript 和 CLR 垃圾回收器足够的智能化,以处理这类案件。 但是,循环引用的一侧不属于 GC 环境或属于不同的 GC 环境时,可以出现问题。

Geolocator 是一个 WinRT 对象。 WinRT 对象在 C/c + + 中实现,因此使用引用计数系统而不是 GC。 当 CLR GC 尝试清理此循环引用时,它无法清除 gl 靠自己。 同样,gl 的引用计数将永远不会达到零,因此 C/c + + 侧的事情不会收拾要么。

当然,这是一个非常简单的示例来说明这个问题。 如果它不是一个单一而是对象的用户界面元素 (如面板 (或在 JavaScript 中,一个 div) 的大型分组吗? 泄漏将包括所有这些对象和跟踪源将非常困难。

有的地方中各种缓解使许多这些姿势可以检测并清除了气相色谱法。 例如,涉及是用 JavaScript 代码的周期中的一个 WinRT 事件源的循环引用 (或与 XAML 对象作为事件源的循环引用) 正确填海。 但是,(如 JavaScript 事件与 C# 事件处理程序),涵盖的姿势并不是所有形式和随着的数量和复杂性对事件源的引用的增长,GC 特别缓解措施成为不少保证。

如果您需要创建事件源的引用,您可以始终显式注销以后要推倒圆度和防止任何泄漏 (这可以回溯到您创建的对象的生存期的推理) 的事件处理程序或 null 引用出。 但如果事件处理程序永远不会保存到源的引用,则不需要依赖于平台提供缓解或显式的代码,以防止什么可以是一个很大的资源利用问题。

使用缓存无界数据结构在许多应用程序,它使存储有关用户的最近的活动,以改善体验一些信息有意义。 例如,假设一个搜索应用程序,显示过去五个查询用户输入。 一种编码模式,实现这一目标就是在一个列表或其他数据结构中存储的每个查询,并提出意见,届时检索的前五名。 这种方法的问题在于如果应用程序在长时间保持打开状态,列表将会增长没有边界,最终占用了大量的不必要的内存。

不幸的是,GC (或任何其他内存管理器) 有没有办法对非常大,但可以到达,永远不会使用的数据结构的原因。 若要避免此问题,您将存储在高速缓存中的项目数继续硬限制。 定期淘汰旧的数据并不依赖于您的应用程序正在终止释放这些类型的数据结构。 如果特别时间敏感或容易重组所存储的信息,您可以考虑挂起时,完全清空高速缓存。 如果不是,保存缓存到本地状态并释放内存资源 ; 它可以在简历上重新获取。

避免举行大型引用中止问题

无论语言,持有大量引用挂起时可能会导致 UX 问题。 您的应用程序将保持悬浮的只要该系统能够无需额外的内存,只能通过终止应用程序检索服务其他正在运行的进程的请求。 因为住悬浮的手段由用户可以更轻松地访问您的应用程序,是保持您的内存占用量较小的暂时吊销期间您最感兴趣。

一个简单的方法来实现此目的是时暂停,可在简历上重组只是免费的大型对象的引用。 例如,如果您的应用程序持有本地应用程序数据在内存中引用,释放引用可能会大大降低您私人的工作集,并很容易在简历上重新获取,因为此数据不哪儿去。 (关于应用程序数据的详细信息,请参阅 bit.ly/MDzzIr.)

若要完全释放一个变量,将分配变量 (和所有对变量的引用) 为 null。 在 c + +,这将立即回收内存。 GC 将为 JavaScript 和 Microsoft.NET 框架的应用程序,运行时应用程序挂起,收回这些变量的内存。 这是一深度防御办法,确保正确的内存管理。

请注意,但是,如果您的应用程序在 JavaScript 中编写的有一些.NET 组件,则不会在上运行.NET GC 暂停。

在 JavaScript Windows 存储应用程序中的内存管理

这里有一些在 JavaScript 中创建资源节约的 Windows 存储应用程序的提示。 这些都是常见的问题在我们自己的应用程序,我们已经看到的推荐修复程序,考虑到与他们的设计将帮助避开许多潜在的问题之前他们带来的麻烦。

使用代码质量工具经常被忽略的资源,免费软件代码质量工具可供所有 JavaScript 开发在 Web 上。 这些工具检查您的代码很多常见的问题,包括内存泄漏,而可以将早期捕捉问题的打赌的最好。 两个有用的工具是 JSHint (jshint.com) 和 JSLint (jslint.com)。

使用严格模式 JavaScript 了一个"严"的模式,限制了您可以在代码中使用变量的方式。 这些限制把自己视为额外规则受到侵犯时被扔掉的运行时错误。 这种编码限制可以帮助您避免常见的内存泄漏,如隐式声明变量在全局范围内。 在严格模式下,它的使用和施加的限制的详细信息,在退房的 MSDN 库文章,"严格模式 (JavaScript)" bit.ly/RrnjeU

避免封闭的循环引用 JavaScript 有一个相当复杂的存储对变量的引用,每当 lambda (或内联) 函数使用系统。 基本上,为了要正确执行调用时的内联函数,JavaScript 存储一组称为一个封闭的引用中的可用变量的范围。 这些变量将保持活动状态直至内联函数本身不再被引用的内存中。 让我们看看一个示例:

myClass.prototype.myMethod = function (paramA, paramB) {
  var that = this;
  // Some code
  var someObject = new someClass(
    // This inline function's closure contains references to the "that" variable,
    // as well as the "paramA" and "paramB" variables
    function foo() {
      that.somethingElse();
    }
  );
  // Some code: someObject is persisted elsewhere
}

保持 someObject 后,"那,""帕尔玛"所引用的内存和"paramB"不会被回收,直到 someObject 被破坏,或者释放其对传递给它的在此处将有机可乘构造函数中的内联函数引用。

可以出现问题的内联函数关闭如果不释放对内联函数的引用,如关闭引用将永久驻留在内存中,导致泄漏。 出现这种情况的最常见方法是当关闭包含对本身的循环引用。 内联函数引用变量的引用的内联函数时通常会发生此种情况:

function addClickHandler(domObj, paramA, paramB, largeObject) {
  domObj.addEventListener("click",
  // This inline function's closure refers to "domObj", "paramA",
  // "paramB", and "largeObject"
    function () {
      paramA.doSomething();
      paramB.somethingElse();
    },
  false);
}

在此示例中,domObj 包含 (通过事件侦听器) 的内联函数的引用和内联函数闭包包含返回到它的引用。 因为大对象不正在使用,目的是它会超出范围,并得到回收 ; 但是,关闭引用让它和 domObj 在内存中活下去。 此循环引用将导致泄漏到 domObj 移除事件侦听器引用,或者获取音步骤和垃圾回收。 完成某件事情像这样的正确方法是使用一个函数,返回一个函数,执行您的任务,如中所示图 6

图 6 使用函数范围,以避免封闭的循环引用

function getOnClick(paramA, paramB) {
  // This function's closure contains references to "paramA" and "paramB"
  return function () {
    paramA.doSomething();
    paramB.somethingElse();
  };
}
function addClickHandlerCorrectly(domObj, paramA, paramB, largeObject) {
  domObj.addEventListener(
    "click",
  // Because largeObject isn't passed to getOnClick, no closure reference
  // to it will be created and it won't be leaked
  getOnClick(paramA, paramB),
  false);
}

使用此解决方案,消除了对 domObj 的封闭引用,但对帕尔玛和 paramB 引用仍然存在,因为他们是事件处理程序执行的必要。 若要确保不漏帕尔玛或 paramB,仍然需要注销事件监听器或只是等待他们获取自动收回时 domObj 获取垃圾回收。

撤销所有 Url 创建的 URL.createObjectURL 加载媒体的音频、 视频或 img 元素的常用方法是使用 URL.createObjectURL 方法来创建一个元素可以使用的 URL。 当您使用此方法时,它将通知系统保持您的媒体内部参考。 系统使用此内部参考以流到相应元素的对象。 但是,系统不知道当不再需要该数据,所以它保留内部引用活在内存中直到它已明确告诉,将其释放。 这些内部引用会占用大量的内存和容易意外地保留这些不必要地。 有两种方法来释放这些引用:

  1. 您可以通过调用 URL.revokeObjectURL 方法,并将它传递 URL 明确吊销该 URL。
  2. 你可以告诉系统 URL.createObjectURL 的 oneTimeOnly 属性设置为 true 以使用一次后自动撤销该 URL:
var url = URL.createObjectURL(blob, {oneTimeOnly: true});

对于临时对象使用弱引用想象一下你有一个大型的对象,您需要在您的应用程序的各个部分中使用的文档对象模型 (DOM) 节点的引用。 现在假设在任何一点可以释放该对象 (例如,node.innerHTML ="")。 您如何确保避免举行对对象的引用,所以它可以完全回收在任何点? 值得庆幸的是,Windows 运行库提供了解决这个问题,使您可以存储到对象的"弱"引用。 弱引用不会阻止从清理它所引用的对象 GC 和时取消引用,它可以返回的对象或返回 null。 为了更好地理解如何,这会很有用,看一看在示例图 7

图 7 JavaScript 内存泄漏

function addOptionsChangedListener () {
  // A WinRT object
  var query = Windows.Storage.KnownFolders.picturesLibrary.createFileQuery();
  // 'data' is a JS object whose lifetime will be associated with the  
  // behavior of the application.
Imagine it is referenced by a DOM node, which
  // may be released at any point.
// For this example, it just goes out of scope immediately,
  // simulating the problem.
var data = {
    _query: query,
    big: new Array(1000).map(function (i) { return i; }),
    someFunction: function () {
      // Do something
    }
  };
  // An event on the WinRT object handled by a JavaScript callback,
  // which captures a reference to data.
query.addEventListener("optionschanged", function () {
    if (data)
      data.someFunction();
  });
  // Other code ...
}

在此示例中,数据对象不是被回收,因为它正在引用上查询的事件监听器。 因为 app 的用意是要清除的数据对象 (和将不会再作尝试这样做),这是现在的内存泄漏。 若要避免此问题,可以使用下面的语法使用 WeakWinRTProperty API 组:

msSetWeakWinRTProperty(WinRTObj, "objectName", objectToStore);

WinRTObj 是任何支持 IWeakReference 的 WinRT 对象、 对象名称是要访问的数据的密钥和 objectToStore 是要存储的数据。

若要检索的信息,请使用:

var weakPropertyValue = msGetWeakWinRTProperty(WinRTObj, "objectName");

WinRTObj 是 WinRT 对象位置存储属性和对象名称是存储数据时的关键。

返回值为 null 或值最初存储 (objectToStore)。

图 8 演示了一种修复泄漏中 addOptions­ChangedListener 函数。

图 8 使用弱引用,避免内存泄漏

function addOptionsChangedListener() {
  var query = Windows.Storage.KnownFolders.picturesLibrary.createFileQuery();
  var data = {
    big: new Array(1000).map(function (i) { return i; }),
    someFunction: function () {
      // Do something
    }
  };
  msSetWeakWinRTProperty(query, "data", data)
  query.addEventListener("optionschanged", function (ev) {
    var data = msGetWeakWinRTProperty(ev.target, "data");
    if (data) data.someFunction();
  });
}

因为数据对象的引用是弱,移除其他对它的引用时,它将垃圾收集及回收其内存。

架构设计使用 JavaScript 的 Windows 存储应用程序

设计应用程序时考虑到资源的开发利用可以通过使您的应用程序从一开始更耐泄漏减少现货修复和内存管理特定的编码实践的需要。 它还使您可以构建保障措施,使它易于识别泄漏发生。 在这一节将讨论构建书面与可以单独使用的 JavaScript 或在一起以创建一个资源高效的应用,易于维护 Windows 存储应用程序的两种方法。

处置建筑处置体系结构是通过具有一致、 简单和可靠的方式来回收资源停止在其发病的内存泄漏的好方法。 你与记住此模式的应用程序是要确保每个类或大型对象实现回收内存的函数 (通常命名为处置) 的设计的第一步与关联的每个对象它引用。 第二步是作为参数,在实施大致可到达功能 (通常也会命名为处置) 对对象调用 dispose 方法传递,然后再使用空值对象本身:

 

var dispose = function (obj) {
  /// <summary>Safe object dispose call.</summary>
  /// <param name="obj">Object to dispose.</param>
  if (obj && obj.dispose) {
    obj.dispose();                
  }
  obj = null;
};

目标是这个程序将花上一个树状结构,与每个对象具有内部 dispose 方法能够释放出自己的资源,通过调用 dispose 方法对所有对象它引用,依此类推。 这种方式,完全释放对象和行为的所有引用,您需要做的一切是调用 dispose(obj) !

在应用程序中每个主要方案中过渡,只需调用 dispose 上所有的顶级对象,则不再需要。 如果您想要获得花式,你可以有所有这些顶级对象是一个主要的"情景"对象的一部分。 当切换场景中,您只需调用实例化一个新场景的切换应用程序和弃置在顶级方案对象上。

膨胀的体系结构 "膨胀"体系结构允许您更轻松地识别时通过使对象真的大权利之前您释放他们发生内存泄漏。 这种方式,如果该对象不实际释放,对您的应用程序 TWS 的影响将明显。 当然,只应在开发期间使用此模式。 App 永远不应附带到位情况下,此代码突增内存使用量 (即使是暂时的) 可以强制用户机终止其他暂停应用程序。

来人为地膨胀的对象,您可以做一些简单的作为附加到它的一个很大的数组。 快速使用联接的语法填充一些数据,从而使任何对象附加到明显较大,它的整个数组:

var bloatArray = [];
bloatArray.length = 50000;
itemToBloat.leakDetector = bloatArray.join("#");

要有效地使用此模式,您需要标识对象应该要释放的代码时的好方法。 此您可以手动为每个对象释放,但有两个更好的方法。 如果您使用的刚才讨论的处置体系结构,只需添加膨胀代码中所讨论的对象的 dispose 方法。 这样,一旦调用 dispose 时,你会知道是否该对象真正已被删除或不及其引用的所有。 第二种方法是使用的任何元素的 dom 的 JavaScript 事件 DOMNodeRemoved 因为移除节点之前,将激发此事件,您可膨胀这些对象的大小和查看是否他们真正要回收。

请注意有时 GC 将需要一些时间来实际收回未使用的内存。 时有泄漏、 方案进行测试,如果应用程序似乎增长十分迅速,等待一段时间才能确认泄漏 ; GC 可能不做通不过。 如果等待、 后 TWS 是仍然很高,然后再试方案。 如果应用程序的 TWS 仍大,它是极有可能有泄漏。 您可以在源中磨练的通过有系统地从您的应用程序中的对象删除此膨胀代码。

前进

我希望我已经确定、 诊断和修复您的 Windows 存储应用程序中的内存泄漏给你一个牢固的基础。 泄漏往往从数据分配和填海工程是如何发生的误解的结果。 这些细微差别的知识 — — 结合如显式地赋空出对大变量的引用简单技巧 — — 会走很长的路,确保高效的应用程序不减慢用户的机器,即使在天的使用。 如果您正在寻找的更多信息,您可以签出的 Internet Explorer 团队的涵盖相关的主题,"理解和解决互联网浏览器泄漏模式,"MSDN 库文章在 bit.ly/Rrta3P

David Tepper 是在 Windows 应用程序体验团队的项目经理。他一直对应用程序模型的设计和应用程序部署 2008 年以来主要集中于 Windows 存储应用程序的性能和这些应用程序可以将如何扩展 Windows 深感提供集成的功能性*.***

衷心感谢以下技术专家对本文的审阅:Jerry Dunietz、 Mike Hillberg、 恩威假话、 卡门 Moutafov、 布伦特校长和奇帕洛街