Visual Studio的.NET内存分配分析器解析
Visual Studio 2012拥有丰富的有价值的功能,以至于我听到开发者反馈的需要的新功能新版本已经有了。另外,我听到开发人员询问具体的功能的某个特性,实际上他真正需要的是另外一个功能点。
上面说的两种情况下适用于Visual Studio的.NET内存分配分析器 。 许多开发人员可能会从中受益却不知道它的存在,而另外一些开发者有却对它有不正确的理解。 这样很不好,因为该功能可以提供很多有价值的特定场景; 许多开发在理解的情况下才能发挥其预期的作用。也就是要做到以下两点:第一,知道它的存在,第二,知道如何使用。
为什么内存分析?
当谈到.NET和内存分析,有两个主要的原因之一,可能需要使用一个诊断工具:
1. 发现内存泄漏。
2. 发现不必要的分配。
内存优化的实例
为了更好地理解内存分析器的作用,以及它如何帮助到我们。让我们通过一个示例来理解。
public static async Task<T> WithCancellation1<T>( this Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource< bool >();
using (cancellationToken.Register(() => tcs.TrySetResult( true )))
if (task != await Task.WhenAny(task, tcs.Task))
throw new OperationCanceledException(cancellationToken);
return await task;
}
上面这段代码的作用是可以异步的取消正在运行的任务
T result = await someTask;
T result = await someTask.WithCancellation1(token);
如果取消要求在任务完成之前,相关的CancellationToken,一个OperationCanceledException将被抛出。
要了解参与这一方法的分配,我们将使用一个小程序去测试。
using System;
using System.Threading;
using System.Threading.Tasks;
class Harness
{
static void Main()
{
Console.ReadLine(); // wait until profiler attaches
TestAsync().Wait();
}
static async Task TestAsync()
{
var token = CancellationToken.None;
for ( int i=0; i<100000; i++)
await Task.FromResult(42).WithCancellation1(token);
}
}
static class Extensions
{
public static async Task<T> WithCancellation1<T>(
this Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource< bool >();
using (cancellationToken.Register(() => tcs.TrySetResult( true )))
if (task != await Task.WhenAny(task, tcs.Task))
throw new OperationCanceledException(cancellationToken);
return await task;
}
}
运行.NET内存分配分析器
要启动内存分配分析器,在Visual Studio中去分析菜单并选择“启动性能向导...”。 这将打开如下所示的对话框:

选择“.NET内存分配(取样)”,单击下一步两次,最后是完成。 在这一点上,应用程序将被启动,并剖析将开始监视它的分配(线束上面的代码也需要你按下“Enter”键) 。 当应用程序完成后,或当你手动选择停止剖析,剖析会加载符号,并开始分析跟踪。 这是一个很好的时间去让自己的一杯咖啡,或午餐,因为这取决于有多少分配发生后,该工具可以需要一段时间才能做到这一点的分析。
当分析完成后,我们看下报告:

从这里,我们可以进一步研究,通过查看分配汇总(从“当前视图”下拉列表中选择“分配”):

在这里,我们能看到一排的被分配的每种类型,同列显示有多少分配被跟踪的信息,有多少空间与分配,以及如何分配映射回该类型的百分比有关。 我们还可以扩展一个条目看,看到这些分配方法的调用堆栈:

通过选择“功能”的观点,我们可以得到一个不同的支点这一数据,高亮它的功能分配的大多数对象和字节:

