go理论知识总结
基于const常量理解个中类型的内存分配引入参考
官方:Constant expressions may contain only constant operands and are evaluated at compile time.
Using constants can often be more efficient than using variables where possible because any references to the constant will be replaced at compile time使用常量通常比在可能的情况下使用变量更有效,因为对常量的任何引用都将在编译时被替换;理解:如const A=7 m:=A 编译后就直接是m:=7用数字替换完了自然没有那个所谓A的地址
In many popular programming environments the stack usually refers to the call stack of a thread;A call stack is a LIFO stack data structure that stores arguments, local variables, and other data tracked as a thread executes functions
在许多流行的编程环境中,堆栈通常是指线程的调用堆栈
We must be able to safely free the memory of the most recent stack frame when it’s popped. We therefore can’t store anything on the stack that later needs to be referenced elsewhere.
Since threads are managed by the OS, the amount of memory available to a thread stack is typically fixed,e.g. a default of 8MB in many Linux environments。This means we also need to be mindful of how much data ends up on the stack, particularly in the case of deeply-nested recursive functions. If the stack pointer in the diagram above passes the stack guard, the program will crash with a stack overflow error.由于线程由操作系统管理,线程堆栈可用的内存量通常是固定的
The heap is a more complex area of memory . We can use the heap on demand to store data needed in our program. Memory allocated here can’t simply be freed when a function returns, and needs to be carefully managed to avoid leaks and fragmentation. 我们可以按需使用堆来存储程序所需的数据,这里分配的内存不能简单地在函数返回时释放,需要小心管理以避免泄漏和碎片
The Go stack and heap
Threads managed by the OS are completely abstracted away from us by the Go runtime, and we instead work with a new abstraction: goroutines. Goroutines are conceptually very similar to threads, but they exist within user space. This means the runtime, and not the OS, sets the rules of how stacks behave。操作系统管理的线程被 Go 运行时完全抽象出来,我们使用一个新的抽象来工作:goroutines。 Goroutines 在概念上与线程非常相似,但它们存在于用户空间中。 这意味着运行时而不是操作系统设置堆栈行为的规则
Rather than having hard limits set by the OS, goroutine stacks start with a small amount of memory (currently 2KB),Before each function call is executed, a check within the function prologue is executed to verify that a stack overflow won’t occur。This means that stacks in Go are dynamically sized, and can typically keep growing as long as there’s enough memory available to feed them。与操作系统设置硬限制不同,goroutine 堆栈以少量内存(当前为 2KB)开始,在执行每个函数调用之前,会执行函数序言中的检查以验证不会发生堆栈溢出,这意味着 Go 中的堆栈是动态大小的,并且只要有足够的可用内存来喂它们,通常就可以保持增长
The Go heap。All goroutines share a common heap and anything that can’t be stored on the stack will end up there.所有 goroutine 共享一个公共堆,任何不能存储在堆栈上的东西都将最终存储在那里。
①线程-堆栈-内存
go数组和切片,参考https://go.dev/blog/slices-intro
Go’s arrays are values. An array variable denotes the entire array; it is not a pointer to the first array element (as would be the case in C).This means that when you assign or pass around an array value you will make a copy of its contents.而切片A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment).
The length is the number of elements referred to by the slice. The capacity is the number of elements in the underlying array (beginning at the element referred to by the slice pointer). The distinction between length and capacity will be made clear as we walk through the next few examples.
As we slice s
, observe the changes in the slice data structure and their relation to the underlying array:s = s[2:4]
Slicing does not copy the slice’s data. It creates a new slice value that points to the original array.
This makes slice operations as efficient as manipulating array indices.
Therefore, modifying the elements (not the slice itself) of a re-slice modifies the elements of the original slice:
关于切片引用数组导致整个数组不释放问题
As mentioned earlier, re-slicing a slice doesn’t make a copy of the underlying array. The full array will be kept in memory until it is no longer referenced. Occasionally this can cause the program to hold all the data in memory when only a small piece of it is needed.
For example, this FindDigits
function loads a file into memory and searches it for the first group of consecutive numeric digits, returning them as a new slice.
var digitRegexp = regexp.MustCompile("[0-9]+")
func FindDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
return digitRegexp.Find(b)
}
This code behaves as advertised, but the returned []byte
points into an array containing the entire file. Since the slice references the original array, as long as the slice is kept around the garbage collector can’t release the array; the few useful bytes of the file keep the entire contents in memory.
To fix this problem one can copy the interesting data to a new slice before returning it:
func CopyDigits(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
b = digitRegexp.Find(b)
c := make([]byte, len(b))
copy(c, b)
return c
}
A more concise version of this function could be constructed by using append
. This is left as an exercise for the reader.