HAA0502 显式新引用类型分配

Posted

技术标签:

【中文标题】HAA0502 显式新引用类型分配【英文标题】:HAA0502 Explicit new reference type allocation 【发布时间】:2020-01-28 00:28:03 【问题描述】:

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

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

其中一种方法如下所示

Ex#1

public void ConfigureServices(IServiceCollection services) 

services.AddSingleton<IPocoDynamo>(serviceProvider => 
    var pocoDynamo = new PocoDynamo(serviceProvider.GetRequieredService<IAmazonDynamoDB>());
    pocoDynamo.SomeMethod();
    return pocoDynamo;
);

例如#2

public async Task<EventTO> AddEvent(EventTO eventObj)

  try
           
      throw new Exception("Error!");
    
 catch (Exception ex)
 
   Logger.Log(ex, eventObj);
   return null;
  

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

在任何使用 Lambda 表达式的地方,它都会发出警告(就像在 ex#1 中一样)

Warning HAA0301 Heap allocation of closure Captures:

是什么原因造成的以及如何解决这个问题?

谢谢!

【问题讨论】:

我的猜测是 HAA0502 试图鼓励您使用可以缓存和重用对象的工厂方法。 HAA0301 试图警告您正在分配一个闭包对象以提供对范围比 lambda 表达式更广泛的局部变量的访问。许多采用委托的方法还允许您传入一个参数,这可能使您可以避免这种分配。 您能详细说明一下吗?您使用的分析器应该显示针对引用类型的每个 new 语句的警告,以及(在大多数情况下)针对 lambdas 的警告。你有什么不清楚的地方? 【参考方案1】:

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

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

            return o.ToString() ?? string.Empty;
        

分析器将在标记行以警告信息的形式发出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();
            
        

除了 HAA0502 将在 new Action( 上标记,因为我们正在创建一个新对象,在 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();
            
        

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

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

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

【讨论】:

以上是关于HAA0502 显式新引用类型分配的主要内容,如果未能解决你的问题,请参考以下文章

Java第三章整理

PHP是不是优化数组类型的函数参数,而不是通过引用显式传递,当它们没有被修改时?

Web | JavaScript的引用数据类型强制转换类型

.net 框架中值类型和引用类型的内存分配

go的值类型和引用类型2——内存分配规则

Javascript - 原始类型与引用类型