Stack vs Heap

分享到:

Stack vs Heap

  1package upload
  2
  3import (
  4	"fmt"
  5	"os"
  6	"runtime/trace"
  7	"testing"
  8)
  9
 10type S struct {
 11	a, b, c int64
 12	d, e, f string
 13	g, h, i float64
 14}
 15func byCopy() S {
 16	return S{
 17		a: 1, b: 1, c: 1,
 18		e: "foo", f: "foo",
 19		g: 1.0, h: 1.0, i: 1.0,
 20	}
 21}
 22
 23func byPointer() *S {
 24	return &S{
 25		a: 1, b: 1, c: 1,
 26		e: "foo", f: "foo",
 27		g: 1.0, h: 1.0, i: 1.0,
 28	}
 29}
 30
 31// 数据分配密集型 好
 32// 由于没有使用堆,因此没有垃圾收集器,也没有额外的 goroutine
 33func BenchmarkMemoryStack1(b *testing.B) {
 34	var s S
 35	// go tool trace stack.out
 36	f, err := os.Create("stack.out")
 37	if err != nil {
 38		panic(err)
 39	}
 40	defer f.Close()
 41
 42	err = trace.Start(f)
 43	if err != nil {
 44		panic(err)
 45	}
 46
 47	for i := 0; i < b.N; i++ {
 48		s = byCopy()
 49	}
 50
 51	trace.Stop()
 52
 53	b.StopTimer()
 54
 55	_ = fmt.Sprintf("%v", s.a)
 56}
 57// 数据分配密集型 坏
 58// 使用指针迫使 go 编译器将变量逃逸到堆,由此增大了垃圾回收器的压力。垃圾回收器占据了进程的重要部分
 59func BenchmarkMemoryHeap1(b *testing.B) {
 60	var s *S
 61	// go tool trace heap.out
 62	f, err := os.Create("heap.out")
 63	if err != nil {
 64		panic(err)
 65	}
 66	defer f.Close()
 67
 68	err = trace.Start(f)
 69	if err != nil {
 70		panic(err)
 71	}
 72
 73	for i := 0; i < b.N; i++ {
 74		s = byPointer()
 75	}
 76
 77	trace.Stop()
 78
 79	b.StopTimer()
 80
 81	_ = fmt.Sprintf("%v", s.a)
 82}
 83
 84
 85
 86func (s S) stack(s1 S) {}
 87
 88func (s S) stack2(s1 *S) {}
 89
 90func (s *S) heap(s1 *S) {}
 91
 92func (s *S) heap2(s1 S) {}
 93
 94// 方法调用密集型 坏
 95func BenchmarkMemoryStack2(b *testing.B) {
 96	var s S
 97	var s1 S
 98
 99	s = byCopy()
100	s1 = byCopy()
101	for i := 0; i < b.N; i++ {
102		for i := 0; i < 1000000; i++  {
103			s.stack(s1)
104		}
105	}
106}
107
108// 方法调用密集型 同BenchmarkMemoryStack2一样
109func BenchmarkMemoryStack2_2(b *testing.B) {
110	var s S
111	var s1 *S
112
113	s = byCopy()
114	s1 = byPointer()
115	for i := 0; i < b.N; i++ {
116		for i := 0; i < 1000000; i++  {
117			s.stack2(s1)
118		}
119	}
120}
121
122// 方法调用密集型 好
123func BenchmarkMemoryHeap2(b *testing.B) {
124	var s *S
125	var s1 *S
126
127	s = byPointer()
128	s1 = byPointer()
129	for i := 0; i < b.N; i++ {
130		for i := 0; i < 1000000; i++ {
131			s.heap(s1)
132		}
133	}
134}
135
136// 方法调用密集型 同BenchmarkMemoryHeap2一样 说明跟 func (s *S)有关系,跟参数没有关系,同时也说了传值没有性能影响
137func BenchmarkMemoryHeap2_2(b *testing.B) {
138	var s *S
139	var s1 S
140
141	s = byPointer()
142	s1 = byCopy()
143	for i := 0; i < b.N; i++ {
144		for i := 0; i < 1000000; i++ {
145			s.heap2(s1)
146		}
147	}
148}
149
150

Go:我应该用指针替代结构体的副本吗? https://studygolang.com/articles/21763/comment/33581

Memory : Stack vs Heap

Stack vs Heap

So far we have seen how to declare basic type variables such as int, double, etc, and complex types such as arrays and structs. The way we have been declaring them so far, with a syntax that is like other languages such as MATLAB, Python, etc, puts these variables on the stack in C.

The Stack

What is the stack? It's a special region of your computer's memory that stores temporary variables created by each function (including the main() function). The stack is a "LIFO" (last in, first out) data structure, that is managed and optimized by the CPU quite closely. Every time a function declares a new variable, it is "pushed" onto the stack. Then every time a function exits, all of the variables pushed onto the stack by that function, are freed (that is to say, they are deleted). Once a stack variable is freed, that region of memory becomes available for other stack variables.

The advantage of using the stack to store variables, is that memory is managed for you. You don't have to allocate memory by hand, or free it once you don't need it any more. What's more, because the CPU organizes stack memory so efficiently, reading from and writing to stack variables is very fast.

A key to understanding the stack is the notion that when a function exits, all of its variables are popped off of the stack (and hence lost forever). Thus stack variables are local in nature. This is related to a concept we saw earlier known as variable scope, or local vs global variables. A common bug in C programming is attempting to access a variable that was created on the stack inside some function, from a place in your program outside of that function (i.e. after that function has exited).

Another feature of the stack to keep in mind, is that there is a limit (varies with OS) on the size of variables that can be stored on the stack. This is not the case for variables allocated on the heap.

To summarize the stack:

  • the stack grows and shrinks as functions push and pop local variables
  • there is no need to manage the memory yourself, variables are allocated and freed automatically
  • the stack has size limits
  • stack variables only exist while the function that created them, is running

The Heap

The heap is a region of your computer's memory that is not managed automatically for you, and is not as tightly managed by the CPU. It is a more free-floating region of memory (and is larger). To allocate memory on the heap, you must use malloc() or calloc(), which are built-in C functions. Once you have allocated memory on the heap, you are responsible for using free() to deallocate that memory once you don't need it any more. If you fail to do this, your program will have what is known as a memory leak. That is, memory on the heap will still be set aside (and won't be available to other processes). As we will see in the debugging section, there is a tool called valgrind that can help you detect memory leaks.

Unlike the stack, the heap does not have size restrictions on variable size (apart from the obvious physical limitations of your computer). Heap memory is slightly slower to be read from and written to, because one has to use pointers to access memory on the heap. We will talk about pointers shortly.

Unlike the stack, variables created on the heap are accessible by any function, anywhere in your program. Heap variables are essentially global in scope.

Stack vs Heap Pros and Cons

Stack

  • very fast access
  • don't have to explicitly de-allocate variables
  • space is managed efficiently by CPU, memory will not become fragmented
  • local variables only
  • limit on stack size (OS-dependent)
  • variables cannot be resized

Heap

  • variables can be accessed globally
  • no limit on memory size
  • (relatively) slower access
  • no guaranteed efficient use of space, memory may become fragmented over time as blocks of memory are allocated, then freed
  • you must manage memory (you're in charge of allocating and freeing variables)
  • variables can be resized using realloc()