Aspnetcore: Asp.net Core 不收集垃圾

创建于 2017-03-21  ·  136评论  ·  资料来源: dotnet/aspnetcore

我不明白为什么 Asp.net 核心似乎没有收集垃圾。 上周我让一个 Web 服务运行了几天,我的内存使用量达到了 20GB。 GC 似乎无法正常工作。 因此,为了测试这一点,我编写了一个非常简单的 Web 方法,它返回大量字符串。 该应用程序开始时仅使用 124MB,但每次我调用 web 方法时,内存使用量都越来越高,直到达到 411MB。 如果我一直调用 web 方法,它会更高。 但我决定停止测试。

我的测试代码是这样的:

`

[HttpGet]
公共异步任务> 测试GC() {
常量字符串消息=“测试”;
返回 Enumerable.Repeat(消息,100000000);
}
`

虽然我可能忽略了一些东西......据我了解,内存使用量不应随着每次调用此方法而增加。 在创建对象并将其发送到 UI 之后,内存应该已被释放。

从下面的截图可以看出,即使调用了 GC,内存也没有被释放。
netcorescreenshot

谢谢您的帮助!
周杰伦

investigate

最有用的评论

这有什么消息吗? 我在 Net Framework 上使用 Core 2,这仍在发生。 每次调用 _Controller_ 都会增加使用的内存,但它永远不会下降。 (_我使用了 WebApi 模板_)

所有136条评论

@rynowak

虽然我可能忽略了一些东西......据我了解,内存使用量不应随着每次调用此方法而增加。 在创建对象并将其发送到 UI 之后,内存应该已被释放。

大对象堆可能会在这里咬你。 如果您分配的对象大小 > 85KB,那么它将被放入 LOH 并且很少被压缩。 有关更多详细信息,请参见http://stackoverflow.com/questions/8951836/why-large-object-heap-and-why-do-we-care (或 https://github.com/dotnet/coreclr/blob/master /Documentation/botr/garbage-collection.md#design-of-allocator 如果你想更深入)。

我不明白为什么 Asp.net 核心似乎没有收集垃圾。 上周我让一个web服务运行了几天,我的内存使用达到了20GB

您的服务在做什么,它正在创建如此大的对象? 您应该在它变得太大之前尝试进行内存转储,这将清楚地向您显示哪些对象被粘在周围以及为什么它被保留(您可以使用 Visual Studio 来查看转储或更高级的工具,如 windbg 或 perfview)。

尝试从头开始分配数组,而不是调用 Enumerable.Repeat
或使用 GCSettings.LargeObjectHeapCompactionMode 压缩内存(.Net Standard 支持)

感谢@davidfowl@MhAllan的回复。 但这个例子是人为的。 我只是想要一些会使用大量内存的东西,以便我可以截屏。 事实是,无论所讨论对象的大小如何,任何应用程序都会发生这种情况。 为了回答您的问题@davidfowl ,我的服务只是使用 dapper 从数据库中提取一些数据,并返回结果对象。 每次调用都有一行数据。 所以内存需要几天的时间才能增长到这个数量。 当我偶然发现这个特性时,我实际上是在尝试测试数据库。 我写了一个小控制台应用程序,一遍又一遍地调用该方法。

@zorthgo确定不是 Dapper? 如果您通过将参数直接注入 SQL 脚本(如 PHP)来创建脚本,您最终会得到大量缓存的 SQL 脚本。 这是 Dapper 是如何做到的
https://github.com/StackExchange/Dapper/blob/fe5c270aceab362c936456087a830e6fe1603cac/Dapper/SqlMapper.cs
您应该使用内存分析器来判断是什么保留了对分配内存的引用。 Visual Studio 2017 应该能够帮助您在多次调用应用程序之前和之后从内存中获取一些快照并进行比较。

@zorthgo是的,我也看过这个。 在我的.net核心控制台应用程序中使用servicestack,每次我调用api时,内存使用量都会增加50mb。 我认为这是一个 VS2017 错误,但随后确认了任务管理器中的高使用率。 正如 zorthgo 所说,只需对 api 进行简单调用,内存使用量就会显着增加,并且似乎不会释放内存。

这只发生在 .NET Core 上的 ASP.NET Core 上还是 .NET Framework 上的 ASP.NET Core 也有问题?

您能否使用 MVC 5(在 System.Web 上)编写类似的应用程序并验证您没有看到相同的行为?

在目前的状态下,我无法从这个问题上取得任何进展。

在我的实例中,我只使用目标框架 .NetCoreApp 1.1,而我的控制台应用程序正在引用共享项目中的对象模型。

不确定这是否会帮助任何人。 调用 hellorequest 的示例应用程序。 在这个应用程序中,启动内存是 85mb,然后通过重复请求,我设法将内存使用量提高到大约 145mb。 它有时会回落到 125mb,但随后会停留在那里。 不确定这是否是正常行为,因为我不习惯 .Net Core 控制台应用程序。 我一直认为我做错了什么或没有正确实例化。

https://drive.google.com/open?id=0B0Gm-m-z_U84TU5keWFTMzc1ZWc

在部署到具有 3000-5000 个活跃用户的生产环境中的 Asp.Net Core 应用程序上面临同样的问题。昨天服务器上的内存增加到 15GB ......我不得不配置 IIS 以每 3 小时回收一次 AppPool,而我仍然尝试找出问题所在。

是否有人进行了内存转储并查看了在您的特定应用程序中占用所有内存的内容?

@davidfowl @Pinox
我在一家大公司工作,我们想用 ASP.NET Core 开始一个新项目,但是当我看到这个问题时,我很害怕 :worried: 。 这是一个关键问题,可能会阻碍我们项目的生命周期。

那么,它与 ASP.NET Core 或 .NET Core (CoreCLR) 有关吗? 我们将针对完整的 .NET (4.6),这就是我要问的原因。

@ikourfaln在我的情况下,我使用的是 .net 核心控制台应用程序、servicestack(.net 核心版本)和 kestrel。 奇怪的是内存使用上升到一个水平,然后突然停止并且不再上升。 我想最好的办法是用小样本在你身边测试它并检查行为。

也许@zorthgo可以检查他是否看到该内存中的类似行为已使用到一定水平然后停止增加,因为这是我所看到的行为。 我已经更新了我的示例应用程序以包含@zorthgo示例,并且我没有看到内存耗尽。 它上升但最终停止。

我确实稍微改变了来源:

公共对象任何(TestGC 请求)
{
常量字符串消息=“测试”;
返回 Enumerable.Repeat(消息,100000);
}

@皮诺克斯
谢谢,我会检查我这边的行为。

2.0的这个bug怎么样?

这有什么消息吗? 我在 Net Framework 上使用 Core 2,这仍在发生。 每次调用 _Controller_ 都会增加使用的内存,但它永远不会下降。 (_我使用了 WebApi 模板_)

你好,

我对 ASP.NET Core 2 也有同样的问题。我进行了内存转储并尝试分析。 据我所知,问题与OP所说的完全一样。 我的应用程序从分配大约 75 MB 开始,很快它一直到 ~750MB,其中 608MB 是“分配给 .NET 的未使用内存”。

应用启动时的第一个快照:
image

3 分钟和 100 个请求后的第二个快照:
image

