群里有个朋友贴了张截图,问了我一个很有意思的问题:他在 Azure App Service 上部署了一个 .NET 应用,压测的时候 CPU 能冲到 90% 以上,但内存始终停留在 80% 左右,怎么压都上不去。他怀疑是不是租户隔离把内存限死了,又怀疑是不是 .NET 的 GC 在搞鬼。这个问题我前前后后遇到过不少次,今天把从外到内的原因一次性拆透,聊聊为什么 .NET 应用在 App Service 上内存很难"占用 100%"。
先说结论:这既不是你的代码写得不对,也不是 .NET 本身有什么内存上限,而是 App Service 的内存本身就是配额制,不是让你去占满宿主机物理内存;再加上 .NET 运行时的 GC 会按内存压力自动回收,你看到的"到了某个百分比就上不去了"其实是多层机制共同作用的结果。下面按层次展开说。
1. 先搞明白 App Service 的内存配额机制
1.1 不是宿主机的 100%,是给你这层的配额
App Service 底层跑在共享的多租户架构上,一台宿主机上不止你一家应用。为了避免某个应用把整台机器拖垮,平台给每个 App Service Plan 定义了明确的配额——包括 CPU 配额和内存配额。这个配额才是你应用的"天花板",而不是宿主机物理内存的 100%。
举个例子,常见的 P1v3 规格,官方给的配额是 8GB 内存。这 8GB 是给你的应用进程的可用额度,不是宿主机上所有内存的 8GB。宿主机的总内存可能大得多,系统要给 Windows 内核、沙箱运行时、其他租户以及各种监控代理留空间。如果你的应用能用满这 8GB 配额,已经是把这个规格榨干了,不可能指望任务管理器里看到宿主机 100% 被你的 w3wp.exe 吃掉。
如果你用的是共享层(Free / Shared)或者基础层,配额逻辑更复杂,还可能跟同一计划内其他 App 共享资源。但核心结论一样:App Service 的"内存"指标,是围绕配额来计算的百分比,不是宿主机物理内存的百分比。
1.2 门户里的"内存"到底在看什么
在 Azure 门户的 App Service "指标" 页面里,有一个很常用的指标叫 Memory working set。很多人直接看这个百分比,然后发现它到了某个值就再也不涨了,于是在群里问:是不是被限制了?
这里要澄清一下:working set 是进程当前驻留在物理内存中的那部分页面集合,不是进程累计分配的虚拟内存。对一个 .NET 进程来说,它申请了大量托管堆内存,但真正放进物理内存的只有被实际访问的部分,其余部分可以驻留在磁盘页面文件里,或者还没被系统换入。
更关键的是:App Service 的配额指标是 working set 与配额的比率,不是"宿主机内存使用率"。所以你会看到一种现象:进程的私有字节(Private Bytes)已经涨得很高了,但门户里的 Mem 百分比还是 70% 左右不动。这个在后面讲进程内存概念时再展开。
注意:不要用宿主机视角去理解 App Service 里的内存指标。在你的进程眼里,"内存"是配额;在宿主机的眼里,"内存"是整个物理机。这两个
