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

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

  •  
  •   drymonfidelia · 7 天前 · 892 次点击
    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  
       7 天前 via Android
    我觉得没问题,又不会持续增长
    timethinker
        2
    timethinker  
       7 天前
    就你贴出的这个例子而言,是的,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  
       7 天前
    DbContext 是只会创建一次,但是因为它不会被 dispose ,如果它的代码有问题产生了大量 tracked entities ,这一个 DbContext 也会导致内存泄露。
    irisdev
        4
    irisdev  
       7 天前
    @cuso45h2o 楼主,请教个问题,如果像 op 这样写,后续请求造成的 changes 会 append 在这个单例的 dbcontext 上吗
    ch3nz
        5
    ch3nz  
       7 天前
    会“可能导致内存泄漏和行为异常”而不是“一定导致”
    你的例子是属于“一定导致”。
    比如你给数据库一顿操作,dbContext track 了所有数据库变化,本该释放,但是被 singleton 持有而不能释放。
    irisdev
        6
    irisdev  
       7 天前
    记得.net 不允许在 singleton 中引用 scope 吧,这么写不会报错吗
    cuso45h2o
        7
    cuso45h2o  
       7 天前 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  
       7 天前 via Android
    @cuso45h2o 明白了,谢谢解惑!
    nikenidage1
        9
    nikenidage1  
       7 天前
    是的 楼上很多人都说了。
    所以.net 的默认 DI 不允许这么做……
    gbw1992
        10
    gbw1992  
       7 天前
    看到了这个例子,突然想起来知乎中一个回答说,ef 至今没有 "解决" 跨线程的 bug
    ne6rd
        11
    ne6rd  
       7 天前
    另外补充一点,一般不推荐在 Singleton 里用 Scoped 的类,但是非要用的话,也是有方法的。
    不能直接注入,但是可以注入一个 IServiceScopeFactory, CreateScope(), 然后 scope.ServiceProvider.GetServices<>();
    这个拿到的 Scoped 实例只能在 method 级别里访问,不能把它存到实例属性上给其他 method 共享。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3432 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 78ms · UTC 10:51 · PVG 18:51 · LAX 02:51 · JFK 05:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.