我们知道传统C/C++编程,程序员是需要自己来手动管理内存的,一般通过malloc来申请内存,通过free、delete来释放内存,而这两个函数 *** 作的对象一般都是指针。而对于高级语言(Java、Golang、Python等),程序员在编写代码时,则无需关系内存的申请和释放,能够从内存的管理中解脱出来。
一般程序运行都有所谓的堆区和栈区。栈区是方法运行的区域,当调用一个方法的时候,该方法入栈,然后一些局部变量在栈上分配,当函数执行完之后,栈上对应函数空间出栈销毁。如果我们需要将函数执行过程中的一些数据返回,一般都是将其分配在堆上.
例如在传统的C中,如果我们在函数内创建一个局部变量(不通过malloc分配内存),则这个变量分配在栈上,当函数执行完之后,对应的栈上的数据会被销毁,函数返回了这个局部变量的地址,那么在调用这个函数后 *** 作这个地址会出现难以预估的错误。
另外,对于C\C++而言,直接使用malloc之后,有可能会导致内存碎片问题。
而现在的高级语言,比如java、golang都有自己的垃圾回收,进行自动内存管理,不需要手动去干预管理内存。golang中也是这样,会自动进行垃圾回收。
一般在栈上分配的内存不需要去管理会随着栈的销毁而被销毁,但是堆上分配的内存不会
在golang中一般在对上分配内存可以通过显示调用new
和make
指令。这两个指令都能够在对上创建分配内存,另外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、makegolang中通过显示调用new、make能够在堆上分配内存,二者的不同点在于:
- new返回的是新分配对象零值的指针,只是将内存清零,并没有初始化;而make返回的是一个有初始值的对象,不是指针,内存已经初始化
- make返回的还是类型本身;而new返回的是指向类型的指针。
- make只能用来分配及初始化类型为slice,map,channel的数据;new可以分配任意类型的数据。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)