我们也面临同样的问题,我们的控制器正在处理大量数据,(这是糟糕的设计,很快就会被替换),每次调用这个控制器都会导致内存增长。 内存减少但只有 40-50%(增加 50 Mb,减少 30-35 Mb),每次调用每次增加 10-15 Mb 范围内的内存。 服务托管在服务结构内。

看起来我在我们的生产服务(20-100 req/s)中遇到了类似的问题,使用了以下组合:

  • ASP.NET 核心 2.0.4
  • 服务堆栈.Core 1.0.44
  • SkiaSharp 1.59.2
  • Docker v17.09.0-ce,在 Ubuntu 16.04 x64 上构建 afdb6d4
  • x4 服务器 @ 40 个 CPU,128 GB 内存
  • 服务器 GC 为真
  • 每个 Docker 容器设置为 12k (mhz) cpu share, 8GB ram

该应用程序有一个前端 Web 服务器和工作者(分别如下图所示)。

网络服务器(过去 6 小时)
screen shot 2018-01-13 at 1 06 24 pm

工人(最后 6 小时)
screen shot 2018-01-13 at 1 07 55 pm

它们都使用大字节数组,因为服务充当对象存储代理,因此将对象放在 LOH 中。 我的问题是,目前这是 .NET Core 的已知限制吗? 似乎 LOH 从未完全清理或碎片化。

话虽如此,SOH 似乎工作正常,因为典型的 web api 对象已被清理。 有什么建议? 我的设置有问题吗? 我已经分析了代码,找不到任何明显的内存泄漏,而且我没有使用 ServiceStack 库之外的任何特殊内容。

@sebastienros - 对此有什么想法吗? 我们是否在我们的系统中观察到任何类似的行为?

  • 我们只在 2.1 上测量这个,我会考虑为 2.0 添加相同的东西
  • 所有评论似乎都与提到的 LOH 问题有关,应在应用程序和池大数组中尽可能考虑到这一点

@sebastienros ,几个问题:

  1. 我使用 Ants profiler 来测量内存使用情况,根据它,没有检测到 LOH 碎片。 您能否建议我如何验证我的应用程序是否存在 LOH 碎片问题?
  2. .net core 2.1 的结果是什么? 问题是否因为 Kestrel 使用 Span 而得到解决?
  3. 如果我们不能池化数组怎么办 - 你能提供一个解决方法吗? 我们应该使用 GCSettings.LargeObjectHeapCompactionMode.CompactOnce 吗?

.net core 2.1 的结果是什么? 问题是否因为 Kestrel 使用 Span 而得到解决?

我们个人还没有看到任何证据表明问题出在 Kestrel 中。 它仍然看起来像一个应用程序问题。

如果我们不能池化数组怎么办 - 你能提供一个解决方法吗? 我们应该使用 GCSettings.LargeObjectHeapCompactionMode.CompactOnce 吗?

@jkotas @Maoni0这里有什么建议吗?

如果我遇到同样的问题,我该如何调查? 正如@sinapis所描述的,根据redgate 内存分析器的LOH 几乎是空的,但对于一个用户仍然使用超过1gb 的简单内存

收集跟踪并使用 perfview 对其进行分析。 Vance 和其他人提供了许多关于如何追踪 .NET 内存泄漏的教程: https ://www.bing.com/search?q=.NET%20memory%20leak%20perfview。

https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md有用于收集跟踪的 Linux 特定说明。

如果您认为没有内存泄漏并且 GC 只是保留了您想要的更多内存,您可以尝试:

  • 使用 Windows 作业对象或 docker 容器内存限制来限制进程可以使用的内存量。 GC 会考虑这些限制,并在接近这些限制时更积极地运行。
  • 或者,从服务器 GC 切换到工作站 GC。 服务器 GC 具有更高的峰值工作集并不罕见。 工作站具有较低的较高峰值工作集,但吞吐量也较低。

你好,

我认为我在生产中遇到了与 .Net Core Web API 相同的问题。

该应用程序在带有 .Net Core 2.0.3 的 Windows Server 2016 上运行。 该机器是具有 28 个 CPU 内核和 24GB RAM 的 Hyper-V VM。 如果我们不经常回收 IIS 应用程序池,我们最终将使用所有可用内存。 当应用程序开始使用大量内存(>= 系统总内存的 95%)时,CPU 使用率也会大幅增加(有时从 2% 增加到 70%)。 我不确定是否触发了 OOM 异常,我们总是在它发生之前回收池(我看到的最大内存使用量是 dotnet.exe 使用的内存的 98%)。

使用“.Net Memory Porfiler”(SciTech Software)分析生产内存转储是我发现的:
image

如果此分析正确,则大约 95% 的内存处于“开销 > 未使用”状态。 下面是这个内存分析器编辑器是如何描述这个类别的(在他们的论坛上):
_"Overhead->Unused" 是 .NET 运行时为托管堆提交的内存。 它目前未使用,但可用于将来的实例分配。 运行时使用许多规则来决定是保留已提交的内存还是将其释放给操作系统。 这取决于可用内存、分配模式、处理器数量、是否使用服务器 GC 等因素。

@jkotas我将应用您的建议(Windows 作业对象,并切换到工作站 GC),我会让您知道结果。 请让我知道我是否可以从我拥有的生产内存转储中提取任何其他有用的信息。

谢谢

@sinapis @ronald7
你们中的任何人都可以分享一个显示问题的应用程序吗? 如果我可以重现它,我们将能够找到原因,或者至少逐段删除一些代码并隔离最小的重现。

@sebastienros我无法共享应用程序,但我可以从PerfView session + memory dump共享会话。
一些描述:我有一个 ASP.NET Core 2 Web API,我创建了 200 个用户的负载测试,所有用户都在 10 秒内发送相同的请求。 总共处理了 775 个请求。

这个应用程序在任务管理器中的内存使用量跃升至近 1 GB 并保持不变。 查看转储,我可以数出大约 18 MB:

image

所以问题是将近 1 GB 去了哪里?

@sinapis谢谢

您描述的行为并不意外,GC 将在峰值负载时根据需要分配一些内存,并随着时间的推移释放它。 这是 GC Server 模式,通常会等待空闲时间释放它,并且不会影响您的应用程序性能。 它将保留的内存量取决于系统上可用的总内存。

如果它继续增加,我们肯定会看到一个问题。 我假设如果您不再发送请求并让您的应用程序运行,您将看到内存使用量下降。

你能运行同样的东西直到它消耗掉你的大部分系统内存吗? 或者至少在相同的负载下足够长,它会显示它持续增长? 我仍然会看看你当前的转储。

您也可以在工作期间和工作结束时进行转储,以便我们查看详细信息。

@sebastienros

不幸的是,我无法共享应用程序或内存转储,但我将创建一个虚拟应用程序(具有相同的架构和依赖项),在同一台机器上运行它,如果我可以重现此行为,我将与您共享此行为。 如果我可以从内存转储中为您提取任何有用的信息,请告诉我。

我已经在一台生产服务器上将 GC 模式从 _server_ 更新为 _workstation_,如果它改变了内存使用情况,我会在几个小时后通知您。