剖析分析报告并做相应优化
我们可以分析我们的例子中的结果。 首先,我们可以看到,有相当数量的分配在哪里,这可能是令人惊讶的。毕竟,在我们的例子中我们使用WithCancellation1与已经完成了任务,这意味着应该有很少的工作要做(与已完成的任务,没有什么取消).然而从上面的跟踪我们可以看到,我们的例子中的每一次迭代中产生:
- 三种分配Task`1的(我们跑了线束10万次,可以看到有〜300K分配)
- 两个分配task[]
有关任务的辅助业务,它实际上是相当普遍的处理已经完成的任务,因为很多时候,操作执行是异步的,实际上完全同步(例如,在网络流中的一个读操作可以缓冲到内存足够的额外数据来完成后续的读操作)。 因此,优化已经完成的情况下,可以为表现的确有益。 让我们来试试。 这里有一个第二次尝试WithCancellation,一个优化的几个“已完成”的情况:
public static Task<T> WithCancellation2<T>( this Task<T> task,
CancellationToken cancellationToken)
{
if (task.IsCompleted || !cancellationToken.CanBeCanceled)
return task;
else if (cancellationToken.IsCancellationRequested)
return new Task<T>(() => default (T), cancellationToken);
else
return task.WithCancellation1(cancellationToken);
}
此实现检查:
- 首先,无论是任务已经完成或是否提供的CancellationToken无法取消; 在两者的那些情况下,有不需要额外的工作,因为取消不能适用,因此我们可以只返回原始任务立即而不是花费任何时间或存储器中创建一个新的。
- 那么是否取消已要求; 如果有,我们可以分配一个业已取消的任务将返回,而不是花费八个分配我们先前支付给调用我们原来的实现。
- 最后,如果没有这些快速通道申请,我们通过降至调用原始的实现。
再分析我们的微基准,而使用的,而不是WithCancellation1 WithCancellation2提供了大大改善的前景(你很可能会发现,分析的速度远远超过它之前,已经是一个迹象,表明我们已经显著减少内存分配完成)。 现在,我们刚刚有主要业务划分,我们预计,从Task.FromResult一个从线束我们TestAsync方法叫做:

所以,我们现在已经成功了优化,其中任务已经完成,取消在那里不能要求,或者取消已申请的情况。 怎么样的情况下,我们确实需要调用更复杂的逻辑? 是否有可有什么改进?
让我们改变我们的基准来使用,这不是已经由我们引用WithCancellation2,并且还使用可以有取消请求令牌的时间内完成的任务。 这将确保我们使它的“慢”的路径:
using (cancellationToken.Register(() => tcs.TrySetResult( true )))
剖析再次提供了更深入的了解:

在这种缓慢的道路,现在有14%的迭代拨款总额,包括2从我们TestAsync线束(该TaskCompletionSource <int>的我们明确地创建和任务<int>的它创建)。 在这一点上,我们可以使用所有的分析结果提供的信息,了解那里的其余12分配的来源,并随后解决这些问题的是相关的和可能的。 例如,让我们看两个分配具体为:在<> c__DisplayClass2`1实例,这两个动作实例之一。 这两种分配将可能是合乎逻辑的任何人都熟悉的C#编译器如何处理倒闭 。 为什么我们有一个封闭? 由于该行的:
使用(cancellationToken.Register(()=> tcs.TrySetResult( 真 )))
电话注册,是关在'TCS'变量。 但是,这不是严格必需的:该注册方法具有另一个超载,而不是采取一个动作这需要一个Action <object>和该对象的状态被传递给它。 如果我们不是重写此行使用基于状态的过载,以及一个手动缓存的委托,我们能够避免倒闭和这两个分配:
private static readonly Action< object > s_cancellationRegistration =
s => ((TaskCompletionSource< bool >)s).TrySetResult( true );
…
using (cancellationToken.Register(s_cancellationRegistration, tcs))
重新运行探查证实这两个分配不再发生:
从今天开始分析!
分析,发现和消除热点,然后将再次围绕这个周期是改善代码的性能,常见的方法是否使用CPU分析器和内存分析器。 所以,如果你发现内存分配有可能程序的瓶颈,不妨尝试.NET的内存分配分析器。
Visual Studio的.NET内存分配分析器解析的更多相关文章
- [图解tensorflow源码] [转载] tensorflow设备内存分配算法解析 (BFC算法)
转载自 http://weibo.com/p/1001603980563068394770 @ICT_吴林阳 tensorflow设备内存管理模块实现了一个best-fit with coales ...
- visual studio如何检查内存泄露?
Visual Studio有专门的插件叫做Visual Leak Detector (VLD)Visual Leak Detector for Visual C++ 2008/2010/2012/20 ...
- java 内存分配全面解析
JVM是什么? 首先要知道的是Java程序运行在JVM(Java Virtual Machine,Java虚拟机)上;可以把JVM理解成Java程序和操作系统之间的桥梁,JVM实现了 Java的平台无 ...
- Java静态内存与动态内存分配的解析
1. 静态内存 静态内存是指在程序开始运行时由编译器分配的内存,它的分配是在程序开始编译时完成的,不占用CPU资源. 程序中的各种变量,在编译时系统已经为其分配了所需的内存空间,当该变量在作用域内使用 ...
- 【Visual Studio】简单内存泄漏检测方法 解决 Detected memory leaks! 问题(转)
原文转自 http://blog.csdn.net/u011430225/article/details/47840647 我的环境是: XP SP2.VS2003 最近在一个项目中, 程序退出后都出 ...
- 使用 Visual Studio 分析器找出应用程序瓶颈(转)
使用 Visual Studio 分析器找出应用程序瓶颈 Hari Pulapaka and Boris Vidolov 本文讨论: 以性能瓶颈为目标 应用程序代码分析 比较分析数据 性能报告 本文使 ...
- 使用 Visual Studio 分析器找出应用程序瓶颈
VS的性能分析工具 性能分析工具的选择 打开一个“性能分析”的会话:Debug->Start Diagnotic Tools Without Debugging(或按Alt+F2),VS2013 ...
- 在 Visual Studio 中安装 FxCop 分析器
本文转自 微软官网 : https://docs.microsoft.com/zh-cn/visualstudio/code-quality/install-fxcop-analyzers?view= ...
- Visual Studio 2022 预览版3 最新功能解说
我们很高兴地宣布Visual Studio 2022 的第三个预览版问世啦!预览版3 提供了更多关于个人和团队生产力.现代开发和持续创新等主题的新功能.在本文中,我们将重点介绍Visual Studi ...
随机推荐
- Python之倒序访问list
我们还是用一个list按分数从高到低表示出班里的3个同学: >>> L = ['Adam', 'Lisa', 'Bart'] 这时,老师说,请分数最低的同学站出来. 要写代码完成这个 ...
- 使用自定义 URL 实现控制器之间的跳转-b
一个app往往有很多界面,而界面之间的跳转也就是对应控制器的跳转,控制器的跳转一般有两种情况 push 或者 modal,push 和 modal 的默认效果是系统提供的 文章配图 1. 概述 系统提 ...
- 仿window阿里旺旺登陆界面,打印机吐纸动画效果-b
偶然的机会发现window的阿里旺旺的登陆效果蛮有意思的,于是就模仿着做了一下打印机吐纸的动画效果看起来很神奇的东西,实现起来却不难,下面我给大家看下主要的源码. - (void)createUI{ ...
- c++ 类的对象与指针
这里首先我们需区分一下指针数组和数组指针. 指针数组:int *p[4];它最终是个数组,只是这个数组存储的是4个指向int类型的指针. 数组指针:int (*P)[4];它最终是个指针,表示一个指向 ...
- jsp include包含html页面产生的乱码问题
大家都知道在jsp中include的有两种方式,一种是<jsp:include page="">,另一种是<%@ include file="" ...
- easyui源码翻译1.32--Window(窗口)
前言 扩展自$.fn.panel.defaults.使用$.fn.window.defaults重写默认值对象.下载该插件翻译源码 窗口控件是一个浮动和可拖拽的面板可以用作应用程序窗口.默认情况下,窗 ...
- perl的输出缓冲
今天写一个小脚本的时候,需要即时输出当前进度到命令行上,并即时将重要数据写入报告文件中.但是perl默认是有输出缓冲的,显示到命令行上必须以\n结尾才行,输出到文件中,回车了都不行,非得要等缓冲区 ...
- Spring事务传播机制
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播,即协调已经有事务标识的方法之间的发生调用时的事务 ...
- Sublime Text 插件 autoprefixer
Sublime Text 早就有插件(Sublime Prefixr)使用 prefixr 的 API 来自动完成 CSS 前缀,但是 autoprefixer 更牛,这款可使用 Can I Use ...
- hadoop2.2编程:MRUnit——Test MaxTemperatureMapper
继承关系1 1. java.lang.Object |__ org.apache.hadoop.mapreduce.JobContext |__org.apache.hadoop.mapreduce. ...