1. func mallocinit() {
    2. if class_to_size[_TinySizeClass] != _TinySize {
    3. throw("bad TinySizeClass")
    4. }
    5. testdefersizes()
    6. // 检查物理页大小
    7. if physPageSize == 0 {
    8. // 操作系统初始化代码获取物理页大小失败
    9. throw("failed to get system page size")
    10. }
    11. if physPageSize < minPhysPageSize {
    12. print("system page size (", physPageSize, ") is smaller than minimum page size (", minPhysPageSize, ")\n")
    13. throw("bad system page size")
    14. }
    15. if physPageSize&(physPageSize-1) != 0 {
    16. print("system page size (", physPageSize, ") must be a power of 2\n")
    17. throw("bad system page size")
    18. }
    19. // 内存区域从 p 开始,在内存中以: span, bitmap, arena 的顺序排列
    20. var p, pSize uintptr
    21. var reserved bool
    22. // span 数组中,对每一块 _PageSize 的 arena 内存都持有一个 *mspan
    23. var spansSize uintptr = (_MaxMem + 1) / _PageSize * sys.PtrSize
    24. spansSize = round(spansSize, _PageSize)
    25. // 每一个 word 的 arena 区域都在 bitmap 中占用 2 bits
    26. var bitmapSize uintptr = (_MaxMem + 1) / (sys.PtrSize * 8 / 2)
    27. bitmapSize = round(bitmapSize, _PageSize)
    28. // 初始化内存分配 arena,arena 是一段连续的内存,负责数据的内存分配。
    29. if sys.PtrSize == 8 {
    30. // 在 64 位机器上,分配一段连续的内存区域 512 GB(MaxMem) 现在应该是足够了。
    31. //
    32. // 代码应该能够用任意地址进行工作,但可能的情况下尽量让 sysReserve 使用 0x0000XXc000000000 (xx=00...7f)。
    33. // 分配 512 GB 的地址需要用掉 39 bits 来进行地址表达,amd64 平台不允许用户使用高位 17 bits
    34. // 所以只剩下中间的 0x00c0 中的 9 bits 能让我们用了。选择 0x00c0 表示合法的内存地址是从
    35. // 0x00c0, 0x00c1, ..., 0x00df 开始
    36. // 在小端系统上,即 c0 00, c1 00, ..., df 00。这些都不是合法的 UTF-8 序列,且距离 ff(常用的单字节) 足够远。
    37. // 如果分配失败,会尝试其它 0xXXc0 地址。
    38. // 之前使用 0x11f8 地址在 OS X 系统上会在线程分配的时候导致 out of memory 错误。
    39. // 0x00c0 会导致和 AddressSanitizer 的冲突,AddressSanitizer 会将 0x0100 以下的地址都进行保留
    40. // 这种选择是为了可调试性,且可以限制垃圾收集器(gccgo 中的版本)的保守性,以使其不要收集那些符合一定模式的内存地址中的内存。
    41. //
    42. // 实际上我们会保留 544 GB(因为 bitmap 需要用 32 GB)
    43. // 不过这不重要: e0 00 也不是合法的 UTF-* 字符
    44. //
    45. // 如果失败,会回退到 32 位内存策略
    46. //
    47. // 然而在 arm64 平台我们会忽略上面所有的建议,直接梭哈分配到 0x40 << 32,因为
    48. // 使用 4k 页搭配 3级 TLB 时,用户地址空间会被限制在 39 位能表达的范围之内,
    49. // 在 darwin/arm64 上,地址空间就更小了。
    50. arenaSize := round(_MaxMem, _PageSize)
    51. pSize = bitmapSize + spansSize + arenaSize + _PageSize
    52. for i := 0; i <= 0x7f; i++ {
    53. switch {
    54. case GOARCH == "arm64" && GOOS == "darwin":
    55. p = uintptr(i)<<40 | uintptrMask&(0x0013<<28)
    56. case GOARCH == "arm64":
    57. p = uintptr(i)<<40 | uintptrMask&(0x0040<<32)
    58. default:
    59. p = uintptr(i)<<40 | uintptrMask&(0x00c0<<32)
    60. }
    61. p = uintptr(sysReserve(unsafe.Pointer(p), pSize, &reserved))
    62. if p != 0 {
    63. break
    64. }
    65. }
    66. }
    67. if p == 0 {
    68. // 在 32 位机器上,我们没法简单粗暴地获得一段巨大的虚拟地址空间,并保留内存。
    69. // 取而代之,我们将内存信息的 bitmap 紧跟在 data segment 之后,
    70. // 这样做足够处理整个 4GB 的内存空间了(256 MB 的 bitmap 消耗)
    71. // 初始化阶段会保留一小段地址
    72. // 用完之后,我们再和 kernel 申请其它位置的内存。
    73. // 我们想要让 arena 区域从低地址开始,但是我们的代码可能和 C 代码进行链接,
    74. // 全局的构造器可能已经调用过 malloc,并且调整过进程的 brk 位置。
    75. // 所以需要查询一次 brk,以避免将我们的 arena 区域覆盖掉 brk 位置,
    76. // 这会导致 kernel 把 arena 放在其它地方,比如放在高地址。
    77. procBrk := sbrk0()
    78. // 如果分配失败,那么尝试用更小一些的 arena 区域。
    79. // 对于像 Android L 这样的系统是需要的,因为我们和 ART 更新同一个进程,
    80. // 其会更激进地保留内存。
    81. // 最差的情况下,会退化为 0 大小的初始 arena
    82. // 这种情况下希望之后紧跟着的内存保留操作能够成功。
    83. arenaSizes := []uintptr{
    84. 512 << 20,
    85. 256 << 20,
    86. 128 << 20,
    87. 0,
    88. }
    89. for _, arenaSize := range arenaSizes {
    90. // sysReserve 会把我们要求保留的地址的末尾作为一种 hint,而不一定会满足
    91. // 这种情况下需要我们自己对指针进行 roundup,先是 1/4 MB,以使其离开运行的二进制
    92. // 镜像位置,然后在 roundup 到 MB 的边界位置
    93. p = round(firstmoduledata.end+(1<<18), 1<<20)
    94. pSize = bitmapSize + spansSize + arenaSize + _PageSize
    95. if p <= procBrk && procBrk < p+pSize {
    96. // 将 start 移动到 brk 之上,给未来的 brk 扩展保留一些空间
    97. p = round(procBrk+(1<<20), 1<<20)
    98. }
    99. p = uintptr(sysReserve(unsafe.Pointer(p), pSize, &reserved))
    100. if p != 0 {
    101. break
    102. }
    103. }
    104. if p == 0 {
    105. throw("runtime: cannot reserve arena virtual address space")
    106. }
    107. }
    108. // PageSize 可能被 OS 定义的 page size 更大
    109. // 所以 sysReserve 会返回给我们一个未 PageSize-对齐的指针。
    110. // 我们需要对其进行 round up,以使其按我们的 PageSize 要求对齐
    111. p1 := round(p, _PageSize)
    112. pSize -= p1 - p
    113. spansStart := p1
    114. p1 += spansSize
    115. mheap_.bitmap = p1 + bitmapSize
    116. p1 += bitmapSize
    117. if sys.PtrSize == 4 {
    118. // 赋值 arena_start 这样我们相当于接受了 4GB 虚拟空间中的保留内存
    119. mheap_.arena_start = 0
    120. } else {
    121. mheap_.arena_start = p1
    122. }
    123. mheap_.arena_end = p + pSize
    124. mheap_.arena_used = p1
    125. mheap_.arena_alloc = p1
    126. mheap_.arena_reserved = reserved
    127. if mheap_.arena_start&(_PageSize-1) != 0 {
    128. println("bad pagesize", hex(p), hex(p1), hex(spansSize), hex(bitmapSize), hex(_PageSize), "start", hex(mheap_.arena_start))
    129. throw("misrounded allocation in mallocinit")
    130. }
    131. // 初始化分配器的剩余部分
    132. mheap_.init(spansStart, spansSize)
    133. _g_ := getg()
    134. _g_.m.mcache = allocmcache()
    135. }