我还进行了另一项测试:我们在 4 个虚拟机上的负载均衡器后面运行我们的应用程序。 从负载平衡器池中删除其中一台机器后,dotnet.exe 使用的内存没有减少,即使在 30 分钟后仍保持在同一水平。 (但是,应用程序仍在处理一些请求:SCOM 每 30 秒在一个虚拟端点上发送一个请求)。 没有内存被释放并返回给系统。

谢谢

@sinapis我查看了您的 ETW 跟踪。 这让我感到困惑——你在最后一次诱导的 gen2 GC 中存活的很少,但我们仍然选择保留这么多的内存。 您的应用程序似乎是边缘情况(由于 LOH 分配,您大多只做了后台 GC) - 我想知道我们是否有一些会计错误(另一种可能性是报告的数字有错误,但如果您已经确认您有那么多承诺,那就是可能性较小)。 如果您可以复制一些可以与我分享的东西,那就太好了; 否则,如果可以使用来自 GC 的一些日志记录来运行您的应用程序(我可以给您一个这样做的提交),那也会很有帮助。

@Maoni0请分享我应该如何启用 GC 日志记录
如果您希望我提供一些其他数据以证明会计错误,请让我知道我应该向您提供什么以及如何提供(也许告诉 perfview 收集更多数据?)
我会尝试创建一个最低限度的复制品,但不确定我会成功,因为我不知道问题出在哪里。

@sebastienros希望我今天会提供另一个内存消耗更多的转储

@sebastienros @Maoni0

我在工作站 GC 模式下运行了我们的应用程序 12 小时,但结果相同。 我还在单个生产节点上使用 .Net 2.1 Preview 2 重新编译了应用程序 1 小时,我会让您知道结果,但目前该过程已经使用 2GB+ 的 RAM。

image

我在同一台机器上运行 PerfView,我正在收集 GC 转储,是否有一个电子邮件地址可以向您发送 OneDrive 链接,不幸的是我无法直接在此线程中共享它。

如果它可以帮助我还可以收集更多的指标或 GC 日志。 谢谢

@ronald7 _redacted_ 我可以转发给@Maoni0

@sebastienros @Maoni0我刚刚给你发了一封电子邮件,里面有两个 PerfView gcdump 和一个 VMMap 文件,我希望这能有所帮助。 在我这边,我仍在尝试使用虚拟应用程序重现这种高内存使用行为。

谢谢!

我也遇到同样的问题。 垃圾收集永远不会发生! 屏幕截图显示了使用相当简单的 dotnet core web api 应用程序执行大约 50 个请求后的内存使用情况。

memory-profile2

我刚刚将在 Ubuntu 16.04 上运行的 ASP.NET Core 应用程序从 1.1 升级到 2.0 并遇到了这个问题。 非常严重,导致内核经常因为OOM错误而杀死应用程序,我正在考虑是否降级回1.x。 有些页面我们根本无法加载 - 即使在 Kestrel 重新启动后,应用程序也会在一次请求后立即耗尽可用内存! 我考虑过升级服务器,但根据此处关于使用所有可用内存的 ASP.NET Core 应用程序的评论,我不希望这会有所帮助。 我们的堆栈基本上是 ASP.NET MVC Core + EF Core...没什么花哨的。 如果我有时间,我会尝试创建一个示例来重现该问题 - 考虑到我们堆栈的简单性,我认为这不应该那么难。

FWIW,我升级的系统还有一个 .NET Core 控制台应用程序,并且在 2.0 升级后似乎没有任何内存问题,所以这肯定是与 ASP.NET Core 相关的问题。

可能相关: https ://github.com/aspnet/KestrelHttpServer/issues/2214

@danports您是否尝试过调用 GC.Collect() 查看内存使用量是否急剧下降? 这会给我们一个线索,我们应该从哪里开始。 如果 GC.Collect() (或 GC.Collect/GC.WaitingForPendingFinalizers/GC.Collect 后续程序)不能使内存使用量急剧下降,这意味着只有那么多内存需要处于活动状态,因此 GC 无法回收它。

@Maoni0我还没有尝试过。 我不认为我的问题在于 GC,因为我确实看到内存使用量不时下降 - 与运行时相比,我的 .NET Core 2.0 应用程序消耗的内存大约是它们的 2-3 倍。 NET 核心 1.1。 😞

我现在已降级回 .NET Core 1.1,稍后我会在有更多时间时重新访问,可能是在 .NET Core 2.1 发布之后。 (我在 2.0 中遇到了一堆问题,这只是其中之一。)

GC.Collect()没有帮助。 尝试了一个非常简单的 ASP.NET Core 2.0 和 2.1 Web API,它有一个返回 200k 整数字典的控制器。 即使应用程序不再使用任何内存,分配的内存也会随着每个请求而增加。

@Serjster返回 200K 整数 (4B) 将占用 800KB。 在这种情况下,您遇到了此评论中解释的问题: https ://github.com/aspnet/Home/issues/1976#issuecomment -289336916

在这种情况下,您应该使用数组池在请求之间重用它们。

另外值得一提的是,如果代码在 64 位模式下运行,那么包含指针的数组/列表等的大小是 32 位的两倍。 如果我没记错的话,完整框架默认在 64 位操作系统中运行任何 32 位 cpu 代码。 因此,迁移代码的人可能会意外遇到 LOH 问题。

我正在与@Serjster 合作,这就是我发现的。 如果我使用 asp.net core(我在最近的测试中使用 2.1)创建了一个 vanilla web api 项目,我注意到当我运行诊断工具(甚至检查代码中设置的进程工作内存)时,返回的字节数当我到达终点时继续攀爬。 例如,如果我有一个返回 Dictionary 的 Web api 端点其中有 20,000 个项目,会发生以下情况:

  1. 首次访问控制器方法时,进程内存为 83MB。
  2. 我等了几秒钟,然后第二次访问它移动到 86MB。
  3. 我等了几秒钟,第三次访问移动到 90MB。
  4. 再次 - 94MB。
  5. 我这样做了 n 次,最终达到了大约 304MB。 一旦这样做,它就会趋于平稳。

如果返回的对象是不同大小的对象,上面所有的数字只是更大/更小(包括平滑量),但增长模式是相同的(也就是说,它会不断增长,直到在多次请求后趋于稳定) .

