diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index 5f1e2f64c01..fe13b8b9a3a 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -97,6 +97,9 @@ const ( pageShift = _PageShift pageSize = _PageSize pageMask = _PageMask + // By construction, single page spans of the smallest object class + // have the most objects per span. + maxObjsPerSpan = pageSize / 8 mSpanInUse = _MSpanInUse diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index 895af9f07c2..8c843be9464 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -117,9 +117,41 @@ type mspan struct { prev **mspan // previous span's next field, or list head's first field if none list *mSpanList // For debugging. TODO: Remove. - start pageID // starting page number - npages uintptr // number of pages in span - freelist gclinkptr // list of free objects + start pageID // starting page number + npages uintptr // number of pages in span + freelist gclinkptr // list of free objects for _MSpanInUse + stackfreelist gclinkptr // list of free stacks, avoids overloading freelist for _MSpanStack + + // freeindex is the slot index between 0 and nelems at which to begin scanning + // for the next free object in this span. + // Each allocation scans allocBits starting at freeindex until it encounters a 0 + // indicating a free object. freeindex is then adjusted so that subsequent scans begin + // just past the the newly discovered free object. + // + // If freeindex == nelem, this span has no free objects. + // + // allocBits is a bitmap of objects in this span. + // If n >= freeindex and allocBits[n/8] & (1<<(n%8)) is 0 + // then object n is free; + // otherwise, object n is allocated. Bits starting at nelem are + // undefined and should never be referenced. + // + // Object n starts at address n*elemsize + (start << pageShift). + freeindex uintptr + allocBits *[maxObjsPerSpan / 8]uint8 + gcmarkBits *[maxObjsPerSpan / 8]uint8 + nelems uintptr // number of object in the span. + // TODO(rlh) consider moving some of these fields into seperate arrays. + // Put another way is an array of structs a better idea than a struct of arrays. + + // allocBits and gcmarkBits currently point to either markbits1 + // or markbits2. At the end of a GC cycle allocBits and + // gcmarkBits swap roles simply by swapping pointers. + // This level of indirection also facilitates an implementation + // where markbits1 and markbits2 are not inlined in mspan. + markbits1 [maxObjsPerSpan / 8]uint8 // A bit for each obj. + markbits2 [maxObjsPerSpan / 8]uint8 // A bit for each obj. + // sweep generation: // if sweepgen == h->sweepgen - 2, the span needs sweeping // if sweepgen == h->sweepgen - 1, the span is currently being swept diff --git a/src/runtime/msize.go b/src/runtime/msize.go index 21fe2f4c615..18577b309bd 100644 --- a/src/runtime/msize.go +++ b/src/runtime/msize.go @@ -55,7 +55,7 @@ var size_to_class128 [(_MaxSmallSize-1024)/128 + 1]int8 func sizeToClass(size int32) int32 { if size > _MaxSmallSize { - throw("SizeToClass - invalid size") + throw("invalid size") } if size > 1024-8 { return int32(size_to_class128[(size-1024+127)>>7]) @@ -79,7 +79,7 @@ func initSizes() { } } if align&(align-1) != 0 { - throw("InitSizes - bug") + throw("incorrect alignment") } // Make the allocnpages big enough that @@ -106,10 +106,18 @@ func initSizes() { sizeclass++ } if sizeclass != _NumSizeClasses { - print("sizeclass=", sizeclass, " NumSizeClasses=", _NumSizeClasses, "\n") - throw("InitSizes - bad NumSizeClasses") + print("runtime: sizeclass=", sizeclass, " NumSizeClasses=", _NumSizeClasses, "\n") + throw("bad NumSizeClasses") + } + // Check maxObjsPerSpan => number of objects invariant. + for i, size := range class_to_size { + if size != 0 && class_to_allocnpages[i]*pageSize/size > maxObjsPerSpan { + throw("span contains too many objects") + } + if size == 0 && i != 0 { + throw("size is 0 but class is not 0") + } } - // Initialize the size_to_class tables. nextsize := 0 for sizeclass = 1; sizeclass < _NumSizeClasses; sizeclass++ { @@ -128,12 +136,12 @@ func initSizes() { for n := int32(0); n < _MaxSmallSize; n++ { sizeclass := sizeToClass(n) if sizeclass < 1 || sizeclass >= _NumSizeClasses || class_to_size[sizeclass] < n { - print("size=", n, " sizeclass=", sizeclass, " runtime·class_to_size=", class_to_size[sizeclass], "\n") + print("runtime: size=", n, " sizeclass=", sizeclass, " runtime·class_to_size=", class_to_size[sizeclass], "\n") print("incorrect SizeToClass\n") goto dump } if sizeclass > 1 && class_to_size[sizeclass-1] >= n { - print("size=", n, " sizeclass=", sizeclass, " runtime·class_to_size=", class_to_size[sizeclass], "\n") + print("runtime: size=", n, " sizeclass=", sizeclass, " runtime·class_to_size=", class_to_size[sizeclass], "\n") print("SizeToClass too big\n") goto dump } @@ -155,18 +163,18 @@ func initSizes() { dump: if true { - print("NumSizeClasses=", _NumSizeClasses, "\n") + print("runtime: NumSizeClasses=", _NumSizeClasses, "\n") print("runtime·class_to_size:") for sizeclass = 0; sizeclass < _NumSizeClasses; sizeclass++ { print(" ", class_to_size[sizeclass], "") } print("\n\n") - print("size_to_class8:") + print("runtime: size_to_class8:") for i := 0; i < len(size_to_class8); i++ { print(" ", i*8, "=>", size_to_class8[i], "(", class_to_size[size_to_class8[i]], ")\n") } print("\n") - print("size_to_class128:") + print("runtime: size_to_class128:") for i := 0; i < len(size_to_class128); i++ { print(" ", i*128, "=>", size_to_class128[i], "(", class_to_size[size_to_class128[i]], ")\n") }