Engineering · 1 min read
記憶體三層模型:cgroup → allocator → heap
RSS 大於 heap inuse 是正常的。中間夾了一層分配器,它拿了記憶體不一定馬上還。三層模型、三套指標。
RSS > heap inuse 是正常的,中間夾了一層分配器,它拿了記憶體不一定馬上還。
三層,三套指標
cgroup 層
memory.current= 所有實體記憶體:RSS + kernel 分配(file cache、socket buffer)。memory.max= 硬上限,超過 = OOM Kill (exit 137)。- 不只看 RSS:kernel-charged memory(大量 socket、file cache)也會推過
memory.max。
分配器層
new(obj)不直接 syscall,分配器預先從 kernel 拿 pages,切成 size-class slots。GC 回收物件時,slot 還給分配器,不是還給 kernel,所以pprof inuse_space掉了,RSS 不一定掉。
Heap 層(cAdvisor + Go runtime)
container_memory_usage_bytes≈ cgroupmemory.current。container_memory_working_set_bytes= K8s 驅逐依據。heap_alloc= 活物件精確位元組。heap_inuse − heap_alloc= 碎片(活物件佔住整個 span)。heap_idle − heap_released= 分配器持有但還沒還給 kernel 的。
在這個 OOM 裡的角色
瀏覽器的記憶體不在這三層裡,它是另一個 process,有自己的 cgroup 帳。Go heap 小不代表 pod 安全;要同時看瀏覽器 sidecar 的 container_memory_usage_bytes(OOM 診斷決策樹)。
記憶體有三層真相:application(pprof)→ process(RSS + allocator)→ kernel(cgroup)。哪層大,問題就在哪層。
參考來源:
- https://man7.org/linux/man-pages/man7/cgroups.7.html
- https://docs.kernel.org/admin-guide/cgroup-v2.html
- https://github.com/google/cadvisor/blob/master/docs/storage/prometheus.md
- https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/
- https://pkg.go.dev/runtime#MemStats
- https://go.dev/doc/gc-guide