如果我在方法调用中添加 GC.Collect (所以它发生在每个请求上,水平要低得多,但仍然有一段时间的增长,直到它趋于平稳。

另一个有趣的细节点是对象的数量和做快照时的堆大小在每次访问时基本上没有变化。 但是进程内存图不断显示越来越高的数字(如果您抓住进程并拉出工作内存设置值,这也是如此)。

我开始怀疑图表显示分配的内存(并且此内存基于某些 asp.net 核心使用/需求预测逻辑而增长),但这不一定是消耗/泄漏的内存。 虽然我知道的不够多,所以想知道是否有更多知识渊博的人可以插话。

编辑-重新@davidfowl评论:关于您对很少收集的东西的评论......这可能是有道理的。 但通常需要多长时间? 我可以在两次请求之间间隔 30 多秒,而 GC 似乎永远不会降低诊断图表中的内存编号。 我确定我对这里的某些东西一无所知,但只是好奇。

编辑 2 - 现在我已经更详细地阅读了 david 在上面发布的 SO 链接,我开始认为这绝对是我们看到的问题。 如果我们在内存有限的环境中运行(我们在开发环境中看到这一点,因为我们很便宜),我们就会遇到这个问题。

编辑 3 - 一个挥之不去的问题。 如果这是 LOH 问题,为什么进程内存一直在增加,但堆大小却没有增加? 其实,我现在可能明白了。 堆是使用的内存。 处理器分配的内存是已用内存加上未使用的碎片内存块。

@RemyArmstro你能把Dictionary<int, int>改成 $ SortedDictionary<int, int>吗? 字典可能正在分配连续内存,甚至可能为每个条目添加一些额外的数据。 SortedDictionary 的实现方式将进行许多小分配而不是一个大分配。

编辑:如果您序列化为字符串而不是直接响应输出流,那么这也可能导致 LOH 分配。

@wanton7你的回答没有抓住重点。 字典只是冰山一角。 我们可以使用列表、数组等,它们都做同样的事情。 但是,正如所指出的,如果 LOH 引起了这种情况,就像听起来那样,那么这种行为可能没问题? 除了这可能会产生一些令人担忧的副作用,比如当你用完内存时会发生什么? 您的应用程序会崩溃吗?

@Serjster好的,我以为您只是发生这种情况的小案例。 对我来说,拥有大列表、像这样的数组并在一个 api 调用中发送这么多数据(如果它不是二进制的)是非常不寻常的。 通常当你有某种 web api 并从中获取一些数据时,你会使用分页。 您不应该向客户端发送 10000 个条目。
但是如果你有很多这样的问题并且没有办法改变你的 api 的工作方式,那么我认为你应该创建自己的分块列表和字典实现。 如果你真的使用这么大的数组,那么你可以用你的分块列表替换它们,或者在应用程序启动时尝试将它们池化。

我确实希望微软能够创建每个人都可以在这种情况下使用的分块实现。

@wanton7你又一次错过了重点。 列表的大小无关紧要。 即使是单个项目或小列表也会导致此问题发生。

@Serjster也许我只是瞎了眼,但我没有看到你说发送单个项目或小列表会导致这种情况发生的任何帖子。 你删了吗?

或者来自@RemyArmstro他谈到了不同大小的字典。 我检查了 corefx 和 Dictionary 将分配数组或这些

private struct Entry
{
  public int hashCode;    // Lower 31 bits of hash code, -1 if unused
  public int next;        // Index of next entry, -1 if last
  public TKey key;           // Key of entry
  public TValue value;         // Value of entry
}

85000 字节分配将导致 LOH 分配,因此具有 5313 个 int 键和 int 值条目的字典将导致 LOH 分配。 容量与数字或条目不同,容量似乎是由素数扩展的,请查看私有字典的私有 Resize 方法。 每个结构都可以有额外的分配和内存填充,因此即使较低的条目也可能导致 LOH 分配。

字典实现细节Dictionary.cs

编辑:固定网址

@wanton7谢谢。 我想我们现在意识到了问题所在。 这只是一个无赖,没有很好的+简单的解决方案。 它基本上归结为更加了解它并调整您编写代码的方式。 不利的一面是,这种非托管内存开始感觉更受管理了。 :( 最后,我们可能只有少数区域真正违反了这个分配限制,但其中一个区域是我们应用程序的核心,所以我们目前看到了很多。我认为我们只需要重新考虑那部分,监视器,并尝试找出我们注意到此蠕变的任何其他区域。再次感谢!

实际上我们很快就会遇到类似的情况,我们需要创建分块的IList<T>实现。 我将为可移位的块使用一些大小,因此我可以只使用移位和掩码进行索引。

我想知道哪个对GC更有利,大块还是小块? 从 1KB 到 64KB 的大小。 较小的块意味着对 GC 的更多引用,但我猜较大的块可能对压缩和碎片更不利。

你的理解是完全正确的——我会选择不太大的尺寸; 可能尝试4k / 8k。

@Maoni0谢谢!

我选择了 4KB,这样如果我们在 Mono 下运行我们的代码,我们就不会得到任何令人讨厌的惊喜。 通过阅读http://www.mono-project.com/docs/advanced/garbage-collector/sgen/working-with-sgen/发现 LOH 阈值在 Mono 下仅为 8000 字节。

这个问题有进展吗?

我们正在观察相同的问题,即尽管堆大小保持不变(或减小),但进程内存仍在继续增长。

@sebastienros你能再看看这个吗? 也许我们可以提供一些详细的说明来帮助人们进一步调查他们的场景?

以下是我们的情况需要考虑的一些事项:

  • 我们只返回一个存储了 1000 个整数值的Dictionary<int, int>对象。 仅此而已。
  • 我们从 .NET Core 2.0 --> 2.1 转换了我们的项目
  • 我们在 MS Visual Studio 2017 中看到了这个问题

代码如下:

    public async Task<IActionResult> SampleAction()
    {
        var list = new Dictionary<int, int>();
        for (int n = 0; n < 1000; n++) list.Add(n, n);
        return Ok(list);
    }

要重现,您必须模拟某种形式的中等负载。 我们只需使用 Postman 快速单击,就可以观察到这种行为。 一旦我们停止模拟负载,我们就会看到堆大小减小但进程内存保持不变(即使我们强制 GC)。

我在我的一个项目中也看到了这一点,但我也可以在针对 .Net Core 2.1 (SDK 2.1.302) 的全新 .net 核心 API 中重新创建它。

我附上了我使用 Visual Studio 15.8.0 Preview 4 创建的示例 API 项目。为了显示内存增加,我有一个 .net 核心控制台应用程序每半秒点击一次默认值 GET 端点以获取 2 个字符串。 进程内存使用很慢,但在返回更多数据的项目中,这可能会快速增长。

screenshot
WebApplication1.zip

我在堆栈交换上找到了这篇文章:

https://stackoverflow.com/questions/48301031/why-doesnt-garbage-collector-in-net-core-2-0-free-all-memory

有没有人在发布模式下分析过他们的应用程序以查看是否存在这种行为? 我今天会试一试,看看问题是否仍然存在。

编辑:我尝试在发布模式下进行分析,但问题仍然存在。 我什至强制 GC 看看这是否会产生任何影响。

image

@chrisaliotta感谢您链接到该帖子,我不知道这种行为。 看看这是否能解释人们所看到的确实很有趣。

@Eilon @chrisaliotta谢谢,但这与本次讨论无关。 .NET 在调试模式下不释放内存是众所周知的行为,这就是为什么我们只在释放模式下测量内存消耗(和潜在泄漏)的原因。 即使在释放模式下,由于服务器 GC 模式,您也会看到内存在一定程度上超时。 因此,由于两个不同的原因,这个例子并不能证明什么。

@sebastienros那么@beef3333和我观察到的行为是否与预期一致? 即,尽管堆大小减小,但私有字节在哪里仍然升高? 如果是这样,尽管有可用的堆空间,但每个增量请求都会继续导致私有字节增长,这对我来说似乎很奇怪。

在调试模式下是的。 所以请尽量使用Release模式,长时间运行你的压力。 如果内存无限增加,则存在内存泄漏。 如果内存被回收(即使它需要大量内存),那么在您的情况下一切都很好。

我刚刚使用我在发布模式下附加的同一个项目再次对其进行了测试,我看到了相同的行为。

我会测试你的应用程序,谢谢。

我在本地运行@beef3333提供的应用程序2小时,速率为5K RPS,内存稳定(总体差异1MB,32GB机器400MB)。 定期正确调用 GC。 我还超时检查了多个转储,并按预期创建和收集了各种实例。 我正在使用 2.1.1。

@sebastienros感谢您对此进行调查。 我认为所有这一切的要点是:

  • .NET Core Web 应用程序与普通桌面应用程序的内存管理行为会有所不同。
  • 开发人员应将平均内存消耗作为一段时间内每秒的函数请求数 (RPS) 来关注。
  • 私有字节的增长可能并不总是表明内存泄漏。

如果我错了,请纠正我,但似乎 .NET Core 会根据平均请求增加分配的内存以确保最快的响应时间? 如果为真,是否可以安全地假设在应用程序池重置之前它可能不会释放这个分配的内存——或者如果 RPS 降低它会随着时间的推移释放这个分配的内存?

同样的问题。
我有一个 asp.net webapi 后端服务。
堆栈是 asp.net mvc、autofac、automapper、castle.dynamicproxy、实体框架核心。
所有内存都会被吃掉,然后服务崩溃。

版本是 2.1.0

@atpyk更新到 2.1.1。 如果那没有帮助,您应该真正分析保留该记忆的内容。 我使用过https://www.jetbrains.com/dotmemory/但可能还有其他工具也可以做到这一点。 它可以显示大对象堆 (LOH) 中实际分配的内容。

你在32位模式下运行吗? 因为大对象堆分配(大于 ~85000 字节)可能会在 32 位模式下由于碎片而导致内存不足异常。 您可以使用 Dictionary 轻松克服此限制. 检查此评论https://github.com/aspnet/Home/issues/1976#issuecomment -393833505

如果您在完整的 .Net Framework 中运行代码,则默认行为是以 32 位模式运行任何 cpu 代码。 您需要从项目设置中取消选中 Prefer 32bit 或将服务器注册表中的某些注册表设置设置为默认为 64 位。

@wanton7非常感谢。 我会尝试你的解决方案。

我更新到 2.1.2 并使用 win-x64 部署在 Microsoft Azure webapp 上,但没有效果。 @wanton7
dotnetcorememory

@atpyk请创建一个内存快照(dump_ 并对其进行分析(Visual Studio、MemoScope)以查看哪些对象占用了所有内存,或者只是增加了计数。您也可以取两个并加班比较它们。

@sabastienros我相信已经有足够多的人对此表示担忧,您/MS 应该自己开始分析这个问题。 也许这可以按您的预期工作,但是设计存在缺陷。

我们的应用程序最终会耗尽内存并崩溃,而这一切都在生产 Azure 环境中运行。 这是不可接受的。

@atpyk然后听起来像是内存泄漏。 分析你的记忆,看看是什么让记忆像@sebastienros所说的那样一直存在。

一个问题,你甚至在使用 ASP.NET Core 吗? 我再次阅读了您的第一条评论,您提到了 ASP.NET MVC。 ASP.NET MVC 和 ASP.NET Core 是两个完全不同的产品。 这些问题和这个 ASP..NET Core 的 repo。

编辑:仅从版本号听起来您正在使用 ASP.NET Core,但想确定一下。

我们使用 .net 核心 MVC。 @wanton7
我正在分析内存。 也许 Castle Dynamic Proxy 会导致内存泄漏。
memroy1
memroy2
memroy3
dynamicproxy

@Serjster您的程序是否在 32 位 .NET Core 中运行并因内存不足异常而崩溃? 如果你的代码做了很多 LOH 分配,那么内存碎片可能是原因。
如果您在 32 位环境中运行,您有两个选择,修复您的代码以避免 LOH 或切换到 64 位环境。

我对 Azure 不是很熟悉,但在谷歌搜索后发现了这个https://blogs.msdn.microsoft.com/webdev/2018/01/09/64-bit-asp-net-core-on-azure-app -服务/

@Serjster我相信并非所有报告都是平等的,并且更愿意检查每个不同案例的有效性。 像“我的应用程序有内存泄漏”之类的东西并不意味着它是因为框架,所以我更愿意确保每个案例都是真实的。

以你的情况为例,“我相信”我回答了你记忆力增加的原因。 经过我的解释,您是否能够修复它? 或者它没有解决问题,在这种情况下,你能提供一个我可以在本地运行的应用程序来重现问题吗?

@sebastienros我们最终发现内存分配只是不断增加,即使内存消耗没有。 我知道 ASP.NET 核心正在做一些启发式方法来告诉它应该获取更多,但它似乎在每个请求上不断地分配越来越多的东西。 在这方面对我来说几乎是贪婪的,但我可能是错的。

无论哪种方式,我认为@Serjster的观点是该线程不断增长,因为这里显然存在一些混乱。 在旧的 ASP.NET 领域(我相信在 core 1 之前),我们不需要看到这种行为(至少不是这个数量级。它可能不是真正的错误/问题,但它绝对是引起很多人的原因一遍又一遍地提出同样的问题。

如果有一篇官方文章真正从上到下讨论这个线程,而不是像以前那样绕圈子,那就太好了。 希望这有助于澄清。

如果有一篇官方文章真正从上到下讨论这个线程,而不是像以前那样绕圈子,那就太好了。 希望这有助于澄清。

我同意那个。 我们最好知道为什么 .NET Core 2.1 在内存管理方面比以前的版本更“投机取巧”。

@sebastienros我们可以在这里总结一下问题吗? 有 81 条评论——我的印象不是它们都是关于问题的(尽管我根本没有仔细阅读它们,所以我可能弄错了)。 如果是这样,我们可以在这里列出所有不同的问题,看看我们是否对每个问题都有重复吗? 有足够多的人提到内存增加,我认为我们有理由进行重复并弄清楚这些是否是一般性问题。

当然。 我目前正在尝试识别所有这些线程并查看每个线程的状态。 我最终会关闭这个问题并重新打开更具体的关注每个报告的问题,因为这个单一的线程不再可持续。 我会将它们链接到此处,以供此线程上想要关注它们的人使用。

同时,我将编写一份列出所有建议、已知内存问题(LOB、HttpClient,...)以及分析和报告这些问题的方法的文档。

只是为了向您保证我们确实关心这个问题并且为了抢先检测内存泄漏,在过去的 6 个月中,我们一直在 Linux 和 Windows 上的 Azure 上连续运行 24 小时和 7 天的 ASP.NET 应用程序。 这些测试在每次迭代(每天或每周)中获取最新的 ASP.NET 源代码。 我们测量 RPS、延迟、CPU 和内存使用情况。 该应用程序使用 EF Core、MVC 和 Razor,并持续使用 50% 的 CPU 来模拟显着负载。

你可以看到结果这里公开本网站(浏览找到日报): https://msit.powerbi.com/view?r=eyJrIjoiYTZjMTk3YjEtMzQ3Yi00NTI5LTg5ZDItNmUyMGRlOTkwMGRlIiwidCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0NyIsImMiOjV9&pageName=ReportSectioneeff188c61c9c6e3a798

这使我们能够在过去解决一些问题,并确信现在系统中没有根本性的泄漏。 但它远未接近使用我们提供的所有组件,并且可能存在我们需要确定的问题。 提供可重现问题的转储和应用程序是您可以帮助我们的主要方式。

@sebastienros感谢您的更新。 我知道在我们的案例中,问题更多是关于一个新的“贪婪内存分配”问题,我们最初将其误认为是内存泄漏。 我什至不确定现在是否存在问题,可能只是新的优化启发式方法更加激进。 不确定...但我认为您在真正评估该线程并就人们所看到/误解的内容提出一些综合解释/摘要方面走在正确的轨道上。 祝你好运!

所有个人报告都已被隔离,并将得到个人跟进,如果已经解决,则将其关闭。 随意订阅这些以保持循环。

同时,我将编写一份列出所有建议、已知内存问题(LOB、HttpClient,...)以及分析和报告这些问题的方法的文档。

这对我来说是一个巨大的+1。 我觉得这里最大的问题之一是收集信息的“感觉”有多难,然后尝试帮助确定_什么_是问题。 有一些很棒的文档可以让我们遵循一些说明来 (i) 收集,(ii) 尝试自我诊断和 (iii) 以对 MS 团队有效的方式发布我们的转储/发现可以真正帮助两者栅栏的两侧。

如果我们(开发人员)能够更好地诊断和/或提供信息,那么这对所有人来说都是双赢的。

再次感谢收听@sebastienros - 非常感谢,伙计!

对于下图中的情况,你怎么看?

我们在同一个计划中运行 4 个 WebApp。 最初是 B1,扩展到 S2,内存一直在增长,直到我设置为饥饿的 webapp csproj:

<ServerGarbageCollection>false</ServerGarbageCollection>

并禁用 Application Insights

  1. 我相信由于上述设置可以控制内存,因此没有内存泄漏。 正确的?
  2. 呈现的行为是否正常?

memory eaten up

这里的情况与@alexiordan相同,我们有一个 .net core 2.1 控制台,它运行一些托管在 Kube 中的 IHostedServices,来自 microsoft/ dotnet:2.1-runtime AS base。 我们想要启用 HealthChecks,所以我们添加了仅带有 HealthChecks 中间件的 asp.net,并将基础映像更改为 microsoft/ dotnet:2.1-aspnetcore-runtime。 结果是OOM。 我们已经设法通过添加来稳定内存分配错误的在 csproj 中。

我们的分析表明,在 asp.net 应用程序中,GC 收集的频率较低,Finalizer Queue 的遍历频率也较低。

此外,如果我们通过在管道中添加以下内容来强制 GC 收集和遍历终结器队列,

System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect();

内存分配保持稳定。

对于下图中的情况,你怎么看?

我们在同一个计划中运行 4 个 WebApp。 最初是 B1,扩展到 S2,内存一直在增长,直到我设置为饥饿的 webapp csproj:

<ServerGarbageCollection>false</ServerGarbageCollection>

并禁用 Application Insights

  1. 我相信由于上述设置可以控制内存,因此没有内存泄漏。 正确的?
  2. 呈现的行为是否正常?

memory eaten up

@alexiordan

我们在使用 AI(net core 2.1 上的网络应用程序)时也看到了非常相似的内存配置文件,您在解决这个问题上是否取得了进一步的进展? 显然,我们希望将 AI 保留在应用程序中。

奇怪的是,每个请求的使用量都会增加,但是将上述设置为 false 似乎对我来说停止了它? 很奇怪,因为您认为 true 将是启用它的值,但似乎相反...

我忘了提到,在宣布我打算写一篇关于这个线程中描述的问题的文章后不久,我确实做到了。 你可以在这里看到它: https ://github.com/sebastienros/memoryleak

它带有一个小型应用程序,可以实时呈现图表上的模式。

但将上述设置为 false 似乎对我停止了它? 很奇怪,因为您认为 true 将是启用它的值,但似乎相反...

客户端垃圾收集(针对与许多应用程序共享内存并保持内存空闲进行了优化)比服务器垃圾收集(针对吞吐量和并发性进行了优化)更具侵略性。

将 SGC 设置为 false,我的 asp.net 核心 api 从 150mb 下降到 48mb,之后每次请求都没有增长。 那么实际上这是目前最好的生产环境吗?

@kgrosvenor实际上,这取决于。 引用优秀的@sebastienros文章:

在典型的 Web 服务器环境中,CPU 资源比内存更重要,因此使用服务器 GC 更合适。 但是,某些服务器场景可能更适合 Workstation GC,例如在托管多个 Web 应用程序的高密度服务器上,其中内存成为稀缺资源。

谢谢你,这非常方便,我会记住 - 将按照这个线程进行更多更新,据说我绝对喜欢 asp.net 核心 :)

.net core 2.1 控制台应用程序也会受到此影响。 不断的记忆增长。 已将 docker 容器设置为较低的最大值,因此它会击中它并重新启动,这工作正常,但它很难看。

这边有消息吗? 我们在 ASP.Net Core v2.1 中也有相同的行为。 从我在提交https://github.com/aspnet/AspNetCore/commit/659fa967a1900653f7a82f02624c7c7995a3b786中可以看到,似乎存在将在 v3.0 中修复的内存管理问题?

@flo8您是否尝试过升级到 2.2 并检查过行为? https://dotnet.microsoft.com/download

运行最新的 2.2 并且也有这个问题 - 仅仅创建一个包含 100 万个整数的列表并返回一个 emptyResult() 将使我的堆在每个请求中增加几百 MB,直到我用完内存。 将 ServerGarbageCollection 设置为错误可以治愈它,尽管它看起来不像是正确的修复...

@dre99gsx ,因为您似乎有一个简单的复制,您能否分享一个项目和步骤,以便我可以在本地做同样的事情?

在这种情况下,它应该填充 LOB,但它们应该在 gen2 上收集。 另外请分享我可以在什么环境下复制它,操作系统,内存,负载。

抱歉,回复神秘。 很容易:
(Windows 7 64 位,16GB 内存,用于 http 请求的 Google chrome,VS2017 社区)- 我仍然习惯于向这些线程添加代码,请原谅外观)

  • 启动一个新的 .NET Core 2.2 Web 应用
  • 将一个服务类,作用域(没什么特别的......)注入控制器构造函数
  • 让控制器的 Index() 操作调用此服务类的方法
  • 创建具有一个属性的模型类(DumbClass): public int ID {get; 放;}
  • 在服务类方法中,实例化一个列表并填充它:
    var lst = 新列表();
    for (i=0; i<10000000; i++) <--- 注意:1000 万次迭代
    {
    lst.add(new DumbClass(){ID=i});
    }
  • 从方法返回,不需要传回任何东西,但你也可以传回那个列表......
  • index() 返回新的 EmptyResult();

现在只需每 8 秒调用一次操作,然后观察分析器内存上升。 在我的系统上,这是我在 Private Bytes 上看到的:

启动应用程序:155MB
第一个 http 获取请求:809MB
第二:1.2GB
第三:1.4GB
第四:1.8GB
第五名:2.1GB ... 2.3GB.. 2.6GB ...

现在,在某个时候,GC 似乎开始发挥作用,但在这个示例中它从未低于 3GB。 如前所述,如果我将 ServerGC 设置为 false,则永远不会超过 1GB,尽管它仍然会爬到那里并徘徊在 1GB。

让我知道这是否有帮助,如果你能重现它。 哦,我阅读了您的 github 帖子:“ASP.NET Core 中的内存管理和模式”,写得很棒,感谢您的贡献!

@dre99gsx 👋

我仍然习惯于向这些线程添加代码,请原谅外观)

完全没有问题 :) 问题:你真的可以将整个示例应用程序放到GitHub (免费 repo)或类似的地方吗? 这是其他人快速克隆/下载您一直在使用的_整个_确切的示例应用程序/存储库的最简单方法。

