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内存分配分析器解析的更多相关文章

  1. [图解tensorflow源码] [转载] tensorflow设备内存分配算法解析 (BFC算法)

    转载自 http://weibo.com/p/1001603980563068394770   @ICT_吴林阳 tensorflow设备内存管理模块实现了一个best-fit with coales ...

  2. visual studio如何检查内存泄露?

    Visual Studio有专门的插件叫做Visual Leak Detector (VLD)Visual Leak Detector for Visual C++ 2008/2010/2012/20 ...

  3. java 内存分配全面解析

    JVM是什么? 首先要知道的是Java程序运行在JVM(Java Virtual Machine,Java虚拟机)上;可以把JVM理解成Java程序和操作系统之间的桥梁,JVM实现了 Java的平台无 ...

  4. Java静态内存与动态内存分配的解析

    1. 静态内存 静态内存是指在程序开始运行时由编译器分配的内存,它的分配是在程序开始编译时完成的,不占用CPU资源. 程序中的各种变量,在编译时系统已经为其分配了所需的内存空间,当该变量在作用域内使用 ...

  5. 【Visual Studio】简单内存泄漏检测方法 解决 Detected memory leaks! 问题(转)

    原文转自 http://blog.csdn.net/u011430225/article/details/47840647 我的环境是: XP SP2.VS2003 最近在一个项目中, 程序退出后都出 ...

  6. 使用 Visual Studio 分析器找出应用程序瓶颈(转)

    使用 Visual Studio 分析器找出应用程序瓶颈 Hari Pulapaka and Boris Vidolov 本文讨论: 以性能瓶颈为目标 应用程序代码分析 比较分析数据 性能报告 本文使 ...

  7. 使用 Visual Studio 分析器找出应用程序瓶颈

    VS的性能分析工具 性能分析工具的选择 打开一个“性能分析”的会话:Debug->Start Diagnotic Tools Without Debugging(或按Alt+F2),VS2013 ...

  8. 在 Visual Studio 中安装 FxCop 分析器

    本文转自 微软官网 : https://docs.microsoft.com/zh-cn/visualstudio/code-quality/install-fxcop-analyzers?view= ...

  9. Visual Studio 2022 预览版3 最新功能解说

    我们很高兴地宣布Visual Studio 2022 的第三个预览版问世啦!预览版3 提供了更多关于个人和团队生产力.现代开发和持续创新等主题的新功能.在本文中,我们将重点介绍Visual Studi ...

随机推荐

  1. 【2】认识Bootstrap

    作为当下最流行的前端开发框架Bootstrap,它可大大简化网站开发过程,从而深受广大开发者的喜欢,当然你去它的官网中文网站就能看到大大的小标定义:“简洁.直观.强悍.移动设备优先的前端开发框架,让w ...

  2. Django基本介绍

    Django板块分类: 1.urls.py  网址的入口(关联到views.py中的一个函数) 2.views.py 处理用户发起的请求,从urls.py中对应过来,通过渲染templates中的网页 ...

  3. sparkR原理

    p.p1 { margin: 0.0px 0.0px 10.0px 0.0px; font: 11.0px "Times New Roman"; min-height: 12.0p ...

  4. python的生成器

    1.生成器 >>> def func1(): ... yield 0 ... yield 1 ... >>> a=func1() >>> a.ne ...

  5. 【DB】SQLite学习笔记

    下载”System.Data.SQLite.DLL”,程序中添加引用即可 //创建数据库文件 SQLiteConnection.CreateFile("sqlitetest.db" ...

  6. IIS修改队列长度

    Internet Information Services (IIS) 限制了在任何给定时间可在队列中等待的应用程序池请求的最大数量.如果达到此限制,则所有新请求都将被拒绝,而且用户将收到错误消息“5 ...

  7. javascript高级编程笔记02(基本概念)

    ParseInt()函数: 由于Number函数在转换字符串时比较复杂而且不合理,我们常常转换字符串都用parseInt函数, Parseint函数规则: 忽略字符串前面的空格,直到找到第一个非空格字 ...

  8. Automotive Security的一些资料和心得(5):Privacy

    1. Introduction 1.1 "Customers own their data and we can be no more than the trsted stewards of ...

  9. asp.net gridview 绑定图片字段,图片不显示

    在浏览器中查看,图片属性. 右键查看,若后面出现若干%20 可使用以下办法解决. 备份表数据,然后删除表,把图片路径字符串在数据库中应使用varchar()类型. 原因可以查看vchar()  var ...

  10. delphi xe5 android 控制蓝牙

    本文部分内容摘自: http://www.pclviewer.com/android/用以下代码中的接口实现控制蓝牙的开.关及详细信息 unit Androidapi.JNI.BluetoothAda ...