使用 dotMemory Unit 发现和修复内存问题





0/5 (0投票)
dotMemory Unit 非常灵活,允许您检查应用程序内存使用的几乎任何方面。
我们都知道单元测试是开发和验证我们在 .NET IDE 中编写代码的重要方面。我们希望我们的测试能够覆盖 .NET 解决方案中最小的独立代码段,并覆盖尽可能多的代码路径,以确保我们的代码按预期运行。
有时测试代码的逻辑是不够的。虽然内存分析器不是我们大多数人每天使用的工具,但我们应该更频繁地使用它们。通常,它们只在开发过程的后期使用,当出现问题或修复错误时。如果我们能在单元测试中利用内存分析器呢?那么我们就可以安心地知道,我们的代码不仅逻辑编写得好,而且还能有效地使用内存。
何时使用 dotMemory Unit?
大多数开发人员会在出现问题时启动内存分析器。我们应该计划在整个开发过程中使用内存分析器,而不仅仅是当我们认为在代码中发现了涉及内存泄漏的 bug 时。这是一个非常有效的理由,但这就像用药物治疗病人一样。为什么不订阅预防性保健,就像我们在自己的生活中做的那样?我们应该计划、锻炼并有纪律地测试我们的代码,以确保我们的应用程序消耗和使用预期的内存。
我们应该用dotMemory Unit测试什么?首先,我们应该识别应用程序中我们知道占用内存最多的区域。我们还应该测试那些需要处理大量对象的区域,比如涉及从数据源查询数据的循环。最后,我们应该检查我们的应用程序如何为数据流量占用大量内存。我们所有的测试最终都可以保存单元测试运行结束时的内存快照,供我们检查消耗模式和行为。
我们还可以使用dotMemory Unit来测试特定类型的对象是否在我们的单元测试所涵盖的代码中被实例化和使用。此外,我们可以使用 dotMemory Unit 来测试保存的快照与当前内存使用之间的差异。
正如我们所见,在整个开发过程中,有不少领域可以从使用 dotMemory Unit 增强的单元测试中受益。除了在单元测试中使用 dotMemory Unit 之外,该工具还可以支持开发过程中对功能和集成测试的需求。现在让我们看看如何创建和使用这些特殊的单元测试。
dotMemory Unit 的工作原理
接下来,我们将了解如何在我们的 .NET Framework 和 .NET Core 解决方案和项目中与 dotMemory Unit 配合使用。有两种方法可以将 dotMemory Unit 添加到我们的 .NET 解决方案中。第一种方法是将 dotMemory Unit NuGet 包安装到我们的项目中。在 Visual Studio 中,我们可以使用以下命令通过包管理器控制台添加该包
Install-Package JetBrains.DotMemoryUnit
在 Rider IDE 中,我们可以使用 NuGet 窗口添加 JetBrains.DotMemoryUnit 包,如下图所示。
我们还可以下载必要的文件(包括独立的 dotMemory 启动器),并将库包含并引用到我们的 .NET 解决方案中。您可以在 dotMemory Unit 主页https://www.jetbrains.com/dotmemory/unit/上找到下载链接。
我们可以使用 ReSharper 的 Test Runner 中的大多数单元测试框架,支持 ReSharper 和 Rider 中的 dotMemory Unit,包括 xUnit、NUnit 和 MSTest。我们还可以从 JetBrains 的 dotCover 产品运行我们的单元测试。只需了解 dotMemory Unit 本身不是一个测试运行器,我们将与支持的单元测试框架合作使用它。
dotMemory Unit 的功能超出了仅仅在单元测试中添加新功能。单元测试运行完毕后,我们可以保存内存配置文件快照,该快照可用于与未来的单元测试进行比较,也可以在 JetBrains dotMemory 工具中打开和分析。我们还可以从独立的 dotMemory Unit 可执行文件运行使用 dotMemory Unit 创建的单元测试。这将使我们能够将测试纳入我们的 DevOps 工作流程,例如持续集成(CI)和交付(CD)。
运行我们的单元测试时,我们将可以使用额外的菜单选项在 dotMemory Unit 下运行我们的测试。
dotMemory Unit 的使用示例
让我们来看看我们在 .NET 和 .NET Core 解决方案中执行的单元测试中可以使用 dotMemory Unit 的几种不同方式。我们将从简单的开始,然后变得更高级。请注意,我将使用 xUnit 框架进行单元测试示例。
注意:在使用 xUnit 时,默认情况下 dotMemory Unit 的输出是不可见的。为了使其可见,我们必须在单元测试类的构造函数中指示它使用 xUnit 的输出助手:
检查对象
我们可以测试的最有用的情况之一是通过检查特定类型的内存来查找泄漏。我们使用这种类型的测试来识别由应用程序中的内存流量引起的性能问题。
在上面的示例中,我们将一个 lambda 传递给 Check()
方法。只有当我们使用Run Unit Tests under dotMemory Test运行测试时,才会执行此操作。传递给 lambda 的内存对象包含当前执行点的所有内存数据。GetObjects()
方法将返回在第二个 lambda 中传递的 Album 对象集合。最后,Assert()
将检查以验证内存对象中只传递了一个 Album 对象。
检查内存流量
检查内存流量的测试更简单。我们所要做的就是用<span lang="IT">AssertTraffic</span>
属性标记测试。在下面的示例中,我们断言 DotMemoryTrafficUnitTest()
方法中所有代码分配的内存量不超过 1,000 字节。
比较快照
我们可以使用检查点不仅比较流量,还可以用于其他类型的快照比较。在下面的示例中,我们断言在 memoryCheckPoint1
和第二个 dotMemory.Check()
调用之间的间隔中,来自<span lang="NL">Chinook</span>
命名空间的对象没有存活下来,从而被垃圾回收。
内存流量检查的复杂场景
如果我们需要有关内存流量的更复杂信息,我们可以使用与第一个示例类似的方法。传递给 dotMemory.Check()
方法的 lambda 验证在 memoryCheckPoint1
和 memoryCheckPoint2
之间的间隔中创建的实现 <span lang="PT">Album</span>
类的对象总大小小于 1,000 字节。
使用独立启动器
如果我们因为工作地点或团队选择的开发工具而无法使用 ReSharper 和 Rider 怎么办?如果我们想使用独立的单元测试运行器(而不是 Visual Studio 或 Rider)运行测试,或者想将内存测试作为持续集成构建的一部分,该怎么办?JetBrains 使这些场景变得易于处理!我们可以使用独立的 dotMemory Unit 可执行文件 - dotMemoryUnit.exe 命令行工具。
dotMemoryUnit.exe 作为中介工作 - 它运行一个独立的单元测试运行器,并为正在运行的测试中的 dotMemory Unit 调用提供支持。
在最简单的情况下,我们所要做的就是指定我们的单元测试运行器的路径及其参数。例如,在下面的示例中,我们想从 MainTests.dll 运行 NUnit 测试
dotMemoryUnit.exe "C:\NUnit 3.11.0\bin\nunit-console.exe" -- "E:\MyProject\bin\Release\MainTests.dll"
默认情况下,如果工具成功完成工作,其退出代码为 0。当我们在 CI 服务器上运行工具时,这很不方便,因为我们需要知道构建中是否有任何失败的测试。在这种情况下,最好的选择是让 dotMemoryUnit.exe 返回单元测试运行器的退出代码。为此,我们应该使用--propagate-exit-code参数。例如
dotMemoryUnit.exe "C:\NUnit 3.11.0\bin\nunit-console.exe" --propagate-exit-code -- "E:\MyProject\bin\Release\MainTests.dll"
分析来自 dotMemory Unit 的工作区
由 dotMemory Unit 生成和保存的工作区可以用独立的 JetBrains 应用程序 dotMemory 打开。dotMemory Unit 默认保存 *.dmw 文件的位置是我们计算机的临时位置(%temp%)。在某些情况下,我们可能希望重新定义工作区文件的位置。这可以通过放在程序集、测试类或测试方法之前的 DotMemoryUnitAttribute
来实现。
在此示例中打开 dotMemory 将显示两个已存储快照的详细信息,并允许我们查看比较和差异。
总结
dotMemory Unit 非常灵活,允许您检查应用程序内存使用的几乎任何方面。像对待应用程序逻辑的单元测试一样使用“内存”测试。
- 在手动发现问题(如泄漏)后,编写一个涵盖它的内存测试。
- 编写测试以进行主动测试 - 以确保新产品功能不会造成任何内存问题,例如对象停留在内存中或大量流量。
感谢阅读,请随时在您自己的项目上尝试 dotMemory Unit!它是完全免费的,唯一的要求是您的计算机上安装了 Rider、ReSharper 或 dotCover。