hugo@portfolio ~/blog/reading-cgroup-memory-in-go> cat index.md

reading-cgroup-memory-in-go/index.md

Reading cgroup v2 memory stats in Go

· 1 min read · view source

Sample post — replace it with your own. It's here to exercise code highlighting.

On a cgroup v2 system every cgroup is a directory under /sys/fs/cgroup, and its memory accounting lives in plain text files. No library required.

Tree of /sys/fs/cgroup with system.slice containing memory.current
Each cgroup is a directory; its accounting lives in plain files
package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
)

// memoryCurrent returns the current memory usage of a cgroup in bytes.
func memoryCurrent(cgroup string) (uint64, error) {
	raw, err := os.ReadFile("/sys/fs/cgroup/" + cgroup + "/memory.current")
	if err != nil {
		return 0, fmt.Errorf("read memory.current: %w", err)
	}
	return strconv.ParseUint(strings.TrimSpace(string(raw)), 10, 64)
}

func main() {
	n, err := memoryCurrent("system.slice")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	fmt.Printf("system.slice uses %d MiB\n", n>>20)
}

You can sanity-check the result from a shell:

cat /sys/fs/cgroup/system.slice/memory.current
systemd-cgtop -m --depth=1

What the number includes#

memory.current counts anonymous memory and page cache charged to the cgroup, so a service that reads a lot of files will look "bigger" than its RSS. For the breakdown, read memory.stat:

field meaning
anon anonymous memory (heap, stacks)
file page cache
slab kernel slab allocations