【问题标题】:.NET Memory Leak - increasing spikes, then back to baseline.NET 内存泄漏 - 增加峰值,然后回到基线
【发布时间】:2018-11-24 08:40:35
【问题描述】:

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

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

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

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

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

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

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

无论如何,我的问题/困惑的原因:

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

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

  • 数据库记录数逐渐增加,但只有几千条,如果进程重新启动,内存问题会重新设置
  • 根据日志记录,尽管峰值持续约 10 分钟,但似乎没有任何操作需要超过几秒钟的时间

【问题讨论】:

    标签: .net memory-leaks windbg


    【解决方案1】:

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

    在 WinDbg 中很难做到。使用诸如Jetbrains dotMemorycan import raw dumpstook care using the right format 之类的内存分析工具会容易得多。

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

    是的。

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

    如果你有一个像这样的 O(n²) 循环

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

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

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

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

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

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

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

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

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

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

    【讨论】:

    • 啊,我有 ANTS,但没有 dotMemory,谢谢,我会试一试。我明白你在说什么 - 也许有一个底层实体池(可能在某些固定的 EF 上下文中),并且每次它查询同一个集合时,都会将池与该池进行比较并添加到其中。我已经查看了一些!gcroot 输出的一些实体,但很难理解。我会试试 dotMemory..
    • Re: 一个操作,工作进程所做的任何事情都包含在一个“事件”中,我对它的运行时间进行计时。 CPU 使用率粗略地说峰值与内存峰值同步
    • @KierenJohnstone:您是否正确注销了事件处理程序?否则它可能会触发多次,一遍又一遍地计算相同的结果
    • 它不是 .NET 意义上的实际事件处理程序,它反序列化一个称为 Event 的对象,如果您明白我的意思,它的类型具有一个 DI 注册的处理程序。 dotMemory 给我一条消息,它在服务器上找不到具有相同精确版本的 mscordacwks.dll(v4.7.3163.00,x64)。比我的 microsoft.net\framework64\v4.0.30319 文件夹中的那个旧,很奇怪。编辑:从服务器本身抓取,我们开始..
    • aaand 与 ANTS 内存分析器一样,这是一个不稳定、崩溃、错误的尝试。加载一个转储 = 好的。加载一秒钟?返回仪表板。再试一次?需要10分钟,同样的事情。叹息..
    猜你喜欢
    • 2016-12-02
    • 1970-01-01
    • 1970-01-01
    • 2011-03-22
    • 1970-01-01
    • 2020-12-08
    • 1970-01-01
    • 2012-02-19
    相关资源
    最近更新 更多