Blazor/C#中如何正确释放Delegate?已出现内存泄漏问题
解决Blazor委托导致的内存泄漏问题
问题根源
你的代码存在两个核心问题:
- 每次
OnParametersSetAsync都会创建新的KeyActionDelegate实例并更新到Attributes字典,但旧的委托引用并未被清理,DOM事件系统或Attributes字典可能仍持有旧委托的引用,而委托又捕获了组件实例,导致组件无法被GC回收。 - 销毁组件时仅将
_keyActionDelegate置为null,但未从Attributes字典中移除onkeydown的绑定,残留的委托引用依然会阻止组件被回收。
具体修复方案
1. 在组件销毁时清理Attributes中的事件绑定
修改ComponentDispose方法,不仅置空委托,还要从Attributes中移除对应的事件键,切断委托与组件的关联:
internal override void ComponentDispose() { base.ComponentDispose(); // 从Attributes中移除onkeydown的绑定 if (Attributes != null && Attributes.ContainsKey("onkeydown")) { Attributes.Remove("onkeydown"); } _keyActionDelegate = null; }
注意:去掉async修饰符,因为Dispose方法不支持异步逻辑,Blazor组件销毁流程仅同步执行Dispose,若需异步清理需实现IAsyncDisposable接口
2. 避免重复创建委托实例
每次OnParametersSetAsync创建新委托会增加不必要的引用,可改为仅在组件初始化时创建一次委托;若确实需要在参数变更时更新委托,需先移除旧绑定再添加新的:
private delegate Task KeyActionDelegate(KeyboardEventArgs e); private KeyActionDelegate _keyActionDelegate; protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); // 仅在初始化时创建一次委托 _keyActionDelegate = new KeyActionDelegate(keyActionHandler); Attributes = BaseUtils.UpdateDictionary("onkeydown", _keyActionDelegate, Attributes); } // 若需在参数变更时更新委托,执行先删后加的逻辑 protected override async Task OnParametersSetAsync() { await base.OnParametersSetAsync(); if (_keyActionDelegate != null && Attributes != null) { // 先移除旧的绑定 Attributes.Remove("onkeydown"); } _keyActionDelegate = new KeyActionDelegate(keyActionHandler); Attributes = BaseUtils.UpdateDictionary("onkeydown", _keyActionDelegate, Attributes); }
3. 使用弱引用委托(可选)
如果无法确保及时清理委托引用,可以将委托包装为弱引用,让GC在组件无其他引用时能够正常回收:
private WeakReference<KeyActionDelegate> _weakKeyActionDelegate; protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); var handler = new KeyActionDelegate(keyActionHandler); _weakKeyActionDelegate = new WeakReference<KeyActionDelegate>(handler); Attributes = BaseUtils.UpdateDictionary("onkeydown", async (e) => { if (_weakKeyActionDelegate.TryGetTarget(out var target)) { await target(e); } }, Attributes); } internal override void ComponentDispose() { base.ComponentDispose(); Attributes?.Remove("onkeydown"); _weakKeyActionDelegate = null; }
这种方式需要注意:当组件被GC回收后,事件触发时不会执行任何逻辑,适合非关键的键盘事件处理场景
4. 确认BaseUtils.UpdateDictionary的实现
检查BaseUtils.UpdateDictionary方法是否正确替换旧的委托值,而非追加或保留旧引用。如果该方法是添加而非覆盖已存在的键值,会导致多个委托实例被保留,进一步加剧内存泄漏问题,需确保其逻辑为:键存在则覆盖值,否则添加新键值对。
内容的提问来源于stack exchange,提问作者Vigneshwaran Govindharajan
相关产品推荐
相关产品推荐

