[奇怪] Golang中缓冲通道内存消耗异常问题探讨
[奇怪] Golang中缓冲通道内存消耗异常问题探讨 大家好,
这里有一个小基准测试,用于展示带缓冲通道消耗的内存:
func getChan1() chan int {
return make(chan int, 101)
}
var c chan int
func BenchmarkChan1(b *testing.B) {
for i := 0; i < b.N; i++ {
c = getChan1()
}
}
这段代码根据带缓冲通道的缓冲区大小S给出了不同的数值,以下是一些示例值: 1 -> 112 B/op 2 -> 112 B/op 3 -> 128 B/op 4 -> 128 B/op 5 -> 144 B/op 6 -> 144 B/op 7 -> 160 B/op
由此我推断出带缓冲整型通道的实际占用大小可通过以下公式计算:size(容量为S的整型通道) = 96 + 整型大小 * 2 * [(N+1) / 2]
为什么当S为奇数时,容量为S的通道与容量为S+1的通道会占用相同的内存?
这种情况虽然奇怪但还不算特别异常。接下来才是真正令人困惑的部分: 100 -> 896 B/op
101 -> 1024 B/op 102 -> 1024 B/op 103 -> 1024 B/op 104 -> 1024 B/op
199 -> 1792 B/op 200 -> 1792 B/op 201 -> 1792 B/op 202 -> 1792 B/op
300 -> 2688 B/op 301 -> 2688 B/op
998 -> 8192 B/op 999 -> 8192 B/op 1000 -> 8192 B/op 1001 -> 8192 B/op
这到底是怎么回事?当带缓冲通道的容量大于100时会出现额外开销,这些开销是从哪里来的?
这是与扩展机制有关,还是为了优化而对数据结构进行填充?或者可能是我的基准测试在处理较大数值时出现了问题?该如何解释这种行为?
更多关于[奇怪] Golang中缓冲通道内存消耗异常问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
大型对象会被向上取整到最接近的页面大小,并使用一系列该尺寸的堆分配。
更多关于[奇怪] Golang中缓冲通道内存消耗异常问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我在你的另一个主题中链接了一个这样的内容。
你所说的最大页面大小是什么意思?如果一个对象比一个页面大怎么办? 能否请你解释一下,或者提供一个简单易懂的教程链接?
vincent-163:
runtime.ReadMemStats
这是个很好的回答。不过有个问题:如果我分配一个大于19072字节(最大堆大小)的对象,并且根据逃逸分析该对象应该进入堆,那么堆会发生什么变化,其机制是怎样的?
// 示例代码请在此处添加
这是由于Go语言采用大小隔离堆模型所致。为了高效管理内存并避免碎片化,该模型根据对象大小将其分配到不同的堆中,因此每个堆中的所有对象大小都相等。每次分配内存时,运行时会将对象大小向上取整到最接近的堆规格,并从对应堆中分配项目。这种机制让运行时能够轻松管理内存,代价是每个对象会产生一定的开销。
可以通过调用runtime.ReadMemStats并检查BySize结构中的Size字段来获取每个堆的规格。在本地机器上调用该函数得到以下数值:
0, 8, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536, 1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072
由此可以观察到968、1024、1792、2688和8192这些数值都包含在列表中。


