Azure Functions 中的高内存占用

Mih*_*ert 5 c# memory azure azure-functions

我在 .NET Core 上使用一些简单的 C# 代码来查看 Azure Functions 的执行情况。

\n\n

用例是这样的:将 1000 万个 int 值添加到 ArrayList 作为 Azure 函数的一部分,并使用设置为 1 分钟的时间触发器来调用该函数。(我知道 ArrayList 已过时,不再使用,但我只是使用它来在堆上分配大量空间)。\n因此该函数的 C# 代码是这样的:

\n\n
using System.Collections;\n\npublic static void Run(TimerInfo myTimer, ILogger log)\n{\n    ArrayList numbers = new ArrayList();\n    Random random = new Random(1);\n    int noNumbers = 10000000;\n    for(int i=0;i<noNumbers;i++) {\n        numbers.Add(random.Next(10));\n    }\n\n    log.LogInformation($"Created an ArrayList of {numbers.Count} elements");\n    log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我检查了平台设置,发现这是 32 位的(我假设这是指 .NET Core 运行时为 32 位):

\n\n

在此输入图像描述

\n\n

在“桌面”环境中运行相同的示例代码,针对 Windows 10 x64 上的 .NET Core 3.0,并使用AppDomain.CurrentDomain.MonitoringTotalAllocatedMemorySize获取总体分配的内存会产生以下结果(VMMap 输出,后跟控制台输出):\n在此输入图像描述\n在此输入图像描述

\n\n

这些值与预期相符,如下所示:

\n\n
    \n
  • 假设 GC 在代码完成最终内存分配之前回收未使用的数据,则只有最终状态才重要。ArrayList 是通过分配 object[] 数组来实现的,该数组的长度是元素数量超出当前元素数量的两倍;对于最后一个这样的内部数组,其长度将为 16,777,216,每个对象引用为 4 字节(32 位平台),因此消耗 67,108,864 字节(正好 64 MB);盒装整数本身将占用 10,000,000 x 12 字节 = 120,000,000 字节(约 117 MB),总共约 181 MB,与 VMMap 在“私有”列中显示的内容非常相似
  • \n
  • 对于 ArrayList,每个装箱 int 占用 12 个字节,这是由于类型对象指针(又名方法表地址 \xe2\x80\x93 4 个字节)、存储的值(一个 int,在 x86/x64 上占用 4 个字节)和同步块索引(4 字节)。所有这些分散的盒装整数总计 114.45 MB(12 字节/对象 x 1000 万个对象)。除此之外,我们还有内部数组本身,它们依次分配,占用 128 MB(16 字节 + 32 字节 + 64 字节 + \xe2\x80\xa6 + 32 MB + 64 MB)。总数达到 242.45 MB,接近控制台输出打印的内容
  • \n
\n\n

然而,观察到的 Azure 函数结果如下(指标图,然后手动运行函数本身):\n在此输入图像描述

\n\n

在此输入图像描述

\n\n

图表上测得的私有字节数远高于之前在 VMMap 中看到的值,而在函数生命周期内分配的字节数既不符合 x86 代码 (~240MB) 的预期内存使用量,也不符合 x64 (~500MB) 的预期内存使用量。这表明 x86 代码正在运行,但上面有一些额外的东西(其开销不可忽略),这会显着影响私有字节。但是,观察到的值确实接近在 Azure 门户中为 Azure Function 测量的“私有字节”指标(Function App\xe2\x80\x99s \xe2\x80\x9cprivate bytes\xe2\x80\x9d 或 App Insights\xe2\x80\x99 \xe2\x80\x9cprocess private bytes\xe2\x80\x9d 大致相同)。

\n\n

与在常规计算机上运行相同的代码相比,Azure Functions 中的私有字节数加倍的解释是什么?

\n\n

无论如何,为什么这很重要?有两个原因:

\n\n
    \n
  1. 根据内存消耗进行计费(作为标准之一),因为专用字节数被引用为用于计算此值的指标(链接
  2. \n
  3. 目前,Azure 函数使用的内存限制为 1.5 GB;只要内存使用量高于预期,分配类型的实际最大限制就会降低(例如,我必须只使用 1.0 GB 的内存结构,因为其余的最多为 1.5 GB) Azure Functions 中的运行时莫名其妙地使用了 GB)
  4. \n
\n\n

相应的 GitHub 问题已在此处打开。

\n