@@ -94,6 +94,9 @@ const (
94
94
pageShift = _PageShift
95
95
pageSize = _PageSize
96
96
pageMask = _PageMask
97
+ // By construction, single page spans of the smallest object class
98
+ // have the most objects per span.
99
+ maxObjsPerSpan = pageSize / 8
97
100
98
101
mSpanInUse = _MSpanInUse
99
102
@@ -167,9 +170,6 @@ const (
167
170
_MaxGcproc = 32
168
171
)
169
172
170
- // Page number (address>>pageShift)
171
- type pageID uintptr
172
-
173
173
const _MaxArena32 = 2 << 30
174
174
175
175
// OS-defined helpers:
@@ -384,6 +384,10 @@ func sysReserveHigh(n uintptr, reserved *bool) unsafe.Pointer {
384
384
return sysReserve (nil , n , reserved )
385
385
}
386
386
387
+ // sysAlloc allocates the next n bytes from the heap arena. The
388
+ // returned pointer is always _PageSize aligned and between
389
+ // h.arena_start and h.arena_end. sysAlloc returns nil on failure.
390
+ // There is no corresponding free function.
387
391
func (h * mheap ) sysAlloc (n uintptr ) unsafe.Pointer {
388
392
if n > h .arena_end - h .arena_used {
389
393
// We are in 32-bit mode, maybe we didn't use all possible address space yet.
@@ -484,6 +488,65 @@ func (h *mheap) sysAlloc(n uintptr) unsafe.Pointer {
484
488
// base address for all 0-byte allocations
485
489
var zerobase uintptr
486
490
491
+ // nextFreeFast returns the next free object if one is quickly available.
492
+ // Otherwise it returns 0.
493
+ func nextFreeFast (s * mspan ) gclinkptr {
494
+ theBit := sys .Ctz64 (s .allocCache ) // Is there a free object in the allocCache?
495
+ if theBit < 64 {
496
+ result := s .freeindex + uintptr (theBit )
497
+ if result < s .nelems {
498
+ freeidx := result + 1
499
+ if freeidx % 64 == 0 && freeidx != s .nelems {
500
+ return 0
501
+ }
502
+ s .allocCache >>= (theBit + 1 )
503
+ s .freeindex = freeidx
504
+ v := gclinkptr (result * s .elemsize + s .base ())
505
+ s .allocCount ++
506
+ return v
507
+ }
508
+ }
509
+ return 0
510
+ }
511
+
512
+ // nextFree returns the next free object from the cached span if one is available.
513
+ // Otherwise it refills the cache with a span with an available object and
514
+ // returns that object along with a flag indicating that this was a heavy
515
+ // weight allocation. If it is a heavy weight allocation the caller must
516
+ // determine whether a new GC cycle needs to be started or if the GC is active
517
+ // whether this goroutine needs to assist the GC.
518
+ func (c * mcache ) nextFree (sizeclass int8 ) (v gclinkptr , s * mspan , shouldhelpgc bool ) {
519
+ s = c .alloc [sizeclass ]
520
+ shouldhelpgc = false
521
+ freeIndex := s .nextFreeIndex ()
522
+ if freeIndex == s .nelems {
523
+ // The span is full.
524
+ if uintptr (s .allocCount ) != s .nelems {
525
+ println ("runtime: s.allocCount=" , s .allocCount , "s.nelems=" , s .nelems )
526
+ throw ("s.allocCount != s.nelems && freeIndex == s.nelems" )
527
+ }
528
+ systemstack (func () {
529
+ c .refill (int32 (sizeclass ))
530
+ })
531
+ shouldhelpgc = true
532
+ s = c .alloc [sizeclass ]
533
+
534
+ freeIndex = s .nextFreeIndex ()
535
+ }
536
+
537
+ if freeIndex >= s .nelems {
538
+ throw ("freeIndex is not valid" )
539
+ }
540
+
541
+ v = gclinkptr (freeIndex * s .elemsize + s .base ())
542
+ s .allocCount ++
543
+ if uintptr (s .allocCount ) > s .nelems {
544
+ println ("s.allocCount=" , s .allocCount , "s.nelems=" , s .nelems )
545
+ throw ("s.allocCount > s.nelems" )
546
+ }
547
+ return
548
+ }
549
+
487
550
// Allocate an object of size bytes.
488
551
// Small objects are allocated from the per-P cache's free lists.
489
552
// Large objects (> 32 kB) are allocated straight from the heap.
@@ -538,7 +601,6 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
538
601
shouldhelpgc := false
539
602
dataSize := size
540
603
c := gomcache ()
541
- var s * mspan
542
604
var x unsafe.Pointer
543
605
noscan := typ == nil || typ .kind & kindNoPointers != 0
544
606
if size <= maxSmallSize {
@@ -591,20 +653,11 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
591
653
return x
592
654
}
593
655
// Allocate a new maxTinySize block.
594
- s = c .alloc [tinySizeClass ]
595
- v := s .freelist
596
- if v .ptr () == nil {
597
- systemstack (func () {
598
- c .refill (tinySizeClass )
599
- })
600
- shouldhelpgc = true
601
- s = c .alloc [tinySizeClass ]
602
- v = s .freelist
656
+ span := c .alloc [tinySizeClass ]
657
+ v := nextFreeFast (span )
658
+ if v == 0 {
659
+ v , _ , shouldhelpgc = c .nextFree (tinySizeClass )
603
660
}
604
- s .freelist = v .ptr ().next
605
- s .ref ++
606
- // prefetchnta offers best performance, see change list message.
607
- prefetchnta (uintptr (v .ptr ().next ))
608
661
x = unsafe .Pointer (v )
609
662
(* [2 ]uint64 )(x )[0 ] = 0
610
663
(* [2 ]uint64 )(x )[1 ] = 0
@@ -623,26 +676,14 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
623
676
sizeclass = size_to_class128 [(size - 1024 + 127 )>> 7 ]
624
677
}
625
678
size = uintptr (class_to_size [sizeclass ])
626
- s = c .alloc [sizeclass ]
627
- v := s .freelist
628
- if v .ptr () == nil {
629
- systemstack (func () {
630
- c .refill (int32 (sizeclass ))
631
- })
632
- shouldhelpgc = true
633
- s = c .alloc [sizeclass ]
634
- v = s .freelist
679
+ span := c .alloc [sizeclass ]
680
+ v := nextFreeFast (span )
681
+ if v == 0 {
682
+ v , span , shouldhelpgc = c .nextFree (sizeclass )
635
683
}
636
- s .freelist = v .ptr ().next
637
- s .ref ++
638
- // prefetchnta offers best performance, see change list message.
639
- prefetchnta (uintptr (v .ptr ().next ))
640
684
x = unsafe .Pointer (v )
641
- if needzero {
642
- v .ptr ().next = 0
643
- if size > 2 * sys .PtrSize && ((* [2 ]uintptr )(x ))[1 ] != 0 {
644
- memclr (unsafe .Pointer (v ), size )
645
- }
685
+ if needzero && span .needzero != 0 {
686
+ memclr (unsafe .Pointer (v ), size )
646
687
}
647
688
}
648
689
} else {
@@ -651,13 +692,15 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
651
692
systemstack (func () {
652
693
s = largeAlloc (size , needzero )
653
694
})
654
- x = unsafe .Pointer (uintptr (s .start << pageShift ))
695
+ s .freeindex = 1
696
+ s .allocCount = 1
697
+ x = unsafe .Pointer (s .base ())
655
698
size = s .elemsize
656
699
}
657
700
658
701
var scanSize uintptr
659
702
if noscan {
660
- // All objects are pre-marked as noscan. Nothing to do.
703
+ heapBitsSetTypeNoScan ( uintptr ( x ), size )
661
704
} else {
662
705
// If allocating a defer+arg block, now that we've picked a malloc size
663
706
// large enough to hold everything, cut the "asked for" size down to
@@ -701,6 +744,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
701
744
if raceenabled {
702
745
racemalloc (x , size )
703
746
}
747
+
704
748
if msanenabled {
705
749
msanmalloc (x , size )
706
750
}
@@ -755,8 +799,8 @@ func largeAlloc(size uintptr, needzero bool) *mspan {
755
799
if s == nil {
756
800
throw ("out of memory" )
757
801
}
758
- s .limit = uintptr ( s . start ) << _PageShift + size
759
- heapBitsForSpan (s .base ()).initSpan (s . layout () )
802
+ s .limit = s . base () + size
803
+ heapBitsForSpan (s .base ()).initSpan (s )
760
804
return s
761
805
}
762
806
0 commit comments