HAA0502 显式新引用类型分配

Kgn*_*web 5 c# clr heap-memory asp.net-core-2.1

我有ASP.Net Core 2.1C#申请。我正在使用Clr 堆分配分析器

https://marketplace.visualstudio.com/items?itemName=MukulSabharwal.ClrHeapAllocationAnalyzer

其中一种方法如下所示

前#1

public void ConfigureServices(IServiceCollection services) {

services.AddSingleton<IPocoDynamo>(serviceProvider => {
    var pocoDynamo = new PocoDynamo(serviceProvider.GetRequieredService<IAmazonDynamoDB>());
    pocoDynamo.SomeMethod();
    return pocoDynamo;
});
Run Code Online (Sandbox Code Playgroud)

}

例#2

public async Task<EventTO> AddEvent(EventTO eventObj)
{
  try
    {       
      throw new Exception("Error!");
    }
 catch (Exception ex)
 {
   Logger.Log(ex, eventObj);
   return null;
  }
}
Run Code Online (Sandbox Code Playgroud)

我在整个应用程序中使用 DI。但无论分析器在哪里找到new关键字,它都会发出警告:

HAA0502 显式新引用类型分配

此外,无论何时使用 Lambda 表达式,它都会发出警告(如 ex#1 中)

Warning HAA0301 Heap allocation of closure Captures:
Run Code Online (Sandbox Code Playgroud)

造成这种情况的原因是什么以及如何解决?

谢谢!

Pie*_*ier 3

堆分配分析器用于标记代码执行的所有分配。这不是您希望始终打开的东西:考虑以下愚蠢的代码

        public static string MyToString(object? o)
        {
            if (o == null)
                throw new ArgumentNullException(nameof(o)); // HAA0502 here

            return o.ToString() ?? string.Empty;
        }
Run Code Online (Sandbox Code Playgroud)

分析器将以警告的形式发出 HAA0502作为标记行上的信息,告诉您正在分配新实例。现在,在这种情况下,您正在做什么是显而易见的,而且这是一个微不足道的警告,但分析器的目的是帮助您发现可能会使您的代码变得更慢的讨厌的分配。

现在考虑这个愚蠢的代码:

        public static void Test1()
        {
            for (int i = 0; i < 100; i++)
            {
                var a = i + 1;

                var action = new Action(
                    () =>   // HAA0301 Heap allocation of closure Capture: a
                    {
                        Console.WriteLine(a);
                    }
                );

                action();
            }
        }
Run Code Online (Sandbox Code Playgroud)

除了new Action(因为我们正在创建新对象而被标记为 HAA0502 之外,lambda 上还有一个额外的警告:HAA0301。这就是分析器变得更加有用的原因:分析器在这里告诉您运行时将创建一个包含捕获的变量的新对象a。如果您对此不熟悉,您可能会认为该代码会被转换为类似这样的内容(仅用于解释目的):

        private sealed class Temp1
        {
            public int Value1 { get; }

            public Temp1(int value1)
            {
                Value1 = value1;
            }

            public void Method1()
            {
                Console.WriteLine(Value1);
            }
        }

        public static void Test1()
        {
            for (int i = 0; i < 100; i++)
            {
                var a = i + 1;

                var t = new Temp1(a);
                t.Method1();
            }
        }
Run Code Online (Sandbox Code Playgroud)

在后面的代码中,很明显,在每次迭代中您都在分配一个对象。

您可能遇到的主要问题是:分配对象是一个问题吗?在 99.9% 的情况下,这不是问题,您可能会接受编写可读、精确和简洁的代码的简单性,而无需处理低级细节,如果您遇到性能问题(即剩余的 0.01%),分析器将可以非常方便,因为它一次性显示您或代表您的编译器正在分配某些内容。分配对象需要未来的垃圾收集器周期来回收内存。

关于您的代码,您正在通过 DI 使用工厂模式初始化服务:该代码运行一次。因此,您分配一个新对象也就不足为奇了。因此您可以安全地抑制这部分代码的警告。您可以使用IDE来生成抑制代码。这就是为什么我建议禁用分析器并仅在寻找性能问题时才启用它。