【发布时间】:2018-08-06 02:50:02
【问题描述】:
简介
我们正在尝试使用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 在这里意味着没有内存泄漏,而不是因为编译器优化没有运行代码。
问题
当我们尝试用TinyIoC 容器解析TestClass 时,混乱就开始了:
[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 值与来自TestBenchmark1 的ResolveTestClass 调用次数成比例增加,
[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);
}
}
顺便说一句,用Autofac 依赖注入框架替换TinyIoC 并没有太大改变这种情况。
问题
这是否意味着所有 DI 框架都必须为已解析的对象实现某种缓存?这是否意味着BenchmarksDotNet 在给定示例中以错误的方式使用?首先使用NUnit 和BenchmarksDotNet 的组合来寻找内存泄漏是个好主意吗?
【问题讨论】:
标签: memory-leaks autofac tinyioc benchmarkdotnet