【问题标题】:.NET RegEx "Memory Leak" investigation.NET RegEx“内存泄漏”调查
【发布时间】:2023-03-21 16:50:01
【问题描述】:

我最近在 WinForms 应用程序中查看了一些 .NET“内存泄漏”(即意外的、挥之不去的 GC 根对象)。加载然后关闭一个巨大的报告后,即使在进行了几次 gen2 收集之后,内存使用量也没有像预期的那样下降。假设报告控件由杂散事件处理程序保持活动状态,我打开 WinDbg 以查看发生了什么...

使用 WinDbg,!dumpheap -stat 命令报告字符串实例消耗了大量内存。使用!dumpheap -type System.String 命令进一步细化这一点,我发现了罪魁祸首,一个用于报告的 90MB 字符串,地址为 03be7930。最后一步是调用!gcroot 03be7930 以查看哪些对象使其保持活动状态。

我的预期不正确 - 它不是挂在报告控件(和报告字符串)上的未挂钩事件处理程序,而是由 System.Text.RegularExpressions.RegexInterpreter 实例持有,它本身是 System.Text.RegularExpressions.CachedCodeEntry 的后代。现在,Regex 的缓存(在某种程度上)是常识,因为这有助于减少每次使用 Regex 时都必须重新编译的开销。但这与让我的字符串保持活力有什么关系呢?

根据使用 Reflector 的分析,结果表明,每当调用 Regex 方法时,输入字符串都会存储在 RegexInterpreter 中。 RegexInterpreter 保留此字符串引用,直到随后的 Regex 方法调用将新字符串输入其中。我希望通过挂在 Regex.Match 实例和其他实例上来实现类似的行为。链条是这样的:

  • Regex.Split、Regex.Match、Regex.Replace 等
    • 正则表达式运行
      • RegexScanner.Scan(RegexScanner 是基类,RegexInterpreter 是上述子类)。

有问题的正则表达式仅用于报告,很少使用,因此不太可能再次用于清除现有的报告字符串。即使稍后使用 Regex,它也可能正在处理另一份大型报告。这是一个相对严重的问题,只是感觉很脏。

综上所述,我找到了一些关于如何解决或至少解决这种情况的选项。我会先让社区做出回应,如果没有人站出来,我会在一两天内填补任何空白。

【问题讨论】:

  • 您在创建正则表达式时是否使用了Compiled 选项?
  • 不,在这种情况下没有使用Compiled 选项。

标签: .net regex memory-leaks


【解决方案1】:

您使用的是正则表达式的实例还是采用字符串模式的静态正则表达式方法? According to this post,Regex 实例不参与缓存。

【讨论】:

  • 是的,使用静态正则表达式方法是罪魁祸首。您可以通过 Reflector 验证静态方法是否使用了缓存 - 所有静态调用都使用带有“useCache”参数的私有 ctor 创建一个正则表达式。这里的简单解决方案是不使用静态方法。缓存并不重要,因为与处理巨大的输入字符串相比,编译是微不足道的。根据 Regex 的使用方式,其他可能有用的解决方案是通过将 Regex.CacheSize 设置为 0 或在处理源后通过 Regex 运行空字符串来禁用 Regex 缓存。
【解决方案2】:

尝试切换到已编译的正则表达式 - 实例化需要更长的时间,但可能不会受到这种奇怪的泄漏。

请参阅http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regexoptions%28v=VS.100%29.aspx 了解更多信息。

或者,不要保留 Regex 实例超过您需要的时间 - 为每个报告调用创建一个新实例。

【讨论】:

  • 投反对票,因为compiling regular expression will ALWAYS leak memory,按照设计(除非卸载整个 AppDomain,否则无法卸载程序集):There are even more costs for compilation that should mentioned, however. Emitting IL with Reflection.Emit loads a lot of code and uses a lot of memory, and that's not memory that you'll ever get back. 除非您在一次性 AppDomain 中执行此操作,否则会带来很多新的挑战,例如与 AppDomain 内性能相比,AppDomain 间性能较差。
猜你喜欢
  • 2011-03-22
  • 1970-01-01
  • 2012-02-19
  • 1970-01-01
  • 2022-12-06
  • 2013-12-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多