golang 栈、堆
作者:互联网
golang 栈、堆
https://segmentfault.com/a/1190000017498101
https://juejin.cn/post/6943596197349163015
https://xie.infoq.cn/article/530c735982a391604d0eebe71
数据结构的堆栈:
堆:堆可以被看成是一棵树,如:堆排序。
栈:一种先进后出的数据结构。
内存分配中的堆和栈
栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
堆栈缓存方式
栈使用的是一级缓存,被调用时处于存储空间中,调用完毕立即释放。
堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
堆和栈都是编程语言里的虚拟概念,并不是说在物理内存上有堆和栈之分,两者的主要区别是栈是每个线程或者协程独立拥有的,从栈上分配内存时不需要加锁。而整个程序在运行时只有一个堆,从堆中分配内存时需要加锁防止多个线程造成冲突,同时回收堆上的内存块时还需要运行可达性分析、引用计数等算法来决定内存块是否能被回收,所以从分配和回收内存的方面来看栈内存效率更高。
go协程栈区的初始化大小2KB,java线程默认栈2MB。
变量是堆(heap)还是堆栈(stack)
Go 编译器自行决定变量分配在堆栈或堆上,以保证程序的正确性。
栈内存、堆内存
栈内存由编译器自动分配和释放,开发者无法控制。栈内存一般存储函数中的局部变量、参数等,函数创建的时候,这些内存会被自动创建;函数返回的时候,这些内存会被自动释放,所以栈内存效率会很高。
栈扩容和栈缩容
栈扩容
编译器会为函数调用插入运行时检查runtime.morestack
,它会在几乎所有的函数调用之前检查当前goroutine
的栈内存是否充足,如果当前栈需要扩容,会调用runtime.newstack
创建新的栈。
栈缩容
在goroutine
运行的过程中,如果栈区的空间使用率不超过 1/4,那么在垃圾回收的时候使用runtime.shrinkstack
进行栈缩容,当然进行缩容前会执行一堆前置检查,都通过了才会进行缩容。
栈内存是应用程序中重要的内存空间,它能够支持本地的局部变量和函数调用,栈空间中的变量会与栈一同创建和销毁,这部分内存空间不需要工程师过多的干预和管理,现代的编程语言通过逃逸分析减少了我们的工作量。
栈空间扩缩容
- 调用
runtime.newstack
在内存空间中分配更大的栈内存空间。 - 使用
runtime.copystack
将旧栈中的所有内容复制到新的栈中。 - 将指向旧栈对应变量的指针重新指向新栈。
- 调用
runtime.stackfree
销毁并回收旧栈的内存空间。
堆内存的生命周期比栈内存要长,如果函数返回的值还会在其他地方使用,那么这个值就会被编译器自动分配到堆上。堆内存相比栈内存来说,不能自动被编译器释放,****只能通过垃圾回收器才能释放。
全局指针变量和局部指针变量
案例一
var p *int //全局指针变量
func f(){
var i int
i = 1
p = &i //全局指针变量指向局部变量i
}
案例二
func f(){
p := new(int) //局部指针变量,使用new申请的空间
*p = 1
}
第一个案例中,使用var定义局部变量,但是由于i赋值给全局指针变量p,当函数结束,此时i并不会被释放,所以局部变量i是申请在堆上(程序员手动释放)。
- 局部变量:在函数中定义的变量,它有一个动态的生命周期:
每次执行的时候就创建一个新的实体,一直生存到没有人使用(例如没有外部指针指向它,函数退出的时候没有路径访问到这个变量)这个时候它占用的空间就会被回收
第二个案例中,使用new申请空间,由于退出函数后p就会被释放,所以p是申请在****栈上
(自动释放)
标签:释放,变量,局部变量,回收,golang,内存,指针 来源: https://www.cnblogs.com/ling11/p/16318253.html