首页 > 解决方案 > .NET 内存泄漏 - 增加峰值,然后回到基线

问题描述

我对 Azure-app-service-webjob 的生产中的内存泄漏感到头疼。

内存使用情况

这是一个后台工作进程,按计划从队列中读取工作。大约每 10 分钟,内存使用量就会达到峰值,然后(大约 10 分钟后)会恢复到正常基线。每个尖峰每次都略高。直到最终,峰值达到足够高(> 80% 说),因此需要越来越长的时间才能返回基线,最后锁定。

我已经非常详细地登录了。没有大量或大量的数据库查询,并且没有任何处理操作花费超过几秒钟的时间。很难获得非常清晰的跟踪,因为在 10 分钟的周期内可能会发生 15-30 次不同的操作。

反正。不久前,当它处于“最大化”阶段时,我得到了一个完整的内存转储,将其插入 WinDbg。尽管数据库中只有几千个,但内存中有数百万个某种类型的实体框架 (6) 实体。

我无法在本地复制。所以我在这个实体类型的构造函数中添加了一些代码——它保留了从构造函数中看到Dictionary<string,long>某个特定的次数。Environment.StackTrace我正在等待“最大化”发生,但是远程连接,目前看起来很标准/正常。

鉴于这些对象/可能/随着时间的推移而增加,但这并不能解释增加的峰值并返回基线。可以?

我还刚刚在“基线”期间捕获了完整的内存转储,然后是“小峰值”(根据图像仅运行了几个小时)。我有基本的 WinDbg 技能。

无论如何,我的问题/混淆的原因:

  1. 如何确定两个完整内存转储之间的差异?
  2. 有没有人见过这样的事情?
  3. 什么可能导致内存中的峰值每次都增长?
  4. 如果它是内存泄漏,为什么它会在峰值之间返回到基线?

我想认为没有魔法发生,但我根本找不到与尖峰相吻合的东西:

标签: .netmemory-leakswindbg

解决方案


如何确定两个完整内存转储之间的差异?

在 WinDbg 中很难做到。如果您注意使用正确的格式,使用Jetbrains dotMemory等内存分析工具会容易得多,它可以导入原始转储

有没有人见过这样的事情?

是的。

什么可能导致内存中的峰值每次都增长?尽管数据库中只有几千个,但内存中有数百万个某种类型的实体框架 (6) 实体。

如果你有一个 O(n²) 循环,比如

foreach(...)
    foreach(...)
        CreateAnObject();

那么数据库中的 1000 行可能会创建 1.000.000 个对象。如果您只是在数据库中再添加一行,那么下次您运行相同的查询时就会多出 2001 个对象。

如果它是内存泄漏,为什么它会在峰值之间返回到基线?

我不会将尖峰行为称为内存泄漏。看起来还不错。但是,您需要考虑到在某个时间点,RAM 不再足够,并且会发生交换到硬盘的情况。然后,您的应用程序会变得慢得多。也许你可以改变算法。

但是,请注意基线不是恒定的:

基线

所以你确实有内存泄漏,但它与尖峰无关。我不会比较尖峰和非尖峰,而是比较两个基线。

数据库记录的数量逐渐增加,但只有几千条,如果进程重新启动,内存问题会重新设置

如果基线泄漏得到修复,这可能会得到解决。

尽管峰值持续约 10 分钟,但根据日志记录,似乎没有任何操作需要超过几秒钟

没有手术?你有什么手术?方法调用?您如何确保测量所有方法调用?下一次,您可能还想添加一个 CPU% 图表。


推荐阅读