liveBook · Manning

Memory allocation

As explained in Go by Example

A memory allocation is a reservation of heap memory made during program execution, commonly counted per operation in performance measurements.

A memory allocation reserves storage while a program runs. In Go performance work, allocations are commonly measured as allocations per operation and bytes per operation. Heap allocations need runtime bookkeeping and garbage collection, so reducing unnecessary ones can improve memory use and latency. Treat allocation count as a measurement to investigate, not a reason to sacrifice clear, correct code. section 3.2.3 section 4.2.3 section 3.2.5

Start with evidence. Run a benchmark repeatedly with memory reporting, save a memory profile, and inspect the allocating lines with pprof. Repeated runs help reduce noise from machine load, temperature, and similar environmental variation. Compare an old and revised implementation with benchstat rather than judging a number in isolation. section 3.2.3 section 3.2.5

$ go tool pprof -list String mem.out
. . .
18.83GB    18.83GB (flat, cum)   100% of Total
      .          .     35:func (u *URL) String() string {
      .          .     36:   if u == nil {
      .          .     37:     return ""
      .          .     38:   }
      .          .     39:   var s string
      .          .     40:   if sc := u.Scheme; sc != "" {
      .          .     41:     s += sc #1
 2.44GB     2.44GB     42:     s += "://" #2
      .          .     43:   }
      .          .     44:   if h := u.Host; h != "" {
 4.66GB     4.66GB     45:     s += h #2
      .          .     46:   }
      .          .     47:   if p := u.Path; p != "" {
 4.63GB     4.63GB     48:     s += "/" #3
 7.10GB     7.10GB     49:     s += p #3
      .          .     50:   }
      .          .     51:   return s
      .          .     52:}
. . .

The profile attributes allocation volume to each string-concatenation line, making repeated rebuilding visible as the optimization target. section 3.2.3

Repeated string building

Combining strings creates new strings because strings are immutable and each combination copies data into a new underlying array. A URL String method that repeatedly uses + allocates four times. Replacing that approach with one contiguous reserved slice reduces the result to one allocation; the compared benchmark falls from 919 to 361 bytes per operation and from four to one allocation per operation. section 3.2.3 section 3.2.5

Use allocation-aware choices without assuming that pointers are always cheaper:

  • Pass a configuration pointer to a function when the function can operate on the caller-owned value; this may avoid a heap allocation, but verify the compiler’s decision. section 4.2.3
  • Check escape analysis with go build -gcflags="-m" to see whether a variable moves to the heap or remains on the goroutine stack. section 4.2.3
  • Prefer value results when mutation and sharing are unnecessary. Value passing gives goroutines independent copies and can avoid pointer-related heap allocation and garbage-collector overhead. section 6.3.3
  • Stream data when only a response size is needed. Reading an entire unknown response with io.ReadAll stores all bytes and can repeatedly grow backing arrays. section 7.3.3

Allocation reductions matter most in repeated or high-load paths. Fewer heap objects mean less work for the garbage collector; in the URL example, one allocation also keeps data contiguous, while the prior approach scattered buffers across the heap. The resulting service can use less memory and show more consistent performance under load. section 3.2.5

How Go by Example covers it

How deep it goes
Explains profiling, benchmark comparison, escape-analysis output, and practical allocation trade-offs without detailing garbage-collector internals.
Examples
Uses URL string construction, a config flag parser, Result values in concurrent processing, and io.ReadAll on HTTP response bodies.
What it assumes
Assumes ability to run Go benchmarks and build commands while explaining heap allocation, stack behavior, and escape-analysis diagnostics.
What you can do after
Diagnose/debug with pprof, benchmark memory metrics, benchstat, and compiler escape-analysis output to target unnecessary allocations.
How it connects
Connects memory allocation with benchmarking, pprof, benchstat, escape analysis, garbage collection, string concatenation, pointers, and io.ReadAll.

Also explained in

Compare how 11 books explain memory allocation →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Go by Example. The section links point to the parts of the book it draws on.