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

如何防止RuntimeMethodInfo对象发生内存泄漏?

问题描述

用dotMemory对比两次内存快照后,发现大量RuntimeMethodInfo对象始终没被回收。这些对象是通过反射获取的方法实例,占用了不少内存,实际应用中可能导致内存过度占用,进而引发性能问题。需要搞清楚这些对象的产生原因,以及对应的清理方法。

内存快照对比

示例与实际应用中的RuntimeMethodInfo对象

可复现代码片段

class Program
{
    static void Main()
    {
        var eventSource = new MyEventSource();

        WeakEventManager<MyEventSource, EventArgs>.AddHandler(eventSource, "MyEvent", EventHandlerMethod);
        MemoryProfiler.GetSnapshot();

        eventSource.RaiseEvent();
        WeakEventManager<MyEventSource, EventArgs>.RemoveHandler(eventSource, "MyEvent", EventHandlerMethod);

        MemoryProfiler.GetSnapshot();
    }

    private static void EventHandlerMethod(object sender, EventArgs e)
    {
    }
}

public class MyEventSource
{
    public event EventHandler<EventArgs> MyEvent;

    public void RaiseEvent()
    {
        MyEvent?.Invoke(this, EventArgs.Empty);
    }
}
成因解析
  • WeakEventManager的反射缓存机制:当调用WeakEventManager.AddHandler并传入事件名称字符串时,内部会通过反射查找事件对应的RuntimeMethodInfo。为了优化后续反射操作的性能,.NET会将这些RuntimeMethodInfo对象存入全局反射缓存池,该缓存由AppDomain级别的引用持有,只要AppDomain不卸载,这些对象就不会被GC回收,即便你已经通过RemoveHandler解绑了事件。
  • 反射对象的缓存特性:.NET中所有通过反射获取的MethodInfo、Type等对象,默认都会被全局缓存,这是框架层面的性能优化,但也会导致这些对象长期驻留内存。
解决办法
  1. 改用强类型的WeakEventManager重载
    避免传入事件名称字符串,改用强类型方式绑定事件,这样能跳过反射步骤,从根源上避免生成和缓存RuntimeMethodInfo:

    // 使用nameof强类型获取事件名称,避免硬编码字符串
    WeakEventManager<MyEventSource, EventArgs>.AddHandler(eventSource, 
        nameof(MyEventSource.MyEvent), EventHandlerMethod);
    // 或者使用更直接的强类型委托重载
    WeakEventManager<MyEventSource, EventArgs>.AddHandler(eventSource, 
        source => source.MyEvent += null, EventHandlerMethod);
    
  2. 谨慎清理反射缓存
    .NET没有公开的API用于清理反射缓存,但可以通过反射调用内部私有方法实现(仅适用于极端场景,存在版本兼容风险):

    // 示例:清理RuntimeMethodInfo的缓存(不推荐生产环境使用)
    var cacheField = typeof(System.Reflection.RuntimeMethodInfo)
        .GetField("s_cache", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
    if (cacheField != null)
    {
        var cache = cacheField.GetValue(null) as System.Collections.IDictionary;
        cache?.Clear();
    }
    
  3. 替换事件绑定方式
    如果业务场景允许,直接使用普通的事件订阅(eventSource.MyEvent += EventHandlerMethod),在不需要时显式解除订阅(eventSource.MyEvent -= EventHandlerMethod),完全避开WeakEventManager带来的反射和缓存问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 15:46:16