此外,使用TaskManager的内存使用屏幕截图会有所帮助(如果在 Windows 上 - 或 *nix 上的等价物 .. 这是top命令??)

到目前为止,非常努力!

/me 回去默默地认真地看着这个帖子。

提醒您,您可以在此处查看线​​程中描述的所有症状的一些演示和解释: https ://github.com/sebastienros/memoryleak

此外,这些问题中的每一个都已单独处理,并且没有一个被证明是 dotnet core __so far__ 中的错误,而是预期的行为。

@dre99gsx现在回到最近的评论,我会敦促你从这个线程中提出一个单独的问题。 在我的手机上,我没有意识到它是“这个”;)。 从你的第一条评论中你说

直到我内存不足

所以我预计会出现 OutOfMemory 异常,这就是我要求重新制作的原因。 但在您的下一条评论中,您声明:

现在,在某个时候,GC 似乎开始起作用了,但在这个例子中它永远不会低于 3GB

所以不存在内存问题。 这是具有大量内核和大量可用内存的机器的典型情况。 GC 将释放托管堆,但内存仍将被提交,因为没有理由取消提交(大量可用内存)。 这是 .NET 中的标准行为,我在我指出的文章中对其进行了演示。 您还可以阅读以下内容: https ://blogs.msdn.microsoft.com/maoni/2018/11/16/running-with-server-gc-in-a-small-container-scenario-part-0/

