golang中内存管理分配

Posted Leo Han

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang中内存管理分配相关的知识,希望对你有一定的参考价值。

我们知道传统C/C++编程,程序员是需要自己来手动管理内存的,一般通过malloc来申请内存,通过free、delete来释放内存,而这两个函数操作的对象一般都是指针。而对于高级语言(Java、Golang、Python等),程序员在编写代码时,则无需关系内存的申请和释放,能够从内存的管理中解脱出来。

一般程序运行都有所谓的堆区和栈区。栈区是方法运行的区域,当调用一个方法的时候,该方法入栈,然后一些局部变量在栈上分配,当函数执行完之后,栈上对应函数空间出栈销毁。如果我们需要将函数执行过程中的一些数据返回,一般都是将其分配在堆上.
例如在传统的C中,如果我们在函数内创建一个局部变量(不通过malloc分配内存),则这个变量分配在栈上,当函数执行完之后,对应的栈上的数据会被销毁,函数返回了这个局部变量的地址,那么在调用这个函数后操作这个地址会出现难以预估的错误。
另外,对于C\\C++而言,直接使用malloc之后,有可能会导致内存碎片问题。

而现在的高级语言,比如java、golang都有自己的垃圾回收,进行自动内存管理,不需要手动去干预管理内存。golang中也是这样,会自动进行垃圾回收。

一般在栈上分配的内存不需要去管理会随着栈的销毁而被销毁,但是堆上分配的内存不会

在golang中一般在对上分配内存可以通过显示调用newmake指令。这两个指令都能够在对上创建分配内存,另外golang在堆上分配内存并不是直接使用malloc,而是直接封装了一层,在这层上增加了缓存层,实现了类似TCmalloc效果。
(底层都是通过调用mallocgc)
大家觉得下面这段代码中Person是在堆上还是在栈上分配的:

func GetPerson() *Person 
	var p Person
	p.age = 20
	p.name = "leo"
	return &p


func main() 
	p := GetPerson()
	fmt.Println(p.name)

这段代码如果对于C而言,程序会出现意想不到的错误,因为在栈上分配了一个对象,但是把对象的地址返回,函数结束后,栈上对应的地址内容就已经改变了。
对于golang而言,这段程序可以正常运行。这里就涉及到golang中的逃逸分析:

1. 如果对象被其他调用引用,只要有可能被引用,那么分配在堆上,否则分配在栈上
2. 即使没有被外部引用,但是对象太大,无法存在在栈u,那么也在堆上分配

逃逸分析主要是为了确定变量应该在堆上分配还是栈上分配。如果函数执行完之后,变量没有被外部引用,那么变量就会在栈上分配,否则将变量放入堆中。

实际上java中的内存分配也有类似的机制-逃逸分析。逃逸分析还是为了高效的垃圾回收。实际上我们也可以将变量都在堆上分配,但是这样的问题是来及回收的工作量巨大。

new、make

golang中通过显示调用new、make能够在堆上分配内存,二者的不同点在于:

  1. new返回的是新分配对象零值的指针,只是将内存清零,并没有初始化;而make返回的是一个有初始值的对象,不是指针,内存已经初始化
  2. make返回的还是类型本身;而new返回的是指向类型的指针。
  3. make只能用来分配及初始化类型为slice,map,channel的数据;new可以分配任意类型的数据。

以上是关于golang中内存管理分配的主要内容,如果未能解决你的问题,请参考以下文章

图解Golang的内存分配

Golang---内存管理(内存分配)

Golang 1.14中内存分配、清扫和内存回收

图解Go语言内存分配

好未来源码分析:Golang内存分配

golang内存扩容