当 App Service Plan 有 7 GiB 时,WebJob 只能访问 2 GiB 内存

WebJob only has access to 2 GiB of memory when App Service Plan has 7 GiB

我有一个如下所示的 Azure 应用服务计划,它应该有 7 GB 的内存。

P2V2
420 total ACU
7 GB memory
Dv2-Series compute equivalent
83.22 USD/Month (Estimated)

我正在使用它来托管具有单个连续 WebJob 的单个应用程序。当然有应用程序本身的网站,但我根本不使用它。

此外,我的服务计划中只有 1 个应用程序实例,所以这不是因为多个实例共享总可用 7 GiB 内存。

服务计划有 2 个部署槽,1 个用于生产,1 个用于暂存。两个应用程序(在生产和暂存中)都已关闭,因为 WebJob 可以在应用程序本身关闭时保持 运行ning。

这是我看到的令人费解的地方。在 App Service Plan 概述页面上,它说我使用了大约 45% 的可用总内存。

但我 运行 有时会遇到 OutOfMemoryException,所以我开始检查我的 WebJob 实际使用了多少内存。

事实证明它使用了 2 GiB 可用物理内存中的 1.6 GiB,而不是我期望的 7 GiB。

我获取当前内存的方式是通过以下代码

var currentMemory = Process.GetCurrentProcess().PrivateMemorySize64;
var totalMemory = GC.GetGCMemoryInfo().TotalAvailableMemoryBytes;

根据此处的文档https://docs.microsoft.com/en-us/dotnet/api/system.gcmemoryinfo.totalavailablememorybytes?view=net-6.0#System_GCMemoryInfo_TotalAvailableMemoryBytes

This property value will be the value of the COMPlus_GCHeapHardLimit environment variable, or the Server.GC.HeapHardLimit value in runtimeconfig.json, if either is set.

If the program is run in a container, this value is an implementation-defined fraction of the container's size.

Otherwise, the value of the property is the physical memory on the machine that was available for the garbage collector to use when the last garbage collection occurred.

因为我没有环境变量,runtimeconfig.json 设置并且它不是容器,我认为 GC.GetGCMemoryInfo().TotalAvailableMemoryBytes 正确返回虚拟机上我的 WebJob 可用的总物理内存,即 2 GiB.

由于 Vova 下面的评论,我也找到了这篇文章。 https://docs.microsoft.com/en-us/azure/app-service/faq-availability-performance-application-issues#i-see-the-message--worker-process-requested-recycle-due-to--percent-memory--limit---how-do-i-address-this-issue-

确实有道理;同时,在我看来这篇文章是针对应用服务本身的。 WebJob 运行s 作为一个独立于网站的进程。虽然症状是一样的。看来我的 WebJob 是 JIT 到 32 位而不是 64 位。

我的 WebJob 构建配置

.NET 6 Console Application
Platform: Any CPU

无论如何,我的应用服务配置设置为 x64,如下所示。

我试图了解我做错了什么,因为我认为 Azure 在这里做的是正确的。

我正在使用 Azure App Service 来 运行 WebJob,所以我的目标是使用可用的 smallest/cheapest SKU 最大化 WebJob 的可用内存。

我发现问题在于我如何通过在我的 ADO 管道中创建的 run.cmd 启动脚本 运行 我的 WebJob。

dotnet MyWebJob.dll 导致 GC.GetGCMemoryInfo().TotalAvailableMemoryBytes return 2 GiB 作为我的 WeJob 的总可用内存,而我的 WebJob 本身 Process.GetCurrentProcess().PrivateMemorySize64 仅消耗 1.5 GiB 的内存。

由于我的 ADO 管道还生成一个特定于平台的可执行文件,Windows,我还有一个可以 运行 的 EXE。通过调用 EXE MyWebJob.exe 启动我的 WebJob 结果 GC.GetGCMemoryInfo().TotalAvailableMemoryBytes return 5.5 GiB 作为我的 WeJob 的总可用内存,而我的 WebJob 本身 Process.GetCurrentProcess().PrivateMemorySize64 膨胀到 3.5 GiB 内存。

这两种不同行为的原因是 Path 环境变量同时包含 dotnet SDK x86 和 x64,在值中 dotnet x86 在 dotnet x64 之前。结果,当我执行 dotnet MyWebJob.dll 时,系统正在使用 x86 dotnet 运行time,这在调试控制台中很明显。

C:\home>dotnet --info
.NET SDK (reflecting any global.json):
 Version:   6.0.100
 Commit:    9e8b04bbff

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.14393
 OS Platform: Windows
 RID:         win10-x86
 Base Path:   C:\Program Files (x86)\dotnet\sdk.0.100\

Host (useful for support):
  Version: 6.0.0
  Commit:  4822e3c3aa

我修改了负责为我的 WebJob 生成启动脚本的 ADO 任务,以明确使用 x64 版本的 dotnet。 [01/07/2022 00:21:06 > 52cb62: INFO] C:\local\Temp\jobs\continuous\MyWebJob\n04q3opt.krt>"C:\Program Files\dotnet\dotnet.exe" MyWebJob.dll 这解决了问题。

结果与 运行 直接执行 EXE 相同,而不使用 dotnet,如上所示。