• Memory leak by misusing Autofac


    Recently I’ve found out that we can easily cause a memory leaks in our .net application by improper usage of the dependency injection container Autofac.

    The case of this problem concerns only components that implements IDisposable interface, so are meant to handle some unmanaged resources and need to be disposed when no longer needed. Let me show you how to reproduce this problem in an example. For the sake of presentation both, the component and testing code are as simple as possible and do nothing really usable. 
    The disposable component is defined as follows

     1 public class MyComponent : IDisposable
     2 {
     3     public MyComponent()
     4     {
     5         //component created
     6     }
     7 
     8     ~MyComponent()
     9     {
    10         //component garbage collected
    11     }
    12 
    13     public void Dispose()
    14     {
    15         //component disposed
    16     }
    17 }

    So we got a constructor, finalizer and the Dispose() method. The most important for us is when the finalizer is called, which means that the instance of MyComponent is being released and collected by a garbage collector. 
    Now let’s do write some testing code with use of Autofac container

     1 static void Main(string[] args)
     2 {
     3     var builder = new ContainerBuilder();
     4     builder.RegisterType<MyComponent>();
     5     var container = builder.Build();
     6     test(container);
     7     GC.Collect();
     8     GC.WaitForPendingFinalizers();
     9 }
    10 
    11 public static void test(ILifetimeScope scope)
    12 {
    13     using (var auxComponent = scope.Resolve<MyComponent>())
    14     { }
    15 }

    First we register MyComponent and build the container. Then, in test method, we ask the container to resolve the component for us in a using block. As we know the disposable object auxComponent, created in scope ofusing block is automatically disposed when runtime leaves the using block. And that is true as we can easily check by placing a breakpoint in Dispose() method of the component and running above code. 
    However, the using block is also a scope for the instance of MyComponent which we create in it, since auxComponent variable is not reachable outside that block. Therefore we expect that the instance, we created, will be garbage collected (the component’s finalizer will be called), when we call lines 7 and 8. Unfortunately this is not happening.

    The Autofac is designed in the way that by default it holds a references to all disposable components we request, and call Dispose() on them when the whole container is disposed! Yeap, somehow to avoid the situation we forgot to call dispose on those. Unfortunately that also means that those components are not collected by a GC as long as the container itself is collected!

    This and other aspects of Autofac are excellently described by Nicholas Blumhardt in his blog post An Autofac Lifetime Primer.

    Her I will only show you a proper and recommended way of working with IDisposable components and Autofac. 
    The general advice using Autofac is to never resolve components from the root container. Always resolve and then dispose the lifetime scopes (ILifetimeScope). This prevents a memory leaks and facilitates creating units of work in the application. IDIsposable component resolved within a lifetime scope is associated to it and stays alive as long as the containing lifetime scope stays alive. 
    Back to our example, let’s modify the code so that we will use a nested lifetime scope.

     1 static void Main(string[] args)
     2 {
     3     var builder = new ContainerBuilder();
     4     builder.RegisterType<MyComponent>();
     5     var container = builder.Build();
     6     test(container);
     7     GC.Collect();
     8     GC.WaitForPendingFinalizers();
     9 }
    10 
    11 public static void test(ILifetimeScope scope)
    12 {
    13     var localScope = scope.BeginLifetimeScope();
    14     using (var auxComponent = localScope.Resolve<MyComponent>())
    15     { }
    16 }

    Now you can see that the finalizer of the MyComponent class is called when debugger reaches lines 7 and 8. The component is resolved from nested lifetime scope and is successfully collected since the localScope exists only within a test method. 
    Here the component is disposed because we keep it within a using block. If we want the Autofac to handle the disposal of the resolved components for us we need to dispose the lifetime scope itself instead.

     1 static void Main(string[] args)
     2 {
     3     var builder = new ContainerBuilder();
     4     builder.RegisterType<MyComponent>();
     5     var container = builder.Build();
     6     test(container);
     7     GC.Collect();
     8     GC.WaitForPendingFinalizers();
     9 }
    10 
    11 public static void test(ILifetimeScope scope)
    12 {
    13     using (var localScope = scope.BeginLifetimeScope())
    14     {
    15         var auxComponent = localScope.Resolve<MyComponent>();
    16         var auxComponent2 = localScope.Resolve<MyComponent>();
    17     }
    18 }

    Now both instances of the MyComponent class are disposed and available for GC collection at the end of usingblock.

  • 相关阅读:
    Yii2 分页
    Yii2 或者当前登录用户帐号
    css3媒体查询判断移动设备横竖屏
    Javascript操作Tr隐藏显示变形~
    php注释标准
    匹配一段html中所有的src
    数据库遇到错误(随时补充)
    NetCore-缓存文件上传和文件流上传
    SVN跨服务器版本迁移
    发票同步微信卡包
  • 原文地址:https://www.cnblogs.com/lenmom/p/9235601.html
Copyright © 2020-2023  润新知