Skip to content
All writing ‹ Part 02 of 11 · From 19 Goroutines to Exit 137
Engineering · 2 min read

Runtime Environment: Socket, Container, Stack, Heap, cgo

GOMEMLIMIT governs the Go heap. It can't reach the browser sidecar, and it can't see cgo's malloc. Where your code actually runs matters.

GOMEMLIMIT governs the Go heap. It cannot reach the browser sidecar, and it cannot see cgo’s malloc.

Where Your Code Actually Runs

Node (64 cores) Pod Container Go app mem: 512Mi Sidecar Browser mem: 2Gi
  • Socket = one end of a network conversation (IP + port). TCP connection = the open pipe between two sockets. HTTP keep-alive reuses the same pipe, which pins retries under L4 load balancing (Keep-Alive Pins Retries).
  • L4 picks a destination once at connection time. L7 re-picks per request. K8s Service defaults to L4.
  • Container = app + deps, isolated via cgroup for CPU/memory. Sidecar = a second container in the same pod. Shared lifecycle, but separate process and memory.
  • Stack = per-goroutine scratch space, auto-freed on return. Heap = data that outlives the function, reclaimed by GC. GOMEMLIMIT only governs the heap.
  • cgo / off-heap = Go calling a C library (for example, libvips). The C side’s malloc memory is completely invisible to the Go runtime. GOMEMLIMIT and GOMAXPROCS cannot touch it.

Role in This OOM

The browser sidecar is another process in the same pod (OS Vocabulary: Process to Syscall), with its own 2Gi memory ceiling. The Go heap was only 256Mi. GOMEMLIMIT was set on the Go process. It cannot touch the browser’s memory at all (Wrong Fix: GOMEMLIMIT and Git Archeology).

Same pod does not mean same memory. A sidecar has its own ceiling and its own way to die.

References:

Related: see OS Vocabulary: Process to Syscall for the process/thread boundary. See GOMAXPROCS Caps Parallelism, Not Concurrency for why cpu:1 does not limit I/O-bound work. Or go back to the series overview.

↑↓ navigate ↵ open esc close