You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Zenject作为DI容器时对象池结构引发内存泄漏问题排查

问题分析与优化建议

问题根源排查

出现实例倍增和Leaked Managed Shell的核心原因是引用泄漏,具体场景如下:

  1. TestRegistry未清理引用:TestRegistry的字典持有TestFacade的引用,场景卸载时如果没清空字典,这些引用会阻止GC回收TestFacade实例,导致旧实例残留,重复加载场景时新实例又被注册,最终数量倍增。
  2. 对象池生命周期未正确处理:TestFacade实现了IPoolable<IMemoryPool>和IDisposable,但如果OnDespawned或Dispose方法中没有从TestRegistry移除自身、销毁TestComponent,会导致旧实例的引用一直存在。
  3. 跨场景依赖持有:如果Scene1的TestScript注入了Scene2的TestSpawner/TestRegistry,那么Scene2卸载时,这些服务会被Scene1的对象持有引用,无法被正确销毁。
  4. Zenject绑定生命周期错误:若TestRegistry、TestSpawner被错误绑定到全局容器而非SceneContext的场景级容器,会导致场景卸载时这些实例不会被销毁,重复加载场景时会新增实例。

优化建议

1. 修复引用泄漏的核心操作

  • 在TestFacade的OnDespawned(IPoolable接口方法)或Dispose方法中,主动调用TestRegistry的移除方法,将自身从字典中删除,同时销毁TestComponent(因为它无独立存在意义):
    public void OnDespawned()
    {
        _testRegistry.RemoveInstance(this);
        Destroy(_testComponent);
        _testComponent = null;
    }
    
  • 给TestRegistry实现IDisposable接口,在Dispose中清空字典,确保场景销毁时释放所有持有的引用:
    public void Dispose()
    {
        _facadeDictionary.Clear();
    }
    
  • 检查Zenject绑定,确保TestRegistry、TestSpawner都绑定到SceneContext的容器,使用场景级生命周期:
    // 在SceneContext的Installer中
    Container.Bind<TestRegistry>().AsSingle().NonLazy();
    Container.Bind<TestSpawner>().AsSingle().NonLazy();
    
  • 禁止跨场景直接注入依赖:Scene1的TestScript不要直接注入Scene2的TestSpawner,改用事件/信号系统(如Zenject Signal)触发场景内的生成/销毁逻辑。

2. Guid作为ID的合理性分析

  • Guid作为实例唯一ID是完全合理的,尤其是在对象池复用场景下,能彻底避免ID重复问题,适合作为TestRegistry字典的键。
  • ref传递Guid的场景:仅当需要在方法内部修改外部Guid变量时才需要用ref传递,比如创建实例时生成Guid并回传外部变量。如果只是将Guid作为注册键传递给TestRegistry,直接传值即可,无需ref。

3. 整体结构优化

  • 将TestComponent改为TestFacade GameObject的直接组件,通过GetComponent<TestComponent>()获取,而非在Construct中动态创建,这样TestFacade销毁/回池时,TestComponent会自动跟随处理,减少手动管理成本。
  • 改用Zenject内置的MemoryPool绑定对象池,替代自定义TestSpawner,Zenject会自动处理IPoolable的生命周期:
    Container.BindMemoryPool<TestFacade, TestFacade.Pool>()
        .WithInitialSize(5)
        .FromComponentInNewPrefab(_testFacadePrefab)
        .UnderTransformGroup("TestFacades");
    
  • 给TestRegistry添加IInitializable和IDisposable接口,让Zenject在场景初始化和销毁时自动调用对应方法,无需手动触发:
    public class TestRegistry : IInitializable, IDisposable
    {
        public void Initialize()
        {
            // 初始化逻辑
        }
    
        public void Dispose()
        {
            _facadeDictionary.Clear();
        }
    }
    

内容的提问来源于stack exchange,提问作者stroibot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 06:21:00