我知道运行时团队目前正在研究限制在容器中运行的 .NET 应用程序的方法,这样它就不会使用那么多内存,针对 3.0 来解决一些微服务场景。

正如您自己发现的那样,如果您的应用程序无法使用服务器上的所有可用内存,您应该使用工作站 GC 模式。

正确,当我说“内存不足”时,我的依据是通过 Windows 私有工作集(任务管理器)直观地看到没有可用于任何其他应用程序的内存; 不是“内存不足异常”。

你知道吗,你是对的。 这看起来越来越像预期的行为,如果有这么多可用内存,如果没有其他人需要它,为什么还要花费资源来释放它! 我知道了。 只要 GC 足够聪明,可以释放内存,让其他应用程序不受限制,我就让它保持原样,让它做自己的事情。 再次感谢!

我在 Asp.net core 2.2 中开发了我的应用程序,也面临与内存释放相关的相同问题。
每次调用都会增加 40-50 Mb 范围内的内存,每次都不会释放。

我还添加了提到的标签ServerGarbageCollection>false
对于 50 个用户仍然面临同样的问题,它在进程内模式下使用大约 2GB RAM(w3wp iis 工作进程)

请帮忙。

请帮忙 !! 与ankitmori14相同的问题

@ankitmori14 ,@ ikourfaln - 如果您在https://github.com/aspnet/AspNetCore/issues/1976#issuecomment -449675298 阅读了 @sebastienros的评论并且仍然认为存在内存问题,请提交新问题包含重现问题的详细步骤,以及您拥有的有关该行为的任何其他信息和痕迹。 请记住,除非应用程序/进程实际上有错误,否则不太可能(但并非不可能)存在错误。 默认的“服务器”垃圾收集器不会尝试使用尽可能少的内存; 它会在需要时启动,例如内存实际用完时。 因此,即使是小型应用程序也可能使用 2GB 内存,这不是问题,因为仍有 4GB 可用。

