golang的gc回收针对堆还是栈?变量内存分配在堆还是栈?

本文探讨Go语言中垃圾回收GC针对堆的特性,以及变量内存分配在堆和栈的情况。通过三个实验验证:全局引用类型变量在内存回收后仍可访问,局部引用类型变量在函数退出后未被回收,而特定情况下局部变量会被强制分配到堆并被GC回收。实验结果显示Go的GC只回收堆上的内存。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里不讲垃圾回收的机制

先给出三个结论:

  1. golang的垃圾回收是针对堆的(垃圾回收都是针对堆的,这里只是做一个简单的证明)
  2. 引用类型的全局变量内存分配在堆上,值类型的全局变量分配在栈上
  3. 局部变量内存分配可能在栈上也可能在堆上

堆和栈的简单说明:
1.栈(操作系统):由操作系统自动分配释放
2.堆(操作系统): 一般由程序员分配释放,例如在c/c++中,在golang,java,python有自动的垃圾回收机制

我们都知道变量占有内存,内存在底层分配上有堆和栈。

  • 值类型变量的内存通常是在栈中分配
  • 引用类型变量的内存通常在堆中分配

注意这里说的是"通常",因为变量又分为局部变量和全局变量。

  • 当变量是全局变量时,符合上面所说的分配规则
  • 但当变量是局部变量时,分配在堆和栈就变的不确定了

以下是摘录go圣经的一部分:

编译器会自动选择在栈上还是在堆上分配局部变量的存储空间,但可能令人惊讶的是,这个选择并不是由用var还是new声明变量的方式决定的。

var global *int

func f() {
   
    var x int
    x = 1
    global = &x
}

func g() {
   
    y := new(int)
    *y = 1
}

函数里的x变量必须在堆上分配,因为它在函数退出后依然可以通过包一级的global变量找到,虽然它是在函数内部定义的;用Go语言的术语说,这个x局部变量从函数f中逃逸了。相反,当g函数返回时,变量*y将是不可达的,也就是说可以马上被回收的。因此,y并没有从函数g中逃逸,编译器可以选择在栈上分配y的存储空间(译注:也可以选择在堆上分配,然后由Go语言的GC回收这个变量的内存空间),虽然这里用的是new方式。其实在任何时候,你并不需为了编写正确的代码而要考虑变量的逃逸行为,要记住的是,逃逸的变量需要额外分配内存,同时对性能的优化可能会产生细微的影响。

个人理解补充说明:go语言编译器会自动决定把一个变量放在栈还是放在堆,编译器会做逃逸分析(escape analysis),当发现变量的作用域没有跑出函数范围,就可以在栈上,反之则必须分配在堆。
可参考 Golang中的逃逸分析

下面做实验来证明三个结论:

(以下实验方法说明请参照Go的原生map中删除元素,内存会自动释放吗?

实验一:

说明的问题:引用类型的全局变量的内存可以被垃圾回收

package main

import (
	"log"
	"runtime"
)

var lastTotalFreed uint64 // size of last memory has been freed

func InitMap(a map[int]int) {
   
	for i := 0; i < 100000; i++ {
   
		a[i] = i
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值