@@ -46,11 +46,11 @@ const (
4646 bytesPerBlock = wordsPerBlock * unsafe .Sizeof (heapStart )
4747 stateBits = 2 // how many bits a block state takes (see blockState type)
4848 blocksPerStateByte = 8 / stateBits
49- markStackSize = 8 * unsafe .Sizeof ((* int )(nil )) // number of to-be-marked blocks to queue before forcing a rescan
5049)
5150
5251var (
5352 metadataStart unsafe.Pointer // pointer to the start of the heap metadata
53+ scanList * objHeader // scanList is a singly linked list of heap objects that have been marked but not scanned
5454 nextAlloc gcBlock // the next block that should be tried by the allocator
5555 endBlock gcBlock // the block just past the end of the available space
5656 gcTotalAlloc uint64 // total number of bytes allocated
@@ -225,6 +225,15 @@ func (b gcBlock) unmark() {
225225 }
226226}
227227
228+ // objHeader is a structure prepended to every heap object to hold metadata.
229+ type objHeader struct {
230+ // next is the next object to scan after this.
231+ next * objHeader
232+
233+ // layout holds the layout bitmap used to find pointers in the object.
234+ layout gcLayout
235+ }
236+
228237func isOnHeap (ptr uintptr ) bool {
229238 return ptr >= heapStart && ptr < uintptr (metadataStart )
230239}
@@ -315,13 +324,10 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
315324 runtimePanicAt (returnAddress (0 ), "heap alloc in interrupt" )
316325 }
317326
318- // Round the size up to a multiple of blocks.
327+ // Round the size up to a multiple of blocks, adding space for the header .
319328 rawSize := size
329+ size += align (unsafe .Sizeof (objHeader {}))
320330 size += bytesPerBlock - 1
321- if preciseHeap {
322- // Add space for the layout.
323- size += align (unsafe .Sizeof (layout ))
324- }
325331 if size < rawSize {
326332 // The size overflowed.
327333 runtimePanicAt (returnAddress (0 ), "out of memory" )
@@ -414,20 +420,18 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
414420 i .setState (blockStateTail )
415421 }
416422
423+ // Create the object header.
424+ pointer := thisAlloc .pointer ()
425+ header := (* objHeader )(pointer )
426+ header .layout = parseGCLayout (layout )
427+
417428 // We've claimed this allocation, now we can unlock the heap.
418429 gcLock .Unlock ()
419430
420431 // Return a pointer to this allocation.
421- pointer := thisAlloc .pointer ()
422- if preciseHeap {
423- // Store the object layout at the start of the object.
424- // TODO: this wastes a little bit of space on systems with
425- // larger-than-pointer alignment requirements.
426- * (* unsafe .Pointer )(pointer ) = layout
427- add := align (unsafe .Sizeof (layout ))
428- pointer = unsafe .Add (pointer , add )
429- size -= add
430- }
432+ add := align (unsafe .Sizeof (objHeader {}))
433+ pointer = unsafe .Add (pointer , add )
434+ size -= add
431435 memzero (pointer , size )
432436 return pointer
433437 }
@@ -562,42 +566,33 @@ func markCurrentGoroutineStack(sp uintptr) {
562566 markRoot (0 , sp )
563567}
564568
565- // stackOverflow is a flag which is set when the GC scans too deep while marking.
566- // After it is set, all marked allocations must be re-scanned.
567- var stackOverflow bool
568-
569- // startMark starts the marking process on a root and all of its children.
570- func startMark (root gcBlock ) {
571- var stack [markStackSize ]gcBlock
572- stack [0 ] = root
573- root .setState (blockStateMark )
574- stackLen := 1
575- for stackLen > 0 {
576- // Pop a block off of the stack.
577- stackLen --
578- block := stack [stackLen ]
579- if gcDebug {
580- println ("stack popped, remaining stack:" , stackLen )
569+ // finishMark finishes the marking process by scanning all heap objects on scanList.
570+ func finishMark () {
571+ for {
572+ // Remove an object from the scan list.
573+ obj := scanList
574+ if obj == nil {
575+ return
581576 }
577+ scanList = obj .next
582578
583- // Scan all pointers inside the block .
584- scanner := newGCObjectScanner ( block )
579+ // Create a scanner with the object layout .
580+ scanner := obj . layout . scanner ( )
585581 if scanner .pointerFree () {
586582 // This object doesn't contain any pointers.
587583 // This is a fast path for objects like make([]int, 4096).
588584 continue
589585 }
590- start , end := block .address (), block .findNext ().address ()
591- if preciseHeap {
592- // The first word of the object is just the pointer layout value.
593- // Skip it.
594- start += align (unsafe .Sizeof (uintptr (0 )))
595- }
586+
587+ // Scan all pointers in the object.
588+ start := uintptr (unsafe .Pointer (obj )) + align (unsafe .Sizeof (objHeader {}))
589+ end := blockFromAddr (uintptr (unsafe .Pointer (obj ))).findNext ().address ()
590+
596591 for addr := start ; addr != end ; addr += unsafe .Alignof (addr ) {
597592 // Load the word.
598593 word := * (* uintptr )(unsafe .Pointer (addr ))
599594
600- if ! scanner .nextIsPointer (word , root . address ( ), addr ) {
595+ if ! scanner .nextIsPointer (word , uintptr ( unsafe . Pointer ( obj ) ), addr ) {
601596 // Not a heap pointer.
602597 continue
603598 }
@@ -628,58 +623,46 @@ func startMark(root gcBlock) {
628623 }
629624 referencedBlock .setState (blockStateMark )
630625
631- if stackLen == len (stack ) {
632- // The stack is full.
633- // It is necessary to rescan all marked blocks once we are done.
634- stackOverflow = true
635- if gcDebug {
636- println ("gc stack overflowed" )
637- }
638- continue
639- }
640-
641- // Push the pointer onto the stack to be scanned later.
642- stack [stackLen ] = referencedBlock
643- stackLen ++
626+ // Add the object to the scan list.
627+ header := (* objHeader )(referencedBlock .pointer ())
628+ header .next = scanList
629+ scanList = header
644630 }
645631 }
646632}
647633
648- // finishMark finishes the marking process by processing all stack overflows.
649- func finishMark () {
650- for stackOverflow {
651- // Re-mark all blocks.
652- stackOverflow = false
653- for block := gcBlock (0 ); block < endBlock ; block ++ {
654- if block .state () != blockStateMark {
655- // Block is not marked, so we do not need to rescan it.
656- continue
657- }
634+ // mark a GC root at the address addr.
635+ func markRoot (addr , root uintptr ) {
636+ // Find the heap block corresponding to the root.
637+ if ! isOnHeap (root ) {
638+ // This is not a heap pointer.
639+ return
640+ }
641+ block := blockFromAddr (root )
658642
659- // Re-mark the block.
660- startMark (block )
661- }
643+ // Find the head of the corresponding object.
644+ if block .state () == blockStateFree {
645+ // The to-be-marked object doesn't actually exist.
646+ // This could either be a dangling pointer (oops!) but most likely
647+ // just a false positive.
648+ return
662649 }
663- }
650+ head := block . findHead ()
664651
665- // mark a GC root at the address addr.
666- func markRoot (addr , root uintptr ) {
667- if isOnHeap (root ) {
668- block := blockFromAddr (root )
669- if block .state () == blockStateFree {
670- // The to-be-marked object doesn't actually exist.
671- // This could either be a dangling pointer (oops!) but most likely
672- // just a false positive.
673- return
674- }
675- head := block .findHead ()
676- if head .state () != blockStateMark {
677- if gcDebug {
678- println ("found unmarked pointer" , root , "at address" , addr )
679- }
680- startMark (head )
681- }
652+ // Mark the object.
653+ if head .state () == blockStateMark {
654+ // This object is already marked.
655+ return
656+ }
657+ if gcDebug {
658+ println ("found unmarked pointer" , root , "at address" , addr )
682659 }
660+ head .setState (blockStateMark )
661+
662+ // Add the object to the scan list.
663+ header := (* objHeader )(head .pointer ())
664+ header .next = scanList
665+ scanList = header
683666}
684667
685668// Sweep goes through all memory and frees unmarked memory.
0 commit comments