gpt4 book ai didi

memory-leaks - DI 容器泄漏内存或 BenchmarksDotNet MemoryDiagnoser 提供不准确的测量?

转载 作者:行者123 更新时间:2023-12-04 05:28:57 25 4
gpt4 key购买 nike

简介

我们正在尝试使用 BenchmarksDotNet 捕获潜在的内存泄漏。 .

为了简单起见,这里是一个简单的 TestClass :

public class TestClass 
{
private readonly string _eventName;

public TestClass(string eventName)
{
_eventName = eventName;
}

public void TestMethod() =>
Console.Write($@"{_eventName} ");
}

我们正在通过 netcoreapp2.0 中的 NUnit 测试执行基准测试。 :
[TestFixture]
[MemoryDiagnoser]
public class TestBenchmarks
{
[Test]
public void RunTestBenchmarks() =>
BenchmarkRunner.Run<TestBenchmarks>(new BenchmarksConfig());

[Benchmark]
public void TestBenchmark1() =>
CreateTestClass("Test");

private void CreateTestClass(string eventName)
{
var testClass = new TestClass(eventName);
testClass.TestMethod();
}
}

测试输出包含以下摘要:
         Method | Mean | Error | Allocated |
--------------- |-----:|------:|----------:|
TestBenchmark1 | NA | NA | 0 B |

测试输出还包含所有 Console.Write输出证明 0 B这意味着没有内存泄漏,而不是由于编译器优化而没有运行代码。

问题

当我们试图解决 TestClass 时,困惑就开始了。与 TinyIoC容器:
[TestFixture]
[MemoryDiagnoser]
public class TestBenchmarks
{
private TinyIoCContainer _container;

[GlobalSetup]
public void SetUp() =>
_container = TinyIoCContainer.Current;

[Test]
public void RunTestBenchmarks() =>
BenchmarkRunner.Run<TestBenchmarks>(new BenchmarksConfig());

[Benchmark]
public void TestBenchmark1() =>
ResolveTestClass("Test");

private void ResolveTestClass(string eventName)
{
var testClass = _container.Resolve<TestClass>(
NamedParameterOverloads.FromIDictionary(
new Dictionary<string, object> {["eventName"] = eventName}));
testClass.TestMethod();
}
}

摘要表明 1.07 KB 被泄露。
         Method | Mean | Error | Allocated |
--------------- |-----:|------:|----------:|
TestBenchmark1 | NA | NA | 1.07 KB |
Allocated值与 ResolveTestClass 的数量成比例增加来自 TestBenchmark1 的电话,总结为
[Benchmark]
public void TestBenchmark1()
{
ResolveTestClass("Test");
ResolveTestClass("Test");
}


         Method | Mean | Error | Allocated |
--------------- |-----:|------:|----------:|
TestBenchmark1 | NA | NA | 2.14 KB |

这表明 TinyIoC保留对每个已解析对象的引用(根据源代码,这似乎不是真的)或 BenchmarksDotNet测量包括在标有 [Benchmark] 的方法之外的一些额外内存分配。属性。

两种情况下使用的配置:
public class BenchmarksConfig : ManualConfig
{
public BenchmarksConfig()
{
Add(JitOptimizationsValidator.DontFailOnError);

Add(DefaultConfig.Instance.GetLoggers().ToArray());
Add(DefaultConfig.Instance.GetColumnProviders().ToArray());

Add(Job.Default
.WithLaunchCount(1)
.WithTargetCount(1)
.WithWarmupCount(1)
.WithInvocationCount(16));

Add(MemoryDiagnoser.Default);
}
}

顺便说一下,替换 TinyIoCAutofac依赖注入(inject)框架并没有太大改变这种情况。

问题

这是否意味着所有 DI 框架都必须为已解析的对象实现某种缓存?意思是 BenchmarksDotNet在给定的示例中以错误的方式使用?使用 NUnit 的组合来寻找内存泄漏是个好主意吗?和 BenchmarksDotNet首先?

最佳答案

我是为 BenchmarkDotNet 实现 MemoryDiagnoser 的人,我很高兴回答这个问题。

但首先我要描述 MemoryDiagnoser 的工作原理。

  • 它通过使用可用的 API 获取分配的内存数量。
  • 它执行一次额外的基准测试迭代。在您的情况下,它是 16 ( .WithInvocationCount(16) )
  • 它通过使用可用的 API 获取分配的内存数量。
  • final result = (totalMemoryAfter - totalMemoryBefore) / invocationCount
    结果有多准确?它与我们使用的可用 API 一样准确: GC.GetAllocatedBytesForCurrentThread()对于 .NET Core 1.1+ 和 AppDomain.MonitoringTotalAllocatedMemorySize对于 .NET 4.6+。

    GC分配量子 defines分配内存的大小。它通常是 8k 字节。

    它的真正含义是:如果我们用 new object() 分配一个对象并且 GC 需要为其分配内存(当前段已满)它将分配 8k 的内存。并且两个 API 都将报告单个对象分配后分配的 8k 内存。
    Console.WriteLine(AppDomain.MonitoringTotalAllocatedMemorySize);
    GC.KeepAlive(new object());
    Console.WriteLine(AppDomain.MonitoringTotalAllocatedMemorySize);

    最终可能会报告:
    x
    x + 8000

    BenchmarkDotNet 如何处理这个问题?我们执行了很多调用(通常是数百万或数十亿),因此尽量减少分配量子大小问题(对我们来说从来不是 8k)。

    如何解决您的问题 :设置 WithInvocationCount到更大的数字(可能是 1000)。

    要验证结果,您可能会考虑使用一些 Memory Profiler。我个人 used Visual Studio 内存分析器,它是 Visual Studio 的一部分。

    另一种选择是使用 JetBrains.DotMemoryUnit .在您的情况下,它很可能是最好的工具。

    关于memory-leaks - DI 容器泄漏内存或 BenchmarksDotNet MemoryDiagnoser 提供不准确的测量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48992038/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com