golang内存管理与垃圾回收
随便聊一聊……
栈和堆简介 & 变量分配
用过C/C++的人都知道,内存分为栈和堆两个部分(其实还有静态区什么的,先忽略);堆从低向高增长,栈从高向低增长;new出来的变量分配在堆上,函数里的变量分配在栈上……所以下面的代码会得到一个warning(但并不是不能跑,但结果是有问题的)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> int *func() { int x = 5; return &x; } int main() { int *p = func(); int x[100] = {1}; // 为了抹掉栈空间 std::cout << (*p) << std::endl; } # malloc.cpp:5:12: warning: address of stack memory associated with local variable 'x' returned [-Wreturn-stack-address] # return &x; # ^ |
Go语言里也有栈和堆的概念,栈就是函数使用的栈,堆是GC堆。
但是Go作为一个内存安全的语言,如果使用这种分配方法,会导致出现dangling pointer。最简单的解决方案是:所有变量都分配到GC堆上,栈只作为调用栈使用。
这种一刀切的方式明显对性能是有很大影响的,需要GC的对象急剧增加,会使得STW时间变长。还好有静态分析技术 Escape Analysis 可以帮助我们在编译器知道:哪些变量是会在作用域外被使用的,将这些变量分配到GC堆中,而其他的变量分配在栈上。
我们可以通过 go tool compile -N -m test.go 查看escape analyse的日志(-N是禁止inlining,-m是打印verbose),也可以通过 go tool compile -N -S -l go.test 看内存分配的汇编代码: