El compilador de Go aplica el mecanismo de análisis de escape: todos los valores, por defecto, se colocan en la pila, pero si una variable "sale" de su ámbito (por ejemplo, se devuelve un puntero a una variable local), se coloca automáticamente en el montón (heap).
Esto es importante para el rendimiento:
El compilador intenta determinar si se puede colocar un objeto en la pila, pero si sobrevive implícitamente los límites de la función, se coloca en el montón.
Ejemplo con escape:
func NewPoint() *int { a := 42 return &a // ¡escape al heap! }
Ejemplo sin escape:
func Sum(a, b int) int { c := a + b return c // en la pila }
Para diagnosticar el comportamiento del compilador, use go build -gcflags='-m' para ver los detalles:
go build -gcflags='-m' main.go
"¿Escapará un objeto al montón si se devuelve de la función solo su valor — no un puntero?"
A menudo se piensa que todos los valores devueltos "escapan al montón". En realidad, si se devuelve un valor (no un puntero), la variable puede permanecer en la pila.
Ejemplo:
func F() int { x := 10 return x // asignación en la pila, no escapa al heap }
Historia
Al crear muchas estructuras a través de una función de fábrica que devolvía punteros a variables locales, el consumo de memoria y la carga del GC aumentaron drásticamente. Resultó que todos los objetos escapaban al heap debido a la devolución de un puntero a una variable local, aunque se podía haber evitado devolviendo el valor.
Historia
En un microservicio, debido a la transmisión de punteros entre gorutinas y su devolución desde diferentes funciones, muchos objetos "escapaban" de la pila al heap, lo que ralentizaba el funcionamiento y provocaba pausas frecuentes del GC.
Historia
Un desarrollador accidentalmente envolvió un arreglo estático en un slice dentro de una función, devolvió un puntero al slice — y durante una prueba de carga comenzó a experimentar fugas de memoria. La diagnosis mostró que la variable no "salía" de los límites de la función, pero Go decidió erróneamente colocarla en el heap debido al uso indirecto.