V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
drymonfidelia
V2EX  ›  程序员

为什么把 Scoped 注入到 Singleton 会导致内存泄露?

  •  
  •   drymonfidelia · 11 天前 · 928 次点击
    public class SingletonService
    {
        private readonly DbContext _dbContext;
    
        public SingletonService(DbContext dbContext)
        {
            _dbContext = dbContext;
        }
    }
    
    public class SomeController
    {
        private readonly SingletonService _singletonService;
        private readonly DbContext _dbContext;
    
        public SomeController(SingletonService singletonService, DbContext dbContext)
        {
            _singletonService = singletonService;
            _dbContext = dbContext;
        }
    }
    

    昨天晚上群里别人在讨论的问题,他们说这样注入会导致 SingletonService 里的 DbContext 释放不掉。我不是很理解,Singleton 每次运行都是同一个,SingletonService 里的 DbContext 永远只会创建一次,为什么会内存泄漏?

    例子是 C#的,别的有依赖注入的语言应该也一样。

    11 条回复    2024-12-11 17:54:26 +08:00
    crysislinux
        1
    crysislinux  
       11 天前 via Android
    我觉得没问题,又不会持续增长
    timethinker
        2
    timethinker  
       11 天前
    就你贴出的这个例子而言,是的,DbContext 永远只会创建一次。

    但是这种做法是有问题的,仅就这个例子而言,DbContext 不是线程安全的,这意味着当多个线程同时操作一个 DbContext 对象可能会引发数据竞争问题,破坏其内部状态的一致性。

    微软官网是这样描述 DbContext 的生命周期的:The lifetime of a DbContext begins when the instance is created and ends when the instance is disposed. A DbContext instance is designed to be used for a single unit-of-work. This means that the lifetime of a DbContext instance is usually very short.

    工作单元模式简单的来讲可以理解为对应于数据库的一个事务范围,在这个事务范围进行的操作会被追踪然后被提交。除非你确实有理由要一直保持一个 DbContext 的实例,并且考虑了线程安全问题,那么这么做就没什么问题。
    cuso45h2o
        3
    cuso45h2o  
       11 天前
    DbContext 是只会创建一次,但是因为它不会被 dispose ,如果它的代码有问题产生了大量 tracked entities ,这一个 DbContext 也会导致内存泄露。
    irisdev
        4
    irisdev  
       11 天前
    @cuso45h2o 楼主,请教个问题,如果像 op 这样写,后续请求造成的 changes 会 append 在这个单例的 dbcontext 上吗
    ch3nz
        5
    ch3nz  
       11 天前
    会“可能导致内存泄漏和行为异常”而不是“一定导致”
    你的例子是属于“一定导致”。
    比如你给数据库一顿操作,dbContext track 了所有数据库变化,本该释放,但是被 singleton 持有而不能释放。
    irisdev
        6
    irisdev  
       11 天前
    记得.net 不允许在 singleton 中引用 scope 吧,这么写不会报错吗
    cuso45h2o
        7
    cuso45h2o  
       11 天前 via iPhone   ❤️ 1
    回复#4 如果在 Singleton 里发生的更改会累积。
    #6 Microsoft.Extensions.DependencyInjection 会检测这种情况,如果把 scoped 注入到 Singleton 会报错 Cannot consume scoped service from singleton 。因为 OP 没说用的是什么 DI 接口,我假设 OP 自己实现了一个简易 DI ,这样注入会导致潜在的内存泄露的问题。
    @irisdev
    irisdev
        8
    irisdev  
       11 天前 via Android
    @cuso45h2o 明白了,谢谢解惑!
    nikenidage1
        9
    nikenidage1  
       11 天前
    是的 楼上很多人都说了。
    所以.net 的默认 DI 不允许这么做……
    gbw1992
        10
    gbw1992  
       10 天前
    看到了这个例子,突然想起来知乎中一个回答说,ef 至今没有 "解决" 跨线程的 bug
    ne6rd
        11
    ne6rd  
       10 天前
    另外补充一点,一般不推荐在 Singleton 里用 Scoped 的类,但是非要用的话,也是有方法的。
    不能直接注入,但是可以注入一个 IServiceScopeFactory, CreateScope(), 然后 scope.ServiceProvider.GetServices<>();
    这个拿到的 Scoped 实例只能在 method 级别里访问,不能把它存到实例属性上给其他 method 共享。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1259 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 17:56 · PVG 01:56 · LAX 09:56 · JFK 12:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.