Skip to content

Commit 160e5b1

Browse files
committed
runtime/mem: revise for go1.12
Update #3
1 parent 258f637 commit 160e5b1

25 files changed

+2380
-1249
lines changed

gosrc/runtime/malloc.go

+109-41
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ package runtime
8181

8282
import (
8383
"runtime/internal/atomic"
84+
"runtime/internal/math"
8485
"runtime/internal/sys"
8586
"unsafe"
8687
)
@@ -99,8 +100,6 @@ const (
99100
// have the most objects per span.
100101
maxObjsPerSpan = pageSize / 8
101102

102-
mSpanInUse = _MSpanInUse
103-
104103
concurrentSweep = _ConcurrentSweep
105104

106105
_PageSize = 1 << _PageShift // 8KB
@@ -113,8 +112,7 @@ const (
113112
_TinySize = 16
114113
_TinySizeClass = int8(2)
115114

116-
_FixAllocChunk = 16 << 10 // FixAlloc 一个 Chunk 的大小
117-
_MaxMHeapList = 1 << (20 - _PageShift) // Maximum page length for fixed-size list in MHeap.
115+
_FixAllocChunk = 16 << 10 // FixAlloc 一个 Chunk 的大小
118116

119117
// Per-P, 每个 order 对应栈所分割的缓存大小
120118
_StackCacheSize = 32 * 1024
@@ -134,7 +132,7 @@ const (
134132
// amd64, addresses are sign-extended beyond heapAddrBits. On
135133
// other arches, they are zero-extended.
136134
//
137-
// On 64-bit platforms, we limit this to 48 bits based on a
135+
// On most 64-bit platforms, we limit this to 48 bits based on a
138136
// combination of hardware and OS limitations.
139137
//
140138
// amd64 hardware limits addresses to 48 bits, sign-extended
@@ -152,10 +150,9 @@ const (
152150
// bits, in the range [0, 1<<48).
153151
//
154152
// ppc64, mips64, and s390x support arbitrary 64 bit addresses
155-
// in hardware. However, since Go only supports Linux on
156-
// these, we lean on OS limits. Based on Linux's processor.h,
157-
// the user address space is limited as follows on 64-bit
158-
// architectures:
153+
// in hardware. On Linux, Go leans on stricter OS limits. Based
154+
// on Linux's processor.h, the user address space is limited as
155+
// follows on 64-bit architectures:
159156
//
160157
// Architecture Name Maximum Value (exclusive)
161158
// ---------------------------------------------------------------------
@@ -172,13 +169,17 @@ const (
172169
// exceed Go's 48 bit limit, it's extremely unlikely in
173170
// practice.
174171
//
172+
// On aix/ppc64, the limits is increased to 1<<60 to accept addresses
173+
// returned by mmap syscall. These are in range:
174+
// 0x0a00000000000000 - 0x0afffffffffffff
175+
//
175176
// On 32-bit platforms, we accept the full 32-bit address
176177
// space because doing so is cheap.
177178
// mips32 only has access to the low 2GB of virtual memory, so
178179
// we further limit it to 31 bits.
179180
//
180181
// WebAssembly currently has a limit of 4GB linear memory.
181-
heapAddrBits = (_64bit*(1-sys.GoarchWasm))*48 + (1-_64bit+sys.GoarchWasm)*(32-(sys.GoarchMips+sys.GoarchMipsle))
182+
heapAddrBits = (_64bit*(1-sys.GoarchWasm)*(1-sys.GoosAix))*48 + (1-_64bit+sys.GoarchWasm)*(32-(sys.GoarchMips+sys.GoarchMipsle)) + 60*sys.GoosAix
182183

183184
// maxAlloc is the maximum size of an allocation. On 64-bit,
184185
// it's theoretically possible to allocate 1<<heapAddrBits bytes. On
@@ -190,16 +191,17 @@ const (
190191
// The number of bits in a heap address, the size of heap
191192
// arenas, and the L1 and L2 arena map sizes are related by
192193
//
193-
// (1 << addrBits) = arenaBytes * L1entries * L2entries
194+
// (1 << addr bits) = arena size * L1 entries * L2 entries
194195
//
195196
// Currently, we balance these as follows:
196197
//
197-
// Platform Addr bits Arena size L1 entries L2 size
198-
// -------------- --------- ---------- ---------- -------
199-
// */64-bit 48 64MB 1 32MB
200-
// windows/64-bit 48 4MB 64 8MB
201-
// */32-bit 32 4MB 1 4KB
202-
// */mips(le) 31 4MB 1 2KB
198+
// Platform Addr bits Arena size L1 entries L2 entries
199+
// -------------- --------- ---------- ---------- -----------
200+
// */64-bit 48 64MB 1 4M (32MB)
201+
// aix/64-bit 60 256MB 4096 4M (32MB)
202+
// windows/64-bit 48 4MB 64 1M (8MB)
203+
// */32-bit 32 4MB 1 1024 (4KB)
204+
// */mips(le) 31 4MB 1 512 (2KB)
203205

204206
// heapArenaBytes 为 heap arena 的大小。
205207
// heap 由大小为 heapArenaBytes 的映射构成,与 heapArenaBytes 对齐。
@@ -219,7 +221,7 @@ const (
219221
// logHeapArenaBytes is log_2 of heapArenaBytes. For clarity,
220222
// prefer using heapArenaBytes where possible (we need the
221223
// constant to compute some other constants).
222-
logHeapArenaBytes = (6+20)*(_64bit*(1-sys.GoosWindows)) + (2+20)*(_64bit*sys.GoosWindows) + (2+20)*(1-_64bit)
224+
logHeapArenaBytes = (6+20)*(_64bit*(1-sys.GoosWindows)*(1-sys.GoosAix)) + (2+20)*(_64bit*sys.GoosWindows) + (2+20)*(1-_64bit) + (8+20)*sys.GoosAix
223225

224226
// heapArenaBitmapBytes 为每个 heap arena 的 bitmap 的大小
225227
heapArenaBitmapBytes = heapArenaBytes / (sys.PtrSize * 8 / 2)
@@ -239,8 +241,10 @@ const (
239241
// We use the L1 map on 64-bit Windows because the arena size
240242
// is small, but the address space is still 48 bits, and
241243
// there's a high cost to having a large L2.
242-
arenaL1Bits = 6 * (_64bit * sys.GoosWindows)
243-
244+
//
245+
// We use the L1 map on aix/ppc64 to keep the same L2 value
246+
// as on Linux.
247+
arenaL1Bits = 6*(_64bit*sys.GoosWindows) + 12*sys.GoosAix
244248
// arenaL2Bits is the number of bits of the arena number
245249
// covered by the second level arena index.
246250
//
@@ -296,27 +300,27 @@ var physPageSize uintptr
296300
// 注意: sysAlloc 返回的是操作系统排布的内存,但堆分配器可能使用更大的布局。
297301
// 因此调用方必须谨慎的重排从 sysAlloc 获得的内存。
298302
//
299-
// SysUnused notifies the operating system that the contents
303+
// sysUnused notifies the operating system that the contents
300304
// of the memory region are no longer needed and can be reused
301305
// for other purposes.
302-
// SysUsed notifies the operating system that the contents
306+
// sysUsed notifies the operating system that the contents
303307
// of the memory region are needed again.
304308
//
305-
// SysFree returns it unconditionally; this is only used if
309+
// sysFree returns it unconditionally; this is only used if
306310
// an out-of-memory error has been detected midway through
307-
// an allocation. It is okay if SysFree is a no-op.
311+
// an allocation. It is okay if sysFree is a no-op.
308312
//
309-
// SysReserve reserves address space without allocating memory.
313+
// sysReserve reserves address space without allocating memory.
310314
// If the pointer passed to it is non-nil, the caller wants the
311-
// reservation there, but SysReserve can still choose another
315+
// reservation there, but sysReserve can still choose another
312316
// location if that one is unavailable.
313-
// NOTE: SysReserve returns OS-aligned memory, but the heap allocator
317+
// NOTE: sysReserve returns OS-aligned memory, but the heap allocator
314318
// may use larger alignment, so the caller must be careful to realign the
315319
// memory obtained by sysAlloc.
316320
//
317-
// SysMap maps previously reserved address space for use.
321+
// sysMap maps previously reserved address space for use.
318322
//
319-
// SysFault marks a (already sysAlloc'd) region to fault
323+
// sysFault marks a (already sysAlloc'd) region to fault
320324
// if accessed. Used only for debugging the runtime.
321325

322326
func mallocinit() {
@@ -379,6 +383,8 @@ func mallocinit() {
379383
// 但是,在 arm64 中,当使用 4K 大小具有三级的转换缓冲区的页(page)时,用户地址空间
380384
// 被限制在了 39 bit,因此我们忽略了上面所有的建议并强制分配在 0x40 << 32 上。
381385
// 在 darwin/arm64 中,地址空间甚至更小。
386+
// On AIX, mmaps starts at 0x0A00000000000000 for 64-bit.
387+
// processes.
382388
//
383389
// 从 0xc000000000 开始设置保留地址
384390
// 如果失败,则尝试 0x1c000000000 ~ 0x7fc000000000
@@ -389,6 +395,13 @@ func mallocinit() {
389395
p = uintptr(i)<<40 | uintptrMask&(0x0013<<28)
390396
case GOARCH == "arm64":
391397
p = uintptr(i)<<40 | uintptrMask&(0x0040<<32)
398+
case GOOS == "aix":
399+
if i == 0 {
400+
// We don't use addresses directly after 0x0A00000000000000
401+
// to avoid collisions with others mmaps done by non-go programs.
402+
continue
403+
}
404+
p = uintptr(i)<<40 | uintptrMask&(0xa0<<52)
392405
case raceenabled:
393406
// TSAN 运行时需要堆地址在 [0x00c000000000, 0x00e000000000) 范围内
394407
p = uintptr(i)<<32 | uintptrMask&(0x00c0<<32)
@@ -420,7 +433,7 @@ func mallocinit() {
420433
// 3. We try to stake out a reasonably large initial
421434
// heap reservation.
422435

423-
const arenaMetaSize = unsafe.Sizeof([1 << arenaBits]heapArena{})
436+
const arenaMetaSize = (1 << arenaBits) * unsafe.Sizeof(heapArena{})
424437
meta := uintptr(sysReserve(nil, arenaMetaSize))
425438
if meta != 0 {
426439
mheap_.heapArenaAlloc.init(meta, arenaMetaSize)
@@ -603,6 +616,27 @@ mapped:
603616
}
604617
}
605618

619+
// Add the arena to the arenas list.
620+
if len(h.allArenas) == cap(h.allArenas) {
621+
size := 2 * uintptr(cap(h.allArenas)) * sys.PtrSize
622+
if size == 0 {
623+
size = physPageSize
624+
}
625+
newArray := (*notInHeap)(persistentalloc(size, sys.PtrSize, &memstats.gc_sys))
626+
if newArray == nil {
627+
throw("out of memory allocating allArenas")
628+
}
629+
oldSlice := h.allArenas
630+
*(*notInHeapSlice)(unsafe.Pointer(&h.allArenas)) = notInHeapSlice{newArray, len(h.allArenas), int(size / sys.PtrSize)}
631+
copy(h.allArenas, oldSlice)
632+
// Do not free the old backing array because
633+
// there may be concurrent readers. Since we
634+
// double the array each time, this can lead
635+
// to at most 2x waste.
636+
}
637+
h.allArenas = h.allArenas[:len(h.allArenas)+1]
638+
h.allArenas[len(h.allArenas)-1] = ri
639+
606640
// Store atomically just in case an object from the
607641
// new heap arena becomes visible before the heap lock
608642
// is released (which shouldn't happen, but there's
@@ -695,6 +729,9 @@ func nextFreeFast(s *mspan) gclinkptr {
695729
// weight allocation. If it is a heavy weight allocation the caller must
696730
// determine whether a new GC cycle needs to be started or if the GC is active
697731
// whether this goroutine needs to assist the GC.
732+
//
733+
// Must run in a non-preemptible context since otherwise the owner of
734+
// c could change.
698735
func (c *mcache) nextFree(spc spanClass) (v gclinkptr, s *mspan, shouldhelpgc bool) {
699736
s = c.alloc[spc]
700737
shouldhelpgc = false
@@ -705,9 +742,7 @@ func (c *mcache) nextFree(spc spanClass) (v gclinkptr, s *mspan, shouldhelpgc bo
705742
println("runtime: s.allocCount=", s.allocCount, "s.nelems=", s.nelems)
706743
throw("s.allocCount != s.nelems && freeIndex == s.nelems")
707744
}
708-
systemstack(func() {
709-
c.refill(spc)
710-
})
745+
c.refill(spc)
711746
shouldhelpgc = true
712747
s = c.alloc[spc]
713748

@@ -929,7 +964,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
929964
}
930965

931966
if rate := MemProfileRate; rate > 0 {
932-
if size < uintptr(rate) && int32(size) < c.next_sample {
967+
if rate != 1 && int32(size) < c.next_sample {
933968
c.next_sample -= int32(size)
934969
} else {
935970
mp := acquirem()
@@ -946,7 +981,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
946981

947982
if shouldhelpgc {
948983
if t := (gcTrigger{kind: gcTriggerHeap}); t.test() {
949-
gcStart(gcBackgroundMode, t)
984+
gcStart(t)
950985
}
951986
}
952987

@@ -993,10 +1028,11 @@ func newarray(typ *_type, n int) unsafe.Pointer {
9931028
if n == 1 {
9941029
return mallocgc(typ.size, typ, true)
9951030
}
996-
if n < 0 || uintptr(n) > maxSliceCap(typ.size) {
1031+
mem, overflow := math.MulUintptr(typ.size, uintptr(n))
1032+
if overflow || mem > maxAlloc || n < 0 {
9971033
panic(plainError("runtime: allocation size out of range"))
9981034
}
999-
return mallocgc(typ.size*uintptr(n), typ, true)
1035+
return mallocgc(mem, typ, true)
10001036
}
10011037

10021038
//go:linkname reflect_unsafe_NewArray reflect.unsafe_NewArray
@@ -1078,6 +1114,15 @@ var globalAlloc struct {
10781114
persistentAlloc
10791115
}
10801116

1117+
// persistentChunkSize is the number of bytes we allocate when we grow
1118+
// a persistentAlloc.
1119+
const persistentChunkSize = 256 << 10
1120+
1121+
// persistentChunks is a list of all the persistent chunks we have
1122+
// allocated. The list is maintained through the first word in the
1123+
// persistent chunk. This is updated atomically.
1124+
var persistentChunks *notInHeap
1125+
10811126
// sysAlloc 的一层封装,能够分配小的 chunk
10821127
// 没有释放操作
10831128
// 用于 函数、类型、调试相关的持久型数据分配。
@@ -1097,7 +1142,6 @@ func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer {
10971142
//go:systemstack
10981143
func persistentalloc1(size, align uintptr, sysStat *uint64) *notInHeap {
10991144
const (
1100-
chunk = 256 << 10
11011145
maxBlock = 64 << 10 // VM reservation granularity is 64K on windows
11021146
)
11031147

@@ -1135,15 +1179,24 @@ func persistentalloc1(size, align uintptr, sysStat *uint64) *notInHeap {
11351179
}
11361180
// 四舍五入 off 到 align 的倍数
11371181
persistent.off = round(persistent.off, align)
1138-
if persistent.off+size > chunk || persistent.base == nil {
1139-
persistent.base = (*notInHeap)(sysAlloc(chunk, &memstats.other_sys))
1182+
if persistent.off+size > persistentChunkSize || persistent.base == nil {
1183+
persistent.base = (*notInHeap)(sysAlloc(persistentChunkSize, &memstats.other_sys))
11401184
if persistent.base == nil {
11411185
if persistent == &globalAlloc.persistentAlloc {
11421186
unlock(&globalAlloc.mutex)
11431187
}
11441188
throw("runtime: cannot allocate memory")
11451189
}
1146-
persistent.off = 0
1190+
1191+
// Add the new chunk to the persistentChunks list.
1192+
for {
1193+
chunks := uintptr(unsafe.Pointer(persistentChunks))
1194+
*(*uintptr)(unsafe.Pointer(persistent.base)) = chunks
1195+
if atomic.Casuintptr((*uintptr)(unsafe.Pointer(&persistentChunks)), chunks, uintptr(unsafe.Pointer(persistent.base))) {
1196+
break
1197+
}
1198+
}
1199+
persistent.off = sys.PtrSize
11471200
}
11481201
p := persistent.base.add(persistent.off)
11491202
persistent.off += size
@@ -1159,6 +1212,21 @@ func persistentalloc1(size, align uintptr, sysStat *uint64) *notInHeap {
11591212
return p
11601213
}
11611214

1215+
// inPersistentAlloc reports whether p points to memory allocated by
1216+
// persistentalloc. This must be nosplit because it is called by the
1217+
// cgo checker code, which is called by the write barrier code.
1218+
//go:nosplit
1219+
func inPersistentAlloc(p uintptr) bool {
1220+
chunk := atomic.Loaduintptr((*uintptr)(unsafe.Pointer(&persistentChunks)))
1221+
for chunk != 0 {
1222+
if p >= chunk && p < chunk+persistentChunkSize {
1223+
return true
1224+
}
1225+
chunk = *(*uintptr)(unsafe.Pointer(chunk))
1226+
}
1227+
return false
1228+
}
1229+
11621230
// linearAlloc 是一个简单的线性分配器,提前储备了内存的一块区域并在需要时指向该区域。
11631231
// 调用方负责加锁。
11641232
type linearAlloc struct {

0 commit comments

Comments
 (0)