你好,

我们遇到了这个问题: https ://stackoverflow.com/questions/53868561/dotnet-core-2-1-hoarding-memory-in-linux

基本上,内存在进程中不断增长,直到 Kubernetes 将其杀死,因为达到了配置的 512Mb 限制。 有趣的是,在进行内存转储时,内存急剧下降,而进程没有重新启动或任何事情。 检查内存转储,我们看到很多没有根的对象。

昨天我们还禁用了并发 GC(即:后台 GC),现在似乎好多了,但我们至少要等一个星期才能确认。

<PropertyGroup>
  <ServerGarbageCollection>false</ServerGarbageCollection>
  <ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>
</PropertyGroup>

@vtortola问题,当您为应用程序配置 512Mb 限制时,您是否知道要处理的并发请求数或测试了您的应用程序在跌倒之前可以处理的并发请求数?

我们做了一些初步和粗略的测试,并检查我们可以使用 512Mb 处理每个 pod 500 个并发 websocket。 我们用 2 个 Pod 和 1000 个并发连接运行了几个小时,内存小于 150Mb。 部署的应用程序有 2 个 Pod,随时有 150 到 300 个并发连接,内存从最初几天的不到 100Mb 到大约 2 周后达到 512Mb 不等。 连接数和使用的内存之间似乎没有相关性。 超过 70% 的连接持续 10 分钟。

您能否在内存为 100MB 和 512MB 时共享内存转储,以查看哪些实例仍然存在?

恐怕我无法共享转储,因为它包含 cookie、令牌和大量私人数据。

那你能在本地比较吗? 就什么对象占用最多的内存而言,以及它们的数量是否与您的负载无关。 就像如果你有 300 个连接不应该有 3K 连接对象。

不幸的是,设置<ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>的测试没有帮助,该过程仍在囤积内存。

我们有一个进程的 linux 转储,我们必须分析它的唯一工具是 lldb,我们在这件事上非常菜鸟。

这里有一些数据,以防它响铃:

(lldb) eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x00007F8481C8D0B0
generation 1 starts at 0x00007F8481C7E820
generation 2 starts at 0x00007F852A1D7000
ephemeral segment allocation context: none
         segment             begin         allocated              size
00007F852A1D6000  00007F852A1D7000  00007F853A1D5E90  0xfffee90(268430992)
00007F84807D0000  00007F84807D1000  00007F8482278000  0x1aa7000(27947008)
Large object heap starts at 0x00007F853A1D7000
         segment             begin         allocated              size
00007F853A1D6000  00007F853A1D7000  00007F853A7C60F8  0x5ef0f8(6222072)
Total Size:              Size: 0x12094f88 (302600072) bytes.
------------------------------
GC Heap Size:            Size: 0x12094f88 (302600072) bytes.

dumpheap -stat的结果: https ://pastebin.com/ERN7LZ0n

您可以在https://stackoverflow.com/questions/53868561/dotnet-core-2-1-hoarding-memory-in-linux中找到 Grafana 如何报告内存状态

我们在 dotnet 核心中有另一组服务可以完美运行,尽管它们不使用 websocket。

@vtortola你介意创建一个新问题,所以我们不会分叉这个问题。 我认为它使用 WebSockets 的事实使其独一无二,我们至少需要创建一个示例应用程序,该应用程序的行为类似于您的应用程序并在很长一段时间内对其施加压力。 您能否描述一下您的客户使用 websocket 做了什么,他们如何连接到它,以及连接了多长时间? 我可以设置一个这样的应用程序并运行几天,如果我看到内存在增长,就进行内存转储。

