Golang中goroutine的内存占用是多少?
Golang中goroutine的内存占用是多少? 各位 Gopher 们好,
一个 goroutine 的内存占用是多少?这里有一段引述:
一个新创建的 goroutine 会被分配几千字节,这几乎总是足够的。 当不够时,运行时会自动增长(和缩小)用于存储栈的内存,使得许多 goroutine 可以存在于适度的内存中。
所以它开始时是几千字节(具体是多少?),然后会自动增长和缩小。
增长和缩小的规则是什么?当需要更多内存时,它的容量会翻倍吗,还是会增加 50% 的内存?
同样地,缩小的规则是什么?缩小操作何时进行?我猜想是在一个 GC 周期释放了足够的内存,以至于其总占用小于某个阈值之后,那么这个阈值是多少?
此外,一个经常增加和减少其内存占用的 goroutine 会产生哪些性能开销?
更多关于Golang中goroutine的内存占用是多少?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
一根绳子有多长? Goroutine 占用的内存取决于它所执行的操作,即它分配了多少内存,以及其调用栈的深度。
更多关于Golang中goroutine的内存占用是多少?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
垃圾回收器不会修改栈。 但它的确会扫描栈以查找引用。
嗨 @ammon,
不知为何我感到困惑,我以为垃圾回收器只清理堆,而不会触及栈。这在 goroutine 中是如何运作的呢?
所有的 Goroutine 共享同一个垃圾回收内存池。因此,当垃圾收集器运行时,它将回收无法访问的对象,无论该对象是由哪个 goroutine 分配的。
嗨 @amnon
显然,一个 goroutine 所需的内存取决于它的具体操作。但问题的核心在于其扩展性:
我的 goroutine 当前已分配了 X 内存,现在它需要更多,因此被分配了额外的 Y 内存。X 和 Y 之间的关系是什么?
我的 goroutine 当前已分配了 X 内存,现在它需要更少,因此被分配了更少的 Y 内存。X 和 Y 之间的关系是什么?
感谢 @amnon,我现在明白你的意思了,并且意识到我应该把我的问题表述得更清楚一些:
一个给定的 goroutine 消耗的栈内存将如何波动?
- goroutine X 以 2048 字节的初始栈内存开始,如果该 goroutine 已经使用了 2048 字节,并且想要再使用一个额外的字节,接下来会发生什么?
- 类似地,另一个 goroutine Y 现在使用了 2049 字节的栈内存,但突然它不再需要其中 50% 的字节,那么栈会变成什么样子?
在Go中,goroutine的初始栈大小在Go 1.4之后从8KB减少到了2KB。具体大小可以通过runtime/debug包的SetMaxStack函数查看和调整,但通常不建议修改。
栈的增长和缩小规则如下:
栈增长: 当goroutine栈空间不足时(例如深度递归或大量局部变量),运行时会分配一个新的更大的栈,并将旧栈内容复制到新栈。增长策略通常是当前大小的2倍,直到达到最大值(默认为1GB,32位系统为250MB)。
示例代码,展示栈增长:
package main
import (
"fmt"
"runtime/debug"
)
func recursiveCall(depth int) {
var buf [1024]byte // 每个调用帧占用1KB
if depth == 0 {
return
}
recursiveCall(depth - 1)
_ = buf // 防止优化
}
func main() {
fmt.Println("初始栈大小:", debug.SetMaxStack(0)) // 获取当前最大栈大小
recursiveCall(1000) // 触发栈增长
}
栈缩小: 在垃圾回收(GC)期间,如果发现goroutine的栈使用量小于当前大小的1/4,运行时会将其缩小为当前大小的1/2。缩小的栈不会立即释放内存,而是放回缓存池供后续重用。
性能开销: 频繁的栈调整会导致:
- 内存复制开销:增长时需要复制整个栈内容。
- CPU缓存失效:栈地址变化影响局部性。
- GC压力:频繁调整增加GC扫描工作量。
示例代码,展示栈缩小:
package main
import (
"fmt"
"runtime"
"time"
)
func allocateLargeStack() {
var arr [1024 * 1024]byte // 占用1MB栈空间
_ = arr
}
func main() {
go func() {
allocateLargeStack()
runtime.GC() // 触发GC,可能引起栈缩小
time.Sleep(time.Second)
}()
time.Sleep(2 * time.Second)
}
可以通过runtime.ReadMemStats监控栈内存变化:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
var ms runtime.MemStats
for i := 0; i < 5; i++ {
runtime.ReadMemStats(&ms)
fmt.Printf("栈内存使用: %d MB\n", ms.StackSys/1024/1024)
time.Sleep(time.Second)
}
}
实际使用中,goroutine栈的自动管理在大多数场景下是高效的,仅在极端情况(如超深递归)下需要注意。


