@@ -8,15 +8,15 @@ package runtime
88// The memory manager internally uses blocks of 4 pointers big (see
99// bytesPerBlock). Every allocation first rounds up to this size to align every
1010// block. It will first try to find a chain of blocks that is big enough to
11- // satisfy the allocation. If it finds one, it marks the first one as the "head"
12- // and the following ones (if any) as the "tail" (see below). If it cannot find
11+ // satisfy the allocation. If it finds one, it marks the last one as the "head"
12+ // and the preceding ones (if any) as the "tail" (see below). If it cannot find
1313// any free space, it will perform a garbage collection cycle and try again. If
1414// it still cannot find any free space, it gives up.
1515//
1616// Every block has some metadata, which is stored at the end of the heap.
1717// The four states are "free", "head", "tail", and "mark". During normal
18- // operation, there are no marked blocks. Every allocated object starts with a
19- // "head" and is followed by "tail" blocks. The reason for this distinction is
18+ // operation, there are no marked blocks. Every allocated object ends with a
19+ // "head" and is preceded by "tail" blocks. The reason for this distinction is
2020// that this way, the start and end of every object can be found easily.
2121//
2222// Metadata is stored in a special area at the end of the heap, in the area
@@ -129,7 +129,7 @@ func (b gcBlock) address() uintptr {
129129 return addr
130130}
131131
132- // findHead returns the head (first block) of an object, assuming the block
132+ // findHead returns the head (last block) of an object, assuming the block
133133// points to an allocated object. It returns the same block if this block
134134// already points to the head.
135135func (b gcBlock ) findHead () gcBlock {
@@ -142,7 +142,7 @@ func (b gcBlock) findHead() gcBlock {
142142 // large allocation.
143143 stateByte := b .stateByte ()
144144 if stateByte == blockStateByteAllTails {
145- b -= (b % blocksPerStateByte ) + 1
145+ b += blocksPerStateByte - (b % blocksPerStateByte )
146146 continue
147147 }
148148
@@ -152,7 +152,7 @@ func (b gcBlock) findHead() gcBlock {
152152 if state != blockStateTail {
153153 break
154154 }
155- b --
155+ b ++
156156 }
157157 if gcAsserts {
158158 if b .state () != blockStateHead && b .state () != blockStateMark {
@@ -162,18 +162,6 @@ func (b gcBlock) findHead() gcBlock {
162162 return b
163163}
164164
165- // findNext returns the first block just past the end of the tail. This may or
166- // may not be the head of an object.
167- func (b gcBlock ) findNext () gcBlock {
168- if b .state () == blockStateHead || b .state () == blockStateMark {
169- b ++
170- }
171- for b .address () < uintptr (metadataStart ) && b .state () == blockStateTail {
172- b ++
173- }
174- return b
175- }
176-
177165func (b gcBlock ) stateByte () byte {
178166 return * (* uint8 )(unsafe .Add (metadataStart , b / blocksPerStateByte ))
179167}
@@ -200,7 +188,22 @@ func (b gcBlock) setState(newState blockState) {
200188 }
201189}
202190
203- // objHeader is a structure prepended to every heap object to hold metadata.
191+ // unmark changes the state of b from blockStateMark to blockStateHead.
192+ func (b gcBlock ) unmark () {
193+ if gcAsserts && b .state () != blockStateMark {
194+ runtimePanic ("gc: block not marked" )
195+ }
196+ stateBytePtr := (* uint8 )(unsafe .Add (metadataStart , b / blocksPerStateByte ))
197+ * stateBytePtr ^= uint8 (blockStateMark ^ blockStateHead ) << (b % blocksPerStateByte )
198+ }
199+
200+ // free changes the state of b to blockStateFree.
201+ func (b gcBlock ) free () {
202+ stateBytePtr := (* uint8 )(unsafe .Add (metadataStart , b / blocksPerStateByte ))
203+ * stateBytePtr &^= uint8 (blockStateMask ) << (b % blocksPerStateByte )
204+ }
205+
206+ // objHeader is a structure appended to every heap object to hold metadata.
204207type objHeader struct {
205208 // next is the next object to scan after this.
206209 next * objHeader
@@ -317,8 +320,12 @@ func initHeap() {
317320 metadataSize := heapEnd - uintptr (metadataStart )
318321 memzero (unsafe .Pointer (metadataStart ), metadataSize )
319322
320- // Rebuild the free ranges list.
321- buildFreeRanges ()
323+ // Create the initial free range.
324+ if endBlock > 0 {
325+ r := (* freeRange )(unsafe .Pointer (heapStart ))
326+ * r = freeRange {len : uintptr (endBlock )}
327+ freeRanges = r
328+ }
322329}
323330
324331// setHeapEnd is called to expand the heap. The heap can only grow, not shrink.
@@ -340,6 +347,7 @@ func setHeapEnd(newHeapEnd uintptr) {
340347 // memcpy is fine as it only copies the old metadata and the new memory will
341348 // have been zero initialized.
342349 heapEnd = newHeapEnd
350+ oldEndBlock := endBlock
343351 calculateHeapAddresses ()
344352 memcpy (metadataStart , oldMetadataStart , oldMetadataSize )
345353
@@ -351,8 +359,14 @@ func setHeapEnd(newHeapEnd uintptr) {
351359 runtimePanic ("gc: heap did not grow enough at once" )
352360 }
353361
354- // Rebuild the free ranges list.
355- buildFreeRanges ()
362+ // Insert the new free range. This range will be separate from any previous
363+ // free space at the end of the heap. This may result in more heap growth
364+ // than strictly necessary when an allocation requests more memory than the
365+ // previous heap size. Otherwise this will only result in slightly more
366+ // memory fragmentation than necessary. We cannot easily remove the old
367+ // range and adding a special free-list rebuild function for this edge case
368+ // would not be worthwhile in terms of binary size or code maintenance.
369+ insertFreeRange (oldEndBlock .pointer (), uintptr (endBlock - oldEndBlock ))
356370}
357371
358372// calculateHeapAddresses initializes variables such as metadataStart and
@@ -400,7 +414,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
400414
401415 // Round the size up to a multiple of blocks, adding space for the header.
402416 rawSize := size
403- size += align ( unsafe .Sizeof (objHeader {}) )
417+ size += unsafe .Sizeof (objHeader {})
404418 size += bytesPerBlock - 1
405419 if size < rawSize {
406420 // The size overflowed.
@@ -456,25 +470,27 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
456470 runtimePanicAt (returnAddress (0 ), "out of memory" )
457471 }
458472
459- // Set the backing blocks as being allocated .
473+ // Set the block states .
460474 block := blockFromAddr (uintptr (pointer ))
461- block .setState (blockStateHead )
462- for i := block + 1 ; i != block + gcBlock (neededBlocks ); i ++ {
475+ i := block + gcBlock (neededBlocks ) - 1
476+ i .setState (blockStateHead )
477+ for i != block {
478+ i --
463479 i .setState (blockStateTail )
464480 }
465481
466482 // Create the object header.
467- header := (* objHeader )(pointer )
483+ size -= unsafe .Sizeof (objHeader {})
484+ header := (* objHeader )(unsafe .Add (pointer , size ))
468485 header .layout = parseGCLayout (layout )
469486
470487 // We've claimed this allocation, now we can unlock the heap.
471488 gcLock .Unlock ()
472489
473- // Return a pointer to this allocation.
474- add := align (unsafe .Sizeof (objHeader {}))
475- pointer = unsafe .Add (pointer , add )
476- size -= add
490+ // Clear the allocation body.
477491 memzero (pointer , size )
492+
493+ // Return a pointer to this allocation.
478494 return pointer
479495}
480496
@@ -483,16 +499,34 @@ func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
483499 return alloc (size , nil )
484500 }
485501
486- ptrAddress := uintptr (ptr )
487- endOfTailAddress := blockFromAddr (ptrAddress ).findNext ().address ()
502+ // Find the first block of the original allocation.
503+ firstBlock := blockFromAddr (uintptr (ptr ))
504+
505+ // Find the last block of the original allocation.
506+ lastBlock := firstBlock
507+ for lastBlock .state () == blockStateTail {
508+ lastBlock ++
509+ }
510+ if gcAsserts && lastBlock .state () != blockStateHead {
511+ runtimePanic ("gc: realloc of free or corrupted allocation" )
512+ }
513+
514+ // Calculate the size of the original allocation body.
515+ oldSize := uintptr (lastBlock - firstBlock )* blocksPerStateByte + (bytesPerBlock - unsafe .Sizeof (objHeader {}))
488516
489- // this might be a few bytes longer than the original size of
490- // ptr, because we align to full blocks of size bytesPerBlock
491- oldSize := endOfTailAddress - ptrAddress
492517 if size <= oldSize {
518+ // The requested size is less than the old size.
519+ // There are likely scenarios for this:
520+ // - The caller intended to grow the allocation, but the original size
521+ // was rounded up by alloc to a multiple of the block size.
522+ // The rounded size is already sufficient.
523+ // - The caller intended to shrink the allocation.
524+ // We currently ignore this case.
525+ // Either way, the current allocation can be left alone.
493526 return ptr
494527 }
495528
529+ // Create a new allocation and copy the old data.
496530 newAlloc := alloc (size , nil )
497531 memcpy (newAlloc , ptr , oldSize )
498532 free (ptr )
@@ -559,11 +593,8 @@ func runGC() (freeBytes uintptr) {
559593 gcResumeWorld ()
560594
561595 // Sweep phase: free all non-marked objects and unmark marked objects for
562- // the next collection cycle.
563- sweep ()
564-
565- // Rebuild the free ranges list.
566- freeBytes = buildFreeRanges ()
596+ // the next collection cycle. This also rebuilds the free ranges list.
597+ freeBytes = sweep ()
567598
568599 // Show how much has been sweeped, for debugging.
569600 if gcDebug {
@@ -629,13 +660,21 @@ func finishMark() {
629660 continue
630661 }
631662
632- // Compute the scan bounds.
633- objAddr := uintptr (unsafe .Pointer (obj ))
634- start := objAddr + align (unsafe .Sizeof (objHeader {}))
635- end := blockFromAddr (objAddr ).findNext ().address ()
663+ // Find the last block in the object.
664+ // This block contains the header.
665+ lastBlock := blockFromAddr (uintptr (unsafe .Pointer (obj )))
666+
667+ // Find the first block in the allocation.
668+ firstBlock := lastBlock
669+ for firstBlock > 0 && (firstBlock - 1 ).state () == blockStateTail {
670+ firstBlock --
671+ }
672+
673+ // Compute the size of the allocation.
674+ bodySize := uintptr (lastBlock - firstBlock )* bytesPerBlock + (bytesPerBlock - unsafe .Sizeof (objHeader {}))
636675
637676 // Scan the object.
638- obj .layout .scan (start , end - start )
677+ obj .layout .scan (firstBlock . address (), bodySize )
639678 }
640679}
641680
@@ -668,97 +707,55 @@ func markRoot(addr, root uintptr) {
668707 head .setState (blockStateMark )
669708
670709 // Add the object to the scan list.
671- header := (* objHeader )(head .pointer ())
710+ header := (* objHeader )(unsafe . Add ( head .pointer (), bytesPerBlock - unsafe . Sizeof ( objHeader {}) ))
672711 header .next = scanList
673712 scanList = header
674713}
675714
676715// Sweep goes through all memory and frees unmarked memory.
677- func sweep () {
678- metadataEnd := unsafe .Add (metadataStart , (endBlock + (blocksPerStateByte - 1 ))/ blocksPerStateByte )
679- var carry byte
680- for meta := metadataStart ; meta != metadataEnd ; meta = unsafe .Add (meta , 1 ) {
681- // Fetch the state byte.
682- stateBytePtr := (* byte )(unsafe .Pointer (meta ))
683- stateByte := * stateBytePtr
684-
685- // Separate blocks by type.
686- // Split the nibbles.
687- // Each nibble is a mask of blocks.
688- high := stateByte >> blocksPerStateByte
689- low := stateByte & blockStateEach
690- // Marked heads are in both nibbles.
691- markedHeads := low & high
692- // Unmarked heads are in the low nibble but not the high nibble.
693- unmarkedHeads := low &^ high
694- // Tails are in the high nibble but not the low nibble.
695- tails := high &^ low
696-
697- // Clear all tail runs after unmarked (freed) heads.
698- //
699- // Adding 1 to the start of a bit run will clear the run and set the next bit:
700- // (2^k - 1) + 1 = 2^k
701- // e.g. 0b0011 + 1 = 0b0100
702- // Bitwise-and with the original mask to clear the newly set bit.
703- // e.g. (0b0011 + 1) & 0b0011 = 0b0100 & 0b0011 = 0b0000
704- // This will not clear bits after the run because the gap stops the carry:
705- // e.g. (0b1011 + 1) & 0b1011 = 0b1100 & 0b1011 = 0b1000
706- // This can clear multiple runs in a single addition:
707- // e.g. (0b1101 + 0b0101) & 0b1101 = 0b10010 & 0b1101 = 0b0000
708- //
709- // In order to find tail run starts after unmarked heads we could use tails & (unmarkedHeads << 1).
710- // It is possible omit the bitwise-and because the clear still works if the next block is not a tail.
711- // A head is not a tail, so corresponding missing tail bit will stop the carry from a previous tail run.
712- // As such it will set the next bit which will be cleared back away later.
713- // e.g. HHTH: (0b0010 + (0b1101 << 1)) & 0b0010 = 0b11100 & 0b0010 = 0b0000
714- //
715- // Treat the whole heap as a single pair of integer masks.
716- // This is accomplished for addition by carrying the overflow to the next state byte.
717- // The unmarkedHeads << 1 is equivalent to unmarkedHeads + unmarkedHeads, so it can be merged with the sum.
718- // This does not require any special work for the bitwise-and because it operates bitwise.
719- tailClear := tails + (unmarkedHeads << 1 ) + carry
720- carry = tailClear >> blocksPerStateByte
721- tails &= tailClear
722-
723- // Construct the new state byte.
724- * stateBytePtr = markedHeads | (tails << blocksPerStateByte )
725- }
726- }
727-
728- // buildFreeRanges rebuilds the freeRanges list.
729- // This must be called after a GC sweep or heap grow.
730- // It returns how many bytes are free in the heap.
731- func buildFreeRanges () uintptr {
716+ func sweep () uintptr {
717+ // Discard the old free ranges list.
732718 freeRanges = nil
719+
720+ // Scan backwards through the block metadata.
733721 block := endBlock
734- var totalBlocks uintptr
722+ var freeBlocks uintptr
735723 for {
736- // Skip backwards over occupied blocks.
737- for block > 0 && (block - 1 ).state () != blockStateFree {
724+ // Scan backwards until we find a marked head.
725+ // Free the blocks as we go.
726+ freeEnd := block
727+ for block > 0 && (block - 1 ).state () != blockStateMark {
738728 block --
729+ block .free ()
730+ }
731+
732+ if freeLen := uintptr (freeEnd - block ); freeLen > 0 {
733+ // Insert the freed blocks.
734+ freeBlocks += freeLen
735+ insertFreeRange (block .pointer (), freeLen )
739736 }
737+
740738 if block == 0 {
739+ // There are no more blocks to sweep.
741740 break
742741 }
743742
744- // Find the start of the free range.
745- end := block
746- for block > 0 && (block - 1 ).state () == blockStateFree {
743+ // Unmark the next head.
744+ block --
745+ block .unmark ()
746+
747+ // Skip the tail.
748+ for block > 0 && (block - 1 ).state () == blockStateTail {
747749 block --
748750 }
749-
750- // Insert the free range.
751- len := uintptr (end - block )
752- totalBlocks += len
753- insertFreeRange (block .pointer (), len )
754751 }
755752
756753 if gcDebug {
757- println ("free ranges after rebuild :" )
754+ println ("free ranges after sweep :" )
758755 dumpFreeRangeCounts ()
759756 }
760757
761- return totalBlocks * bytesPerBlock
758+ return freeBlocks * bytesPerBlock
762759}
763760
764761func dumpFreeRangeCounts () {
0 commit comments