func mallocinit() { if class_to_size[_TinySizeClass] != _TinySize { throw("bad TinySizeClass") } testdefersizes() // 检查物理页大小 if physPageSize == 0 { // 操作系统初始化代码获取物理页大小失败 throw("failed to get system page size") } if physPageSize < minPhysPageSize { print("system page size (", physPageSize, ") is smaller than minimum page size (", minPhysPageSize, ")\n") throw("bad system page size") } if physPageSize&(physPageSize-1) != 0 { print("system page size (", physPageSize, ") must be a power of 2\n") throw("bad system page size") } // 内存区域从 p 开始,在内存中以: span, bitmap, arena 的顺序排列 var p, pSize uintptr var reserved bool // span 数组中,对每一块 _PageSize 的 arena 内存都持有一个 *mspan var spansSize uintptr = (_MaxMem + 1) / _PageSize * sys.PtrSize spansSize = round(spansSize, _PageSize) // 每一个 word 的 arena 区域都在 bitmap 中占用 2 bits var bitmapSize uintptr = (_MaxMem + 1) / (sys.PtrSize * 8 / 2) bitmapSize = round(bitmapSize, _PageSize) // 初始化内存分配 arena,arena 是一段连续的内存,负责数据的内存分配。 if sys.PtrSize == 8 { // 在 64 位机器上,分配一段连续的内存区域 512 GB(MaxMem) 现在应该是足够了。 // // 代码应该能够用任意地址进行工作,但可能的情况下尽量让 sysReserve 使用 0x0000XXc000000000 (xx=00...7f)。 // 分配 512 GB 的地址需要用掉 39 bits 来进行地址表达,amd64 平台不允许用户使用高位 17 bits // 所以只剩下中间的 0x00c0 中的 9 bits 能让我们用了。选择 0x00c0 表示合法的内存地址是从 // 0x00c0, 0x00c1, ..., 0x00df 开始 // 在小端系统上,即 c0 00, c1 00, ..., df 00。这些都不是合法的 UTF-8 序列,且距离 ff(常用的单字节) 足够远。 // 如果分配失败,会尝试其它 0xXXc0 地址。 // 之前使用 0x11f8 地址在 OS X 系统上会在线程分配的时候导致 out of memory 错误。 // 0x00c0 会导致和 AddressSanitizer 的冲突,AddressSanitizer 会将 0x0100 以下的地址都进行保留 // 这种选择是为了可调试性,且可以限制垃圾收集器(gccgo 中的版本)的保守性,以使其不要收集那些符合一定模式的内存地址中的内存。 // // 实际上我们会保留 544 GB(因为 bitmap 需要用 32 GB) // 不过这不重要: e0 00 也不是合法的 UTF-* 字符 // // 如果失败,会回退到 32 位内存策略 // // 然而在 arm64 平台我们会忽略上面所有的建议,直接梭哈分配到 0x40 << 32,因为 // 使用 4k 页搭配 3级 TLB 时,用户地址空间会被限制在 39 位能表达的范围之内, // 在 darwin/arm64 上,地址空间就更小了。 arenaSize := round(_MaxMem, _PageSize) pSize = bitmapSize + spansSize + arenaSize + _PageSize for i := 0; i <= 0x7f; i++ { switch { case GOARCH == "arm64" && GOOS == "darwin": p = uintptr(i)<<40 | uintptrMask&(0x0013<<28) case GOARCH == "arm64": p = uintptr(i)<<40 | uintptrMask&(0x0040<<32) default: p = uintptr(i)<<40 | uintptrMask&(0x00c0<<32) } p = uintptr(sysReserve(unsafe.Pointer(p), pSize, &reserved)) if p != 0 { break } } } if p == 0 { // 在 32 位机器上,我们没法简单粗暴地获得一段巨大的虚拟地址空间,并保留内存。 // 取而代之,我们将内存信息的 bitmap 紧跟在 data segment 之后, // 这样做足够处理整个 4GB 的内存空间了(256 MB 的 bitmap 消耗) // 初始化阶段会保留一小段地址 // 用完之后,我们再和 kernel 申请其它位置的内存。 // 我们想要让 arena 区域从低地址开始,但是我们的代码可能和 C 代码进行链接, // 全局的构造器可能已经调用过 malloc,并且调整过进程的 brk 位置。 // 所以需要查询一次 brk,以避免将我们的 arena 区域覆盖掉 brk 位置, // 这会导致 kernel 把 arena 放在其它地方,比如放在高地址。 procBrk := sbrk0() // 如果分配失败,那么尝试用更小一些的 arena 区域。 // 对于像 Android L 这样的系统是需要的,因为我们和 ART 更新同一个进程, // 其会更激进地保留内存。 // 最差的情况下,会退化为 0 大小的初始 arena // 这种情况下希望之后紧跟着的内存保留操作能够成功。 arenaSizes := []uintptr{ 512 << 20, 256 << 20, 128 << 20, 0, } for _, arenaSize := range arenaSizes { // sysReserve 会把我们要求保留的地址的末尾作为一种 hint,而不一定会满足 // 这种情况下需要我们自己对指针进行 roundup,先是 1/4 MB,以使其离开运行的二进制 // 镜像位置,然后在 roundup 到 MB 的边界位置 p = round(firstmoduledata.end+(1<<18), 1<<20) pSize = bitmapSize + spansSize + arenaSize + _PageSize if p <= procBrk && procBrk < p+pSize { // 将 start 移动到 brk 之上,给未来的 brk 扩展保留一些空间 p = round(procBrk+(1<<20), 1<<20) } p = uintptr(sysReserve(unsafe.Pointer(p), pSize, &reserved)) if p != 0 { break } } if p == 0 { throw("runtime: cannot reserve arena virtual address space") } } // PageSize 可能被 OS 定义的 page size 更大 // 所以 sysReserve 会返回给我们一个未 PageSize-对齐的指针。 // 我们需要对其进行 round up,以使其按我们的 PageSize 要求对齐 p1 := round(p, _PageSize) pSize -= p1 - p spansStart := p1 p1 += spansSize mheap_.bitmap = p1 + bitmapSize p1 += bitmapSize if sys.PtrSize == 4 { // 赋值 arena_start 这样我们相当于接受了 4GB 虚拟空间中的保留内存 mheap_.arena_start = 0 } else { mheap_.arena_start = p1 } mheap_.arena_end = p + pSize mheap_.arena_used = p1 mheap_.arena_alloc = p1 mheap_.arena_reserved = reserved if mheap_.arena_start&(_PageSize-1) != 0 { println("bad pagesize", hex(p), hex(p1), hex(spansSize), hex(bitmapSize), hex(_PageSize), "start", hex(mheap_.arena_start)) throw("misrounded allocation in mallocinit") } // 初始化分配器的剩余部分 mheap_.init(spansStart, spansSize) _g_ := getg() _g_.m.mcache = allocmcache()}