托管内存转储分析器
对于日志中未显示的问题或无法通过本地调试进行调查的问题,您可能会尝试在问题在生产环境中处于活动状态时捕获诊断工件,如内存转储。然而,在与开发人员和支持工程师交谈时,我们知道内存分析可能很耗时、很复杂,需要多年才能完善的技能。
我们的新产品。NET分析器的开发有助于识别内存转储中可能指示生产服务出现问题的关键信号。这篇博客文章详细介绍了如何使用VisualStudio的新功能。NET内存转储分析器通过以下方式查找生产问题:
- 打开内存转储
- 根据转储选择和执行分析器
- 审查分析仪的结果
- 导航到有问题的代码
查找异步反模式
异步(Asynchronous)编程在上已经有好几年了。NET平台,但可能很难做好。关于异步的最佳实践以及如何正确使用它,这导致了一些反模式可能在您的服务处于高负载下时才会显示出来。
我们与一群支持工程师进行了交谈,“sync-over-async”反模式具有一组负面的性能特征:
- 服务响应时间比正常情况慢。
- 导致超时的请求数量增加。
- CPU和内存往往保持在正常范围内。
当然,在这一点上,仍然有许多潜在原因可能是您有问题的服务行为的核心。我们的团队希望让开发人员更容易找到这些类型的问题,并确定您可以在哪里解决问题。
为了实现这个目标,我们已经开始开发一个新的.NET诊断分析器工具为了帮助刚开始转储调试的开发人员快速识别(或排除)影响生产环境中客户的问题,我们来看一个示例。
自动分析内存转储
假设该服务出现了我们前面提到的症状(超时、响应速度慢等),您如何验证(或失效)可能的根本原因?
打开内存转储
首先,让我们使用文件->打开->文件菜单并选择内存转储。您还可以将转储拖放到Visual Studio中以打开它。
请注意,内存转储摘要页面上有一个新的行动调用运行诊断分析.
选择此操作将启动调试器并打开新的诊断分析页面中包含可用分析仪选项的列表,按基本症状进行组织。
针对转储选择并执行分析器
在我的例子中,我担心我的“应用程序没有及时响应请求”.为了调查这些症状,我将选择下面的所有选项流程响应性因为这最符合我的应用程序的问题。
点击分析按钮将启动调查,并根据内存转储中捕获的进程信息和CLR数据的组合显示结果。
审查分析仪的结果
在下面的图像中,分析器发现了两个错误结果,在选择第一个结果(“线程池超出线程数”)时,我可以看到分析摘要这意味着“CLR线程池正处于饥饿状态”。这一信息对调查非常重要,它表明CLR当前已使用了所有可用的线程池线程,这意味着在释放线程之前,我们无法响应任何新请求。
选择第二个结果“由于异步方法阻塞,线程池中的线程不足”,可以更明确地揭示问题的核心。通过查看转储,分析器能够找到我们无意中从异步线程上下文调用阻塞代码的特定位置,这直接导致线程池耗尽。
本例中的调用是“不要同步等待Monitors、Events、Task或任何其他可能会阻塞线程的对象。看看是否可以将方法更新为异步。”。我的下一个工作是找到有问题的代码。
导航到有问题的代码
通过单击显示调用堆栈链接Visual Studio将立即切换到显示此行为的线程调用堆栈window将向我展示所有需要进一步检查的方法。我可以快速区分我的代码(SyncOverAsyncExmple.*)和框架代码(System.*),并且可以将其作为我调查的起点。
调用堆栈的每一帧(或行)都对应于一个方法,通过双击任何堆栈帧,我会提示VisualStudio引导我找到在此线程上直接导致此场景的代码。很遗憾,我没有与此应用程序关联的符号或代码,因此未加载符号下面的页面我可以选择反编译源代码选项.
在下面的反编译源代码中,我很清楚我有一个异步任务(ConsumeThreadPoolThread)调用包含WaitHandle(等待处理)。WaitOne方法,这将阻塞当前线程池线程,直到它收到信号(这正是我想要避免的!)。因此,为了提高我的应用程序的响应能力,我必须找到一种方法,从所有异步上下文中删除这些阻塞代码。
现在就去看看吧!
新的。NET内存分析器工具使开发人员和支持工程师更容易开始调试和诊断内存转储中的问题,使他们能够快速找出生产环境中的问题根源。
我们目前支持以下分析仪,并在不久的将来提供新的和改进的分析:
我们相信还有更多的问题可以通过使用转储分析器快速确认,我们希望获得社区反馈,了解哪些问题对您最重要。
拜托帮助我们优先考虑要改进和构建的分析器填写这份调查!
马克·唐尼
Visual Studio首席程序经理