What is escape
In a word,Escape analysis is an act used by the compiler to decide whether to assign variables to the stack or the heap。
As we all know, functions are run in the stack space in the operating system memory space. We declare temporary variables on the stack, allocate memory, and recycle memory after the function is run. The stack space of each function is independent, and other functions do not have permission to access. But in some cases, we need to access some data on the stack after the function ends, which involvesMemory escapeNow.
If the variable escapes from the stack, where will it escape? He would run to the pile. Since variables on the stack are automatically recycled at the end of the function, the recycling cost is relatively small; when the heap space allocates memory, you first need to find a piece of memory of the right size, and then recycle it through GC to release it. In this case, frequent use of garbage collection will take up a lot of overhead, so try to allocate memory to the stack to reduce the pressure on GC.
Escape analysis basic process
The most basic principles of escape analysis in Go language:If a function returns a reference to a variable, it will escape.
In any case, if a value is allocated outside the stack space, it must be allocated to the heap. In short: the compiler analyzes the characteristics and life cycle of the code. Variables in Go will only be allocated to the stack if the compiler can prove that the function will not be referenced again after it returns, otherwise it will be allocated to the heap.
Unlike new in C++, the new keyword in Go does not necessarily allocate memory to the heap space. In Go, there are no keywords or functions that can directly allocate variables to the heap, but instead analyze the code through the compiler to decide where to allocate variables.
In a word:
The compiler will decide whether to escape based on whether the variable is referenced externally.
If there is no reference outside the function, it is given priority to put it on the stack;
If there is a reference outside the function, it must be placed in the heap;
Common escape situations
Pointer escape
We know that passing pointers can reduce the copy of underlying values and improve efficiency. However, if the amount of copy data is small, pointer transfer will cause escape, which may use heap space and increase the burden on GC, so passing pointers is not necessarily efficient.
for example:
package main type Student struct { Name string Age int } func StudentRegister(name string, age int) *Student { s := new(Student) // Local variable s escapes to the heap = name = age return s } func main() { StudentRegister("similar", 18) }
Although the functionStudentRegister
The internal s is a local variable, but since the pointer is returned, the memory address it points to will not be the stack but the heap, which is a typical escape case.
Use commandsgo build -gcflags '-m -l'
,get:
# command-line-arguments
./:8:22: leaking param: name
./:9:10: new(Student) escapes to heap # indicates that there is an escape in the memory of the line
Insufficient stack space
If a slice with too large capacity is allocated on the stack, the object will be allocated to the heap when the stack space is insufficient to store the current object or the current slice length cannot be determined.
package main func MakeSlice() { s := make([]int, 10000, 10000) for index := range(s){ s[index] = index } } func main() { MakeSlice() }
Use the same commandgo build -gcflags '-m -l'
:
# command-line-arguments
./escape_1.go:4:11: make([]int, 10000) escapes to heap
Dynamic Type Escape
Many functions have parametersinterface
Types, such as:
func Printf(format string, a ...interface{}) (n int, err error) func Scanf(format string, a ...interface{}) (n int, err error) func Fprintf(w , format string, a ...interface{}) (n int, err error)
It is difficult to determine the specific type of its parameters during compilation, and it can also cause escape.
Variable size is uncertain
When creating slices and initializing slice capacity, a variable is sometimes passed in to specify its size. Since the value of the variable cannot be determined by the compiler, it cannot determine its size of space, so the compiler may directly allocate the variable to the heap.
package main func MakeSlice() { length := 1 a := make([]int, length, length) for i := 0; i < length; i++ { a[i] = i } } func main() { MakeSlice() }
Compilation result:
# command-line-arguments
./escape_1.go:5:11: make([]int, length, length) escapes to heap
Summary of common escape situations
Pointer escape: Return a local variable pointer inside the function
Allocation of large objects: resulting in insufficient stack space and having to allocate them to the heap
Calling interface-type methods, interface-type method calls are dynamic scheduling - the actual specific implementation can only be determined at runtime.
Although it can meet the scenarios allocated to the stack, its size cannot be determined at compile time and will also be allocated to the heap.
How to avoid
Method calls for interface types in Go are dynamically scheduled, so they cannot be determined during the compilation stage. The process of converting all type structures into interfaces will involve memory escape. If you have high performance requirements and high access frequency function calls, you should try to avoid using interface types.
Since slices are generally used in the scenario of function delivery, and slices may involve reallocating memory when append, if the size of the slice cannot be confirmed during compilation or the size exceeds the stack limit, they will be allocated to the heap in most cases.
Summarize
Allocating memory on the heap is much more expensive than allocating memory on the stack.
Variable allocation needs to be able to determine its scope during the compilation period on the stack, otherwise it will be allocated to the heap.
The Go language compiler will decide whether to escape by whether the variable is referenced externally.
passgo build -gcflags '-m'
Commands can observe whether variables escape
You cannot blindly use the pointer of a variable as a function parameter. Although it will reduce the copy operation, when the parameter is the variable itself, copy is an operation completed on the stack, and the overhead is much less than dynamically allocating memory on the heap after the variable escapes.
This is the end of this article about detailed explanation of escape analysis in Go. For more relevant Go escape analysis content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!