Source file
src/runtime/mgcmark_greenteagc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 package runtime
38
39 import (
40 "internal/goarch"
41 "internal/runtime/atomic"
42 "internal/runtime/gc"
43 "internal/runtime/gc/scan"
44 "internal/runtime/sys"
45 "unsafe"
46 )
47
48 const doubleCheckGreenTea = false
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 type spanInlineMarkBits struct {
67 scans [63]uint8
68 owned spanScanOwnership
69 marks [63]uint8
70 class spanClass
71 }
72
73
74
75
76
77
78
79 type spanScanOwnership uint8
80
81 const (
82 spanScanUnowned spanScanOwnership = 0
83 spanScanOneMark = 1 << iota
84 spanScanManyMark
85
86
87
88 )
89
90
91 func (o *spanScanOwnership) load() spanScanOwnership {
92 return spanScanOwnership(atomic.Load8((*uint8)(unsafe.Pointer(o))))
93 }
94
95 func (o *spanScanOwnership) or(v spanScanOwnership) spanScanOwnership {
96
97
98
99
100
101
102
103
104
105 o32 := (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(o)) &^ 0b11))
106 off := (uintptr(unsafe.Pointer(o)) & 0b11) * 8
107 if goarch.BigEndian {
108 off = 32 - off - 8
109 }
110 return spanScanOwnership(atomic.Or32(o32, uint32(v)<<off) >> off)
111 }
112
113 func (imb *spanInlineMarkBits) init(class spanClass, needzero bool) {
114 if imb == nil {
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 throw("runtime: span inline mark bits nil?")
133 }
134 if needzero {
135
136
137
138
139 memclrNoHeapPointers(unsafe.Pointer(imb), unsafe.Sizeof(spanInlineMarkBits{}))
140 }
141 imb.class = class
142 }
143
144
145
146 func (imb *spanInlineMarkBits) tryAcquire() bool {
147 switch imb.owned.load() {
148 case spanScanUnowned:
149
150 if imb.owned.or(spanScanOneMark) == spanScanUnowned {
151 return true
152 }
153
154
155
156 fallthrough
157 case spanScanOneMark:
158
159
160 return imb.owned.or(spanScanManyMark) == spanScanUnowned
161 }
162 return false
163 }
164
165
166
167
168
169
170
171
172
173 func (imb *spanInlineMarkBits) release() spanScanOwnership {
174 return spanScanOwnership(atomic.Xchg8((*uint8)(unsafe.Pointer(&imb.owned)), uint8(spanScanUnowned)))
175 }
176
177
178
179
180 func spanInlineMarkBitsFromBase(base uintptr) *spanInlineMarkBits {
181 return (*spanInlineMarkBits)(unsafe.Pointer(base + gc.PageSize - unsafe.Sizeof(spanInlineMarkBits{})))
182 }
183
184
185 func (s *mspan) initInlineMarkBits() {
186 if doubleCheckGreenTea && !gcUsesSpanInlineMarkBits(s.elemsize) {
187 throw("expected span with inline mark bits")
188 }
189
190 s.inlineMarkBits().init(s.spanclass, s.needzero != 0)
191 }
192
193
194
195
196 func (s *mspan) moveInlineMarks(dst *gcBits) {
197 if doubleCheckGreenTea && !gcUsesSpanInlineMarkBits(s.elemsize) {
198 throw("expected span with inline mark bits")
199 }
200 bytes := divRoundUp(uintptr(s.nelems), 8)
201 imb := s.inlineMarkBits()
202 imbMarks := (*gc.ObjMask)(unsafe.Pointer(&imb.marks))
203 for i := uintptr(0); i < bytes; i += goarch.PtrSize {
204 marks := bswapIfBigEndian(imbMarks[i/goarch.PtrSize])
205 if i/goarch.PtrSize == uintptr(len(imb.marks)+1)/goarch.PtrSize-1 {
206 marks &^= 0xff << ((goarch.PtrSize - 1) * 8)
207 }
208 *(*uintptr)(unsafe.Pointer(dst.bytep(i))) |= bswapIfBigEndian(marks)
209 }
210 if doubleCheckGreenTea && !s.spanclass.noscan() && imb.marks != imb.scans {
211 throw("marks don't match scans for span with pointer")
212 }
213
214
215 imb.init(s.spanclass, true )
216 }
217
218
219
220
221 func (s *mspan) inlineMarkBits() *spanInlineMarkBits {
222 if doubleCheckGreenTea && !gcUsesSpanInlineMarkBits(s.elemsize) {
223 throw("expected span with inline mark bits")
224 }
225 return spanInlineMarkBitsFromBase(s.base())
226 }
227
228 func (s *mspan) markBitsForIndex(objIndex uintptr) (bits markBits) {
229 if gcUsesSpanInlineMarkBits(s.elemsize) {
230 bits.bytep = &s.inlineMarkBits().marks[objIndex/8]
231 } else {
232 bits.bytep = s.gcmarkBits.bytep(objIndex / 8)
233 }
234 bits.mask = uint8(1) << (objIndex % 8)
235 bits.index = objIndex
236 return
237 }
238
239 func (s *mspan) markBitsForBase() markBits {
240 if gcUsesSpanInlineMarkBits(s.elemsize) {
241 return markBits{&s.inlineMarkBits().marks[0], uint8(1), 0}
242 }
243 return markBits{&s.gcmarkBits.x, uint8(1), 0}
244 }
245
246
247
248 func (s *mspan) scannedBitsForIndex(objIndex uintptr) markBits {
249 return markBits{&s.inlineMarkBits().scans[objIndex/8], uint8(1) << (objIndex % 8), objIndex}
250 }
251
252
253
254
255
256
257
258 func gcUsesSpanInlineMarkBits(size uintptr) bool {
259 return heapBitsInSpan(size) && size >= 16
260 }
261
262
263
264 func tryDeferToSpanScan(p uintptr, gcw *gcWork) bool {
265 if useCheckmark {
266 return false
267 }
268
269
270 ha := heapArenaOf(p)
271 if ha == nil {
272 return false
273 }
274 pageIdx := ((p / pageSize) / 8) % uintptr(len(ha.pageInUse))
275 pageMask := byte(1 << ((p / pageSize) % 8))
276 if ha.pageUseSpanInlineMarkBits[pageIdx]&pageMask == 0 {
277 return false
278 }
279
280
281 base := alignDown(p, gc.PageSize)
282 q := spanInlineMarkBitsFromBase(base)
283 objIndex := uint16((uint64(p-base) * uint64(gc.SizeClassToDivMagic[q.class.sizeclass()])) >> 32)
284
285
286 idx, mask := objIndex/8, uint8(1)<<(objIndex%8)
287 if atomic.Load8(&q.marks[idx])&mask != 0 {
288 return true
289 }
290 atomic.Or8(&q.marks[idx], mask)
291
292
293 if q.class.noscan() {
294 gcw.bytesMarked += uint64(gc.SizeClassToSize[q.class.sizeclass()])
295 return true
296 }
297
298
299 if q.tryAcquire() {
300 if gcw.spanq.put(makeObjPtr(base, objIndex)) {
301 if gcphase == _GCmark {
302
303
304
305 if !work.spanqMask.read(uint32(gcw.id)) {
306 work.spanqMask.set(gcw.id)
307 }
308 gcw.mayNeedWorker = true
309 }
310 gcw.flushedWork = true
311 }
312 }
313 return true
314 }
315
316
317 func (w *gcWork) tryGetSpanFast() objptr {
318 return w.spanq.tryGetFast()
319 }
320
321
322 func (w *gcWork) tryGetSpan() objptr {
323 if s := w.spanq.tryGetFast(); s != 0 {
324 return s
325 }
326
327 if s := w.spanq.steal(&w.spanq); s != 0 {
328 return s
329 }
330
331
332 if work.spanqMask.read(uint32(w.id)) {
333 work.spanqMask.clear(w.id)
334 }
335 return 0
336 }
337
338
339 type spanQueue struct {
340
341 head, tail uint32
342 ring [256]objptr
343
344
345 putsSinceDrain int
346
347
348
349
350
351
352
353 chain struct {
354
355
356 head *spanSPMC
357
358
359
360 tail atomic.UnsafePointer
361 }
362 }
363
364
365 func (q *spanQueue) putFast(s objptr) (ok bool) {
366 if q.tail-q.head == uint32(len(q.ring)) {
367 return false
368 }
369 q.ring[q.tail%uint32(len(q.ring))] = s
370 q.tail++
371 return true
372 }
373
374
375
376
377 func (q *spanQueue) put(s objptr) bool {
378
379
380
381
382
383
384
385
386
387
388 const (
389
390
391 spillPeriod = 64
392
393
394
395 spillMax = 16
396 )
397
398 if q.putFast(s) {
399
400 q.putsSinceDrain++
401 if q.putsSinceDrain >= spillPeriod {
402
403 q.putsSinceDrain = 0
404
405
406
407 n := min((q.tail-q.head)/2, spillMax)
408 if n > 4 && q.chainEmpty() {
409 q.drain(n)
410 return true
411 }
412 }
413 return false
414 }
415
416
417 q.drain(uint32(len(q.ring)) / 2)
418 if !q.putFast(s) {
419 throw("failed putFast after drain")
420 }
421 return true
422 }
423
424
425 func (q *spanQueue) flush() {
426 n := q.tail - q.head
427 if n == 0 {
428 return
429 }
430 q.drain(n)
431 }
432
433
434
435
436 func (q *spanQueue) empty() bool {
437
438 if q.tail-q.head > 0 {
439 return false
440 }
441 return q.chainEmpty()
442 }
443
444
445
446
447 func (q *spanQueue) chainEmpty() bool {
448
449 r := (*spanSPMC)(q.chain.tail.Load())
450 for r != nil {
451 if !r.empty() {
452 return false
453 }
454 r = (*spanSPMC)(r.prev.Load())
455 }
456 return true
457 }
458
459
460 func (q *spanQueue) drain(n uint32) {
461 q.putsSinceDrain = 0
462
463 if q.chain.head == nil {
464
465
466
467 r := newSpanSPMC(1024)
468 q.chain.head = r
469 q.chain.tail.StoreNoWB(unsafe.Pointer(r))
470 }
471
472
473 if q.tryDrain(q.chain.head, n) {
474 return
475 }
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495 const maxCap = 1 << 20 / goarch.PtrSize
496 newCap := q.chain.head.cap * 2
497 if newCap > maxCap {
498 newCap = maxCap
499 }
500 newHead := newSpanSPMC(newCap)
501 if !q.tryDrain(newHead, n) {
502 throw("failed to put span on newly-allocated spanSPMC")
503 }
504 q.chain.head.prev.StoreNoWB(unsafe.Pointer(newHead))
505 q.chain.head = newHead
506 }
507
508
509
510
511 func (q *spanQueue) tryDrain(r *spanSPMC, n uint32) bool {
512 if q.head+n > q.tail {
513 throw("attempt to drain too many elements")
514 }
515 h := r.head.Load()
516 t := r.tail.Load()
517 rn := t - h
518 if rn+n <= r.cap {
519 for i := uint32(0); i < n; i++ {
520 *r.slot(t + i) = q.ring[(q.head+i)%uint32(len(q.ring))]
521 }
522 r.tail.Store(t + n)
523 q.head += n
524 return true
525 }
526 return false
527 }
528
529
530
531 func (q *spanQueue) tryGetFast() objptr {
532 if q.tail-q.head == 0 {
533 return 0
534 }
535 s := q.ring[q.head%uint32(len(q.ring))]
536 q.head++
537 return s
538 }
539
540
541
542
543 func (q *spanQueue) steal(q2 *spanQueue) objptr {
544 r := (*spanSPMC)(q2.chain.tail.Load())
545 if r == nil {
546 return 0
547 }
548 for {
549
550
551
552
553
554
555 r2 := (*spanSPMC)(r.prev.Load())
556
557
558 if s := q.refill(r); s != 0 {
559 return s
560 }
561
562 if r2 == nil {
563
564
565 return 0
566 }
567
568
569
570
571
572 if q2.chain.tail.CompareAndSwapNoWB(unsafe.Pointer(r), unsafe.Pointer(r2)) {
573 r.dead.Store(true)
574 }
575
576 r = r2
577 }
578 }
579
580
581
582
583
584
585
586 func (q *spanQueue) refill(r *spanSPMC) objptr {
587 if q.tail-q.head != 0 {
588 throw("steal with local work available")
589 }
590
591
592 var n uint32
593 for {
594 h := r.head.Load()
595 t := r.tail.Load()
596 n = t - h
597 n = n - n/2
598 if n == 0 {
599 return 0
600 }
601 if n > r.cap {
602 continue
603 }
604 n = min(n, uint32(len(q.ring)/2))
605 for i := uint32(0); i < n; i++ {
606 q.ring[i] = *r.slot(h + i)
607 }
608 if r.head.CompareAndSwap(h, h+n) {
609 break
610 }
611 }
612
613
614 q.head = 0
615 q.tail = n
616
617
618 return q.tryGetFast()
619 }
620
621
622
623
624
625
626
627 func (q *spanQueue) destroy() {
628 assertWorldStopped()
629 if gcphase != _GCoff {
630 throw("spanQueue.destroy during the mark phase")
631 }
632 if !q.empty() {
633 throw("spanQueue.destroy on non-empty queue")
634 }
635
636 lock(&work.spanSPMCs.lock)
637
638
639 for r := (*spanSPMC)(q.chain.tail.Load()); r != nil; r = (*spanSPMC)(r.prev.Load()) {
640 work.spanSPMCs.list.remove(unsafe.Pointer(r))
641 r.deinit()
642 mheap_.spanSPMCAlloc.free(unsafe.Pointer(r))
643 }
644
645 q.chain.head = nil
646 q.chain.tail.Store(nil)
647 q.putsSinceDrain = 0
648
649 unlock(&work.spanSPMCs.lock)
650 }
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677 type spanSPMC struct {
678 _ sys.NotInHeap
679
680
681
682
683 allnode listNodeManual
684
685
686
687
688
689 dead atomic.Bool
690
691
692
693
694 prev atomic.UnsafePointer
695
696
697
698 head atomic.Uint32
699 tail atomic.Uint32
700 cap uint32
701 ring *objptr
702 }
703
704
705
706
707
708 func newSpanSPMC(cap uint32) *spanSPMC {
709 lock(&work.spanSPMCs.lock)
710 r := (*spanSPMC)(mheap_.spanSPMCAlloc.alloc())
711 work.spanSPMCs.list.push(unsafe.Pointer(r))
712 unlock(&work.spanSPMCs.lock)
713
714
715 pageCap := uint32(physPageSize / goarch.PtrSize)
716 if cap < pageCap {
717 cap = pageCap
718 }
719 if cap&(cap-1) != 0 {
720 throw("spmc capacity must be a power of 2")
721 }
722
723 r.cap = cap
724 ring := sysAlloc(uintptr(cap)*unsafe.Sizeof(objptr(0)), &memstats.gcMiscSys, "GC span queue")
725 atomic.StorepNoWB(unsafe.Pointer(&r.ring), ring)
726 return r
727 }
728
729
730
731
732 func (r *spanSPMC) empty() bool {
733 h := r.head.Load()
734 t := r.tail.Load()
735 return t == h
736 }
737
738
739 func (r *spanSPMC) deinit() {
740 sysFree(unsafe.Pointer(r.ring), uintptr(r.cap)*unsafe.Sizeof(objptr(0)), &memstats.gcMiscSys)
741 r.ring = nil
742 r.dead.Store(false)
743 r.prev.StoreNoWB(nil)
744 r.head.Store(0)
745 r.tail.Store(0)
746 r.cap = 0
747 r.allnode = listNodeManual{}
748 }
749
750
751 func (r *spanSPMC) slot(i uint32) *objptr {
752 idx := uintptr(i & (r.cap - 1))
753 return (*objptr)(unsafe.Add(unsafe.Pointer(r.ring), idx*unsafe.Sizeof(objptr(0))))
754 }
755
756
757 func freeDeadSpanSPMCs() {
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775 lock(&work.spanSPMCs.lock)
776 if gcphase != _GCoff || work.spanSPMCs.list.empty() {
777 unlock(&work.spanSPMCs.lock)
778 return
779 }
780 r := (*spanSPMC)(work.spanSPMCs.list.head())
781 for r != nil {
782 next := (*spanSPMC)(unsafe.Pointer(r.allnode.next))
783 if r.dead.Load() {
784
785 work.spanSPMCs.list.remove(unsafe.Pointer(r))
786 r.deinit()
787 mheap_.spanSPMCAlloc.free(unsafe.Pointer(r))
788 }
789 r = next
790 }
791 unlock(&work.spanSPMCs.lock)
792 }
793
794
795
796
797 func (w *gcWork) tryStealSpan() objptr {
798 pp := getg().m.p.ptr()
799
800 for enum := stealOrder.start(cheaprand()); !enum.done(); enum.next() {
801 if !work.spanqMask.read(enum.position()) {
802 continue
803 }
804 p2 := allp[enum.position()]
805 if pp == p2 {
806 continue
807 }
808 if s := w.spanq.steal(&p2.gcw.spanq); s != 0 {
809 return s
810 }
811
812
813
814
815
816
817
818 work.spanqMask.clear(int32(enum.position()))
819 }
820 return 0
821 }
822
823
824 type objptr uintptr
825
826
827 func makeObjPtr(spanBase uintptr, objIndex uint16) objptr {
828 if doubleCheckGreenTea && spanBase&((1<<gc.PageShift)-1) != 0 {
829 throw("created objptr with address that is incorrectly aligned")
830 }
831 return objptr(spanBase | uintptr(objIndex))
832 }
833
834 func (p objptr) spanBase() uintptr {
835 return uintptr(p) &^ ((1 << gc.PageShift) - 1)
836 }
837
838 func (p objptr) objIndex() uint16 {
839 return uint16(p) & ((1 << gc.PageShift) - 1)
840 }
841
842
843
844 func scanSpan(p objptr, gcw *gcWork) {
845 spanBase := p.spanBase()
846 imb := spanInlineMarkBitsFromBase(spanBase)
847 spanclass := imb.class
848 if spanclass.noscan() {
849 throw("noscan object in scanSpan")
850 }
851 elemsize := uintptr(gc.SizeClassToSize[spanclass.sizeclass()])
852
853
854 if imb.release() == spanScanOneMark {
855
856
857 objIndex := p.objIndex()
858 bytep := &imb.scans[objIndex/8]
859 mask := uint8(1) << (objIndex % 8)
860 if atomic.Load8(bytep)&mask != 0 {
861 return
862 }
863 atomic.Or8(bytep, mask)
864 gcw.bytesMarked += uint64(elemsize)
865 if debug.gctrace > 1 {
866 gcw.stats[spanclass.sizeclass()].sparseObjsScanned++
867 }
868 b := spanBase + uintptr(objIndex)*elemsize
869 scanObjectSmall(spanBase, b, elemsize, gcw)
870 return
871 }
872
873
874 divMagic := uint64(gc.SizeClassToDivMagic[spanclass.sizeclass()])
875 usableSpanSize := uint64(gc.PageSize - unsafe.Sizeof(spanInlineMarkBits{}))
876 if !spanclass.noscan() {
877 usableSpanSize -= gc.PageSize / goarch.PtrSize / 8
878 }
879 nelems := uint16((usableSpanSize * divMagic) >> 32)
880
881
882 var toScan gc.ObjMask
883 objsMarked := spanSetScans(spanBase, nelems, imb, &toScan)
884 if objsMarked == 0 {
885 return
886 }
887 gcw.bytesMarked += uint64(objsMarked) * uint64(elemsize)
888
889
890
891
892 if !scan.HasFastScanSpanPacked() || objsMarked < int(nelems/8) {
893 if debug.gctrace > 1 {
894 gcw.stats[spanclass.sizeclass()].spansSparseScanned++
895 gcw.stats[spanclass.sizeclass()].spanObjsSparseScanned += uint64(objsMarked)
896 }
897 scanObjectsSmall(spanBase, elemsize, nelems, gcw, &toScan)
898 return
899 }
900
901
902
903
904
905
906 nptrs := scan.ScanSpanPacked(
907 unsafe.Pointer(spanBase),
908 &gcw.ptrBuf[0],
909 &toScan,
910 uintptr(spanclass.sizeclass()),
911 spanPtrMaskUnsafe(spanBase),
912 )
913 gcw.heapScanWork += int64(objsMarked) * int64(elemsize)
914
915 if debug.gctrace > 1 {
916
917 gcw.stats[spanclass.sizeclass()].spansDenseScanned++
918 gcw.stats[spanclass.sizeclass()].spanObjsDenseScanned += uint64(objsMarked)
919 }
920
921
922 for _, p := range gcw.ptrBuf[:nptrs] {
923 if !tryDeferToSpanScan(p, gcw) {
924 if obj, span, objIndex := findObject(p, 0, 0); obj != 0 {
925 greyobject(obj, 0, 0, span, gcw, objIndex)
926 }
927 }
928 }
929 }
930
931
932
933
934
935
936 func spanSetScans(spanBase uintptr, nelems uint16, imb *spanInlineMarkBits, toScan *gc.ObjMask) int {
937 arena, pageIdx, pageMask := pageIndexOf(spanBase)
938 if arena.pageMarks[pageIdx]&pageMask == 0 {
939 atomic.Or8(&arena.pageMarks[pageIdx], pageMask)
940 }
941
942 bytes := divRoundUp(uintptr(nelems), 8)
943 objsMarked := 0
944
945
946
947
948 imbMarks := (*gc.ObjMask)(unsafe.Pointer(&imb.marks))
949 imbScans := (*gc.ObjMask)(unsafe.Pointer(&imb.scans))
950
951
952
953
954 for i := uintptr(0); i < bytes; i += goarch.PtrSize {
955 scans := atomic.Loaduintptr(&imbScans[i/goarch.PtrSize])
956 marks := imbMarks[i/goarch.PtrSize]
957 scans = bswapIfBigEndian(scans)
958 marks = bswapIfBigEndian(marks)
959 if i/goarch.PtrSize == uintptr(len(imb.marks)+1)/goarch.PtrSize-1 {
960 scans &^= 0xff << ((goarch.PtrSize - 1) * 8)
961 marks &^= 0xff << ((goarch.PtrSize - 1) * 8)
962 }
963 toGrey := marks &^ scans
964 toScan[i/goarch.PtrSize] = toGrey
965
966
967 if toGrey != 0 {
968 toGrey = bswapIfBigEndian(toGrey)
969 if goarch.PtrSize == 4 {
970 atomic.Or32((*uint32)(unsafe.Pointer(&imbScans[i/goarch.PtrSize])), uint32(toGrey))
971 } else {
972 atomic.Or64((*uint64)(unsafe.Pointer(&imbScans[i/goarch.PtrSize])), uint64(toGrey))
973 }
974 }
975 objsMarked += sys.OnesCount64(uint64(toGrey))
976 }
977 return objsMarked
978 }
979
980 func scanObjectSmall(spanBase, b, objSize uintptr, gcw *gcWork) {
981 hbitsBase, _ := spanHeapBitsRange(spanBase, gc.PageSize, objSize)
982 hbits := (*byte)(unsafe.Pointer(hbitsBase))
983 ptrBits := extractHeapBitsSmall(hbits, spanBase, b, objSize)
984 gcw.heapScanWork += int64(sys.Len64(uint64(ptrBits)) * goarch.PtrSize)
985 nptrs := 0
986 n := sys.OnesCount64(uint64(ptrBits))
987 for range n {
988 k := sys.TrailingZeros64(uint64(ptrBits))
989 ptrBits &^= 1 << k
990 addr := b + uintptr(k)*goarch.PtrSize
991
992
993
994 sys.Prefetch(addr)
995
996
997 gcw.ptrBuf[nptrs] = addr
998 nptrs++
999 }
1000
1001
1002 for _, p := range gcw.ptrBuf[:nptrs] {
1003 p = *(*uintptr)(unsafe.Pointer(p))
1004 if p == 0 {
1005 continue
1006 }
1007 if !tryDeferToSpanScan(p, gcw) {
1008 if obj, span, objIndex := findObject(p, 0, 0); obj != 0 {
1009 greyobject(obj, 0, 0, span, gcw, objIndex)
1010 }
1011 }
1012 }
1013 }
1014
1015 func scanObjectsSmall(base, objSize uintptr, elems uint16, gcw *gcWork, scans *gc.ObjMask) {
1016 nptrs := 0
1017 for i, bits := range scans {
1018 if i*(goarch.PtrSize*8) > int(elems) {
1019 break
1020 }
1021 n := sys.OnesCount64(uint64(bits))
1022 hbitsBase, _ := spanHeapBitsRange(base, gc.PageSize, objSize)
1023 hbits := (*byte)(unsafe.Pointer(hbitsBase))
1024 for range n {
1025 j := sys.TrailingZeros64(uint64(bits))
1026 bits &^= 1 << j
1027
1028 b := base + uintptr(i*(goarch.PtrSize*8)+j)*objSize
1029 ptrBits := extractHeapBitsSmall(hbits, base, b, objSize)
1030 gcw.heapScanWork += int64(sys.Len64(uint64(ptrBits)) * goarch.PtrSize)
1031
1032 n := sys.OnesCount64(uint64(ptrBits))
1033 for range n {
1034 k := sys.TrailingZeros64(uint64(ptrBits))
1035 ptrBits &^= 1 << k
1036 addr := b + uintptr(k)*goarch.PtrSize
1037
1038
1039
1040 sys.Prefetch(addr)
1041
1042
1043 gcw.ptrBuf[nptrs] = addr
1044 nptrs++
1045 }
1046 }
1047 }
1048
1049
1050 for _, p := range gcw.ptrBuf[:nptrs] {
1051 p = *(*uintptr)(unsafe.Pointer(p))
1052 if p == 0 {
1053 continue
1054 }
1055 if !tryDeferToSpanScan(p, gcw) {
1056 if obj, span, objIndex := findObject(p, 0, 0); obj != 0 {
1057 greyobject(obj, 0, 0, span, gcw, objIndex)
1058 }
1059 }
1060 }
1061 }
1062
1063 func extractHeapBitsSmall(hbits *byte, spanBase, addr, elemsize uintptr) uintptr {
1064
1065
1066
1067
1068
1069
1070
1071
1072 i := (addr - spanBase) / goarch.PtrSize / ptrBits
1073 j := (addr - spanBase) / goarch.PtrSize % ptrBits
1074 bits := elemsize / goarch.PtrSize
1075 word0 := (*uintptr)(unsafe.Pointer(addb(hbits, goarch.PtrSize*(i+0))))
1076 word1 := (*uintptr)(unsafe.Pointer(addb(hbits, goarch.PtrSize*(i+1))))
1077
1078 var read uintptr
1079 if j+bits > ptrBits {
1080
1081 bits0 := ptrBits - j
1082 bits1 := bits - bits0
1083 read = *word0 >> j
1084 read |= (*word1 & ((1 << bits1) - 1)) << bits0
1085 } else {
1086
1087 read = (*word0 >> j) & ((1 << bits) - 1)
1088 }
1089 return read
1090 }
1091
1092
1093
1094
1095
1096
1097
1098 func spanPtrMaskUnsafe(spanBase uintptr) *gc.PtrMask {
1099 base := spanBase + gc.PageSize - unsafe.Sizeof(gc.PtrMask{}) - unsafe.Sizeof(spanInlineMarkBits{})
1100 return (*gc.PtrMask)(unsafe.Pointer(base))
1101 }
1102
1103 type sizeClassScanStats struct {
1104 spansDenseScanned uint64
1105 spanObjsDenseScanned uint64
1106 spansSparseScanned uint64
1107 spanObjsSparseScanned uint64
1108 sparseObjsScanned uint64
1109
1110
1111
1112
1113 }
1114
1115 func dumpScanStats() {
1116 var (
1117 spansDenseScanned uint64
1118 spanObjsDenseScanned uint64
1119 spansSparseScanned uint64
1120 spanObjsSparseScanned uint64
1121 sparseObjsScanned uint64
1122 )
1123 for _, stats := range memstats.lastScanStats {
1124 spansDenseScanned += stats.spansDenseScanned
1125 spanObjsDenseScanned += stats.spanObjsDenseScanned
1126 spansSparseScanned += stats.spansSparseScanned
1127 spanObjsSparseScanned += stats.spanObjsSparseScanned
1128 sparseObjsScanned += stats.sparseObjsScanned
1129 }
1130 totalObjs := sparseObjsScanned + spanObjsSparseScanned + spanObjsDenseScanned
1131 totalSpans := spansSparseScanned + spansDenseScanned
1132 print("scan: total ", sparseObjsScanned, "+", spanObjsSparseScanned, "+", spanObjsDenseScanned, "=", totalObjs, " objs")
1133 print(", ", spansSparseScanned, "+", spansDenseScanned, "=", totalSpans, " spans\n")
1134 for i, stats := range memstats.lastScanStats {
1135 if stats == (sizeClassScanStats{}) {
1136 continue
1137 }
1138 totalObjs := stats.sparseObjsScanned + stats.spanObjsSparseScanned + stats.spanObjsDenseScanned
1139 totalSpans := stats.spansSparseScanned + stats.spansDenseScanned
1140 if i == 0 {
1141 print("scan: class L ")
1142 } else {
1143 print("scan: class ", gc.SizeClassToSize[i], "B ")
1144 }
1145 print(stats.sparseObjsScanned, "+", stats.spanObjsSparseScanned, "+", stats.spanObjsDenseScanned, "=", totalObjs, " objs")
1146 print(", ", stats.spansSparseScanned, "+", stats.spansDenseScanned, "=", totalSpans, " spans\n")
1147 }
1148 }
1149
1150 func (w *gcWork) flushScanStats(dst *[gc.NumSizeClasses]sizeClassScanStats) {
1151 for i := range w.stats {
1152 dst[i].spansDenseScanned += w.stats[i].spansDenseScanned
1153 dst[i].spanObjsDenseScanned += w.stats[i].spanObjsDenseScanned
1154 dst[i].spansSparseScanned += w.stats[i].spansSparseScanned
1155 dst[i].spanObjsSparseScanned += w.stats[i].spanObjsSparseScanned
1156 dst[i].sparseObjsScanned += w.stats[i].sparseObjsScanned
1157 }
1158 clear(w.stats[:])
1159 }
1160
1161
1162
1163
1164
1165 func gcMarkWorkAvailable() bool {
1166 if !work.full.empty() {
1167 return true
1168 }
1169 if work.markrootNext.Load() < work.markrootJobs.Load() {
1170 return true
1171 }
1172 if work.spanqMask.any() {
1173 return true
1174 }
1175 return false
1176 }
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187 func scanObject(b uintptr, gcw *gcWork) {
1188
1189
1190
1191
1192 sys.Prefetch(b)
1193
1194
1195
1196
1197
1198
1199 s := spanOfUnchecked(b)
1200 n := s.elemsize
1201 if n == 0 {
1202 throw("scanObject n == 0")
1203 }
1204 if s.spanclass.noscan() {
1205
1206
1207 throw("scanObject of a noscan object")
1208 }
1209
1210 var tp typePointers
1211 if n > maxObletBytes {
1212
1213
1214 if b == s.base() {
1215
1216
1217
1218
1219
1220 for oblet := b + maxObletBytes; oblet < s.base()+s.elemsize; oblet += maxObletBytes {
1221 if !gcw.putObjFast(oblet) {
1222 gcw.putObj(oblet)
1223 }
1224 }
1225 }
1226
1227
1228
1229
1230 n = s.base() + s.elemsize - b
1231 n = min(n, maxObletBytes)
1232 tp = s.typePointersOfUnchecked(s.base())
1233 tp = tp.fastForward(b-tp.addr, b+n)
1234 } else {
1235 tp = s.typePointersOfUnchecked(b)
1236 }
1237
1238 var scanSize uintptr
1239 for {
1240 var addr uintptr
1241 if tp, addr = tp.nextFast(); addr == 0 {
1242 if tp, addr = tp.next(b + n); addr == 0 {
1243 break
1244 }
1245 }
1246
1247
1248
1249
1250 scanSize = addr - b + goarch.PtrSize
1251
1252
1253
1254 obj := *(*uintptr)(unsafe.Pointer(addr))
1255
1256
1257
1258 if obj != 0 && obj-b >= n {
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268 if !tryDeferToSpanScan(obj, gcw) {
1269 if obj, span, objIndex := findObject(obj, b, addr-b); obj != 0 {
1270 greyobject(obj, b, addr-b, span, gcw, objIndex)
1271 }
1272 }
1273 }
1274 }
1275 gcw.bytesMarked += uint64(n)
1276 gcw.heapScanWork += int64(scanSize)
1277 if debug.gctrace > 1 {
1278 gcw.stats[s.spanclass.sizeclass()].sparseObjsScanned++
1279 }
1280 }
1281
View as plain text