Vì sao .NET ngốn 96% RAM container dù app đang rảnh? Server GC và cách giảm 65% bộ nhớ
Một container ASP.NET Core 9 chiếm 987 MiB trên trần 1 GiB (96%) dù suốt 2 giờ không nhận request nào. Đây không phải rò rỉ bộ nhớ: mức RAM đứng yên tuyệt đối qua nhiều lần đo, dấu hiệu của heap bị giữ lại chứ không phải rò rỉ. Nguyên nhân là Server GC — mặc định của ASP.NET Core, tạo một heap cho mỗi CPU core và rất ít trả bộ nhớ về hệ điều hành. Đặt DOTNET_gcServer=0 và DOTNET_GCConserveMemory=5 giảm bộ nhớ anon từ 822 MB xuống 280 MB (−65%), đo ổn định sau 30 phút. Đánh đổi: Workstation GC cho throughput thấp hơn khi tải cao và nhiều core.
Bài này ghi lại một buổi chẩn đoán thật trên VPS 4 core / 8 GB RAM đang chạy song song 9 site WordPress và một API .NET. Mọi con số bên dưới là số đo thật, không phải ví dụ minh hoạ.
Triệu chứng
docker stats cho con số đáng báo động:
$ docker stats --no-stream --format '{{.Name}} {{.MemUsage}} {{.MemPerc}}' vievent-api
vievent-api 987.6MiB / 1GiB 96.45%
Container đã chạy 2 tuần, RestartCount=2, trạng thái healthy. Ứng dụng là ABP 9.2 trên .NET 9.0.17, database PostgreSQL, cache Redis.
Phản xạ đầu tiên của hầu hết chúng ta: "rò rỉ bộ nhớ rồi". Phản xạ đó sai, và mất khá nhiều thời gian mới nhận ra.