这也可能是 GC 和 Kubernetes 之间的一些冲突,以某种方式 GC 不会收到警告说它接近 pod 的限制。

@richlander正在研究类似的问题。 Rich 这个案例(仅阅读最后 6 条评论)可能与您的工作有关吗?

如果它有任何区别,我有相同(或非常相似)的问题。 我有十几个在 docker 上运行的 .net core 2.1 和 2.2 控制台应用程序——每个容器的内存限制为 512mb,也使用 websockets(客户端),并且应用程序每 3 天达到容器内存限制。 然后容器关闭并重新启动。

一切都在使用经过身份验证的服务,所以我无法提供我所拥有的,但我可能能够将一些东西放在一起,使用测试站点来重现问题。

您使用哪些参数来设置内存限制? 只是--memory还是--memory-swap

当我测试它时,我可以看到它被很好地考虑到了,永远不会超过我设置的限制,即使是像 16MiB 这样的超低限制,但是它像疯了一样在磁盘上交换。 我正在测试一个执行数据库查询(EF)和 Razor 视图渲染的应用程序。

@sebastienros绝对,我创建了https://github.com/aspnet/AspNetCore/issues/6803

如果您需要更多信息,请与我们联系。

我目前遇到内存问题。

在生产一个小时后,所有内存都被 w3wp.exe 进程使用(运行 .NET Core InProcess)。 将 GC 更改为 Workstation,并没有为我解决问题。

在分析内存转储后,我发现了这个类似的问题, https://github.com/aspnet/AspNetCore/issues/6102。

在升级到最新的 .NET Core 运行时(目前为 2.2.3)后,我希望在今天晚些时候在生产中对其进行测试。 我会让你知道情况如何。

我在内存使用方面遇到了类似的问题。 如果我对我的 Linux 容器设置限制,它只会让它 OOM 更快。 甚至使用 Visual Studio 中的基本模板也会发生这种情况。 我发现一件事 - Core 2.1 和 2.2 受到影响。 Core 2.0 不是 - 但它是 EOL :(

往上看

我知道运行时团队目前正在研究限制在容器中运行的 .NET 应用程序的方法,这样它就不会使用那么多内存,针对 3.0 来解决一些微服务场景。

正如您自己发现的那样,如果您的应用程序无法使用服务器上的所有可用内存,您应该使用工作站 GC 模式。

可悲的是工作站模式根本没有帮助 - 可悲的是,我真的无法尝试 3.0 预览版或类似版本之一。

@PrefabPanda通过检查System.Runtime.GCSettings.IsServerGC是否为false来确保您实际上在工作站 GC 模式下运行。

那么这可能是真正的内存泄漏。 打开一个单独的问题也许是最好的一步。

@wanton7 - 谢谢,我已经仔细检查过,这肯定是设置好的。

@DAllanCarr - 会的

我几乎可以肯定这不是内存泄漏。 更像是 Docker 设置可能无法正确执行,并且 GC 在达到限制之前不会启动。 我知道 3.0 在这个意义上引入了修复。

@richlander这个问题看起来像是在 2.2 中无法解决的问题吗?

@PrefabPanda您介意分享您正在使用的确切 docker 版本和 docker compose 文件吗? 我正在尝试重现,但在 docker 和 docker-compose 版本之间,我很难在本地复制这个问题。

@sebastienros@richlander - 感谢您回复我。 对此,我真的非常感激。

我的 Docker 版本:

Docker 桌面社区
版本 2.0.0.2 (30215)

引擎:18.09.1
撰写:1.23.2

整个测试项目见附件:
WebApplication1.zip

测试卷曲请求.zip

以防万一,我的 Dockerfile:

FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS 基础
工作目录 /app
曝光 80
暴露 443

FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS 构建
工作目录 /src
复制 ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
运行 dotnet restore "WebApplication1/WebApplication1.csproj"
复制 。 .
WORKDIR "/src/WebApplication1"
运行 dotnet build "WebApplication1.csproj" -c Release -o /app

FROM 构建 AS 发布
运行 dotnet 发布“WebApplication1.csproj”-c 发布 -o /app

从基础 AS 最终
工作目录 /app
复制 --from=publish /app 。
入口点 ["dotnet", "WebApplication1.dll"]

码头工人-compose.yml:

版本:'2.4'

服务:
网络应用程序1:
图片:${DOCKER_REGISTRY-}webapplication1
mem_reservation:128m
内存限制:256m
内存交换限制:256m
中央处理器:1
建造:
语境: 。
dockerfile:WebApplication1/Dockerfile

docker-compose-override.yml:

版本:'2.4'

服务:
网络应用程序1:
环境:
- ASPNETCORE_ENVIRONMENT=开发
- ASPNETCORE_URLS=https://+:443;http://+:80
- ASPNETCORE_HTTPS_PORT=44329
- DOTNET_RUNNING_IN_CONTAINER=true
- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true
- ASPNETCORE_preventHostingStartup=true
端口:
- “50996:80”
- “44329:443”
卷:
- ${APPDATA}/ASP.NET/Https:/root/.aspnet/ https:ro
- ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/us ersecrets:ro

@sebastienros - 即使有一种方法可以将单独的环境变量放入容器中供 GC 查看,这对我来说也很好。

我已经尝试显式调用它,但它没有帮助 - 我假设即使在代码中调用它时,它仍然会看到容器/机器的错误内存大小。

@PrefabPanda当您说“我已尝试明确调用它”时,您能否详细说明一下究竟是什么意思? 如果您指定了一个,无论是自然触发还是诱导,GC 都会看到正确的容器内存限制。 如果你调用了 GC.Collect(),它会做一个完整的阻塞收集; 如果您执行 GC.Collect(2, GCCollectionMode.Default, true, true) ,它将执行完整的压缩 GC,当它返回时,堆的大小可能是最小的,而不管容器限制或其他任何东西。

@Maoni0 - 我试过 GC.Collect(2, GCCollectionMode.Default, true, true)

我刚刚看到另一条评论说 256MB 对于 2.2 来说太小了,但可能会在 3.0 中“修复”。 看来我还需要多尝试一些...

如果您尝试过 GC.Collect(2, GCCollectionMode.Default, true, true) 并且内存没有像您预期的那样下降,则意味着您确实存在内存泄漏。 不确定您的环境中有多少工具可用。 你能运行 sos 命令吗? 如果是这样,您可以随时查看诱导 GC 后堆上剩余的内容

谢谢@Maoni0 - 请记住我在 Visual Studio 中使用模板 - 在我附加的这个测试项目中实际上没有我自己的代码。 我还有一些其他反馈要处理,我会与您联系。 非常感谢。

@Maoni0 - 我尝试设置上限,没有区别。 看来我得试试 3.0 预览版了

我想锁定对这个问题的评论,因为它是一个非常古老的问题,它的所有案例都在单独的问题中处理。 我错误地评论了这个,以为我在评论https://github.com/aspnet/AspNetCore/issues/10200

谢谢

此页面是否有帮助?
0 / 5 - 0 等级