Go的内存管理缺陷?

根据上一篇文章(dwing:谈谈JVM和Go的内存管理差异), 我们了解了Go内存管理的一些细节.

其中Go为了加快内存分配速度,并方便GC回收内存再利用,于是针对不同大小做了很多档位的池,这些池的分配单元是固定的,而且Go目前还不支持内存移动,那么可以想象出如果遇到最坏的情况,内存利用率会变得非常差.

因此我尝试构造了一个针对性的程序(Go 1.11.2, win64):

package main

func main() {
	const ARRAY_SIZE int = 1024 * 1024 * 6
	var a [ARRAY_SIZE][]byte
	var b [ARRAY_SIZE][]byte
	for i, p := 16, 0; i <= 256; i += 16 {
		println(i)
		for j := 0; j < ARRAY_SIZE; j++ {
			a[j] = make([]byte, i)
			a[j][0] = 1
			if j % (8192 / i) == 0 {
				b[p] = a[j]
				p++
			}
		}
	}
	println("end")
}

如果你的电脑内存不超过16G(包括交换/页面文件的空间), 那么在跑完之前就会遇到"fatal error: out of memory". 即使设置"GOGC=1"的环境变量也无济于事.

同样的程序用Java来写,即使限制了2G的最大堆也能跑完(OpenJDK 11.0.1,win64,默认的G1GC):

public class TestMem {
	public static void main(String[] args) {
		final int ARRAY_SIZE = 1024 * 1024 * 6;
		byte[][] a = new byte[ARRAY_SIZE][];
		byte[][] b = new byte[ARRAY_SIZE][];
		for(int i = 16, p = 0; i <= 256; i += 16) {
			System.out.println(i);
			for(int j = 0; j < ARRAY_SIZE; j++) {
				a[j] = new byte[i];
				a[j][0] = 1;
				if(j % (8192 / i) == 0)
					b[p++] = a[j];
			}
		}
		System.out.println("end");
	}
}

PS: 这只是个有趣的极端情况测试, 并不代表Go真的有缺陷, 实际的程序很难触发这种情况.

编辑于 2019-01-08