gc_leaking.mx raw

   1  //go:build gc.leaking
   2  
   3  package runtime
   4  
   5  // This GC implementation is the simplest useful memory allocator possible: it
   6  // only allocates memory and never frees it. For some constrained systems, it
   7  // may be the only memory allocator possible.
   8  
   9  import (
  10  	"internal/task"
  11  	"unsafe"
  12  )
  13  
  14  const needsStaticHeap = true
  15  
  16  // Ever-incrementing pointer: no memory is freed.
  17  var heapptr uintptr
  18  
  19  // Total amount allocated for runtime.MemStats
  20  var gcTotalAlloc uint64
  21  
  22  // Total number of calls to alloc()
  23  var gcMallocs uint64
  24  
  25  // Heap lock for parallel goroutines. No-op when single threaded.
  26  var gcLock task.PMutex
  27  
  28  // Total number of objected freed; for leaking collector this stays 0
  29  const gcFrees = 0
  30  
  31  // Inlining alloc() speeds things up slightly but bloats the executable by 50%,
  32  // see https://github.com/moxie-org/moxie/issues/2674.  So don't.
  33  //
  34  //go:noinline
  35  func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
  36  	// TODO: this can be optimized by not casting between pointers and ints so
  37  	// much. And by using platform-native data types (e.g. *uint8 for 8-bit
  38  	// systems).
  39  	gcLock.Lock()
  40  	size = align(size)
  41  	addr := heapptr
  42  	gcTotalAlloc += uint64(size)
  43  	gcMallocs++
  44  	heapptr += size
  45  	for heapptr >= heapEnd {
  46  		// Try to increase the heap and check again.
  47  		if growHeap() {
  48  			continue
  49  		}
  50  		// Failed to make the heap bigger, so we must really be out of memory.
  51  		runtimePanic("out of memory")
  52  	}
  53  	gcLock.Unlock()
  54  
  55  	pointer := unsafe.Pointer(addr)
  56  	zero_new_alloc(pointer, size)
  57  	return pointer
  58  }
  59  
  60  func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
  61  	newAlloc := alloc(size, nil)
  62  	if ptr == nil {
  63  		return newAlloc
  64  	}
  65  	// according to POSIX everything beyond the previous pointer's
  66  	// size will have indeterminate values so we can just copy garbage
  67  	memcpy(newAlloc, ptr, size)
  68  
  69  	return newAlloc
  70  }
  71  
  72  func free(ptr unsafe.Pointer) {
  73  	// Memory is never freed.
  74  }
  75  
  76  func markRoots(start, end uintptr) {
  77  	runtimePanic("unreachable: markRoots")
  78  }
  79  
  80  // ReadMemStats populates m with memory statistics.
  81  //
  82  // The returned memory statistics are up to date as of the
  83  // call to ReadMemStats. This would not do GC implicitly for you.
  84  func ReadMemStats(m *MemStats) {
  85  	gcLock.Lock()
  86  
  87  	m.HeapIdle = 0
  88  	m.HeapInuse = gcTotalAlloc
  89  	m.HeapReleased = 0 // always 0, we don't currently release memory back to the OS.
  90  
  91  	m.HeapSys = m.HeapInuse + m.HeapIdle
  92  	m.GCSys = 0
  93  	m.TotalAlloc = gcTotalAlloc
  94  	m.Mallocs = gcMallocs
  95  	m.Frees = gcFrees
  96  	m.Sys = uint64(heapEnd - heapStart)
  97  	// no free -- current in use heap is the total allocated
  98  	m.HeapAlloc = gcTotalAlloc
  99  	m.Alloc = m.HeapAlloc
 100  
 101  	gcLock.Unlock()
 102  }
 103  
 104  func GC() {
 105  	// No-op.
 106  }
 107  
 108  func SetFinalizer(obj interface{}, finalizer interface{}) {
 109  	// No-op.
 110  }
 111  
 112  func initHeap() {
 113  	// Initialize this bump-pointer allocator to the start of the heap.
 114  	// Needed here because heapStart may not be a compile-time constant.
 115  	heapptr = heapStart
 116  }
 117  
 118  // setHeapEnd sets a new (larger) heapEnd pointer.
 119  func setHeapEnd(newHeapEnd uintptr) {
 120  	// This "heap" is so simple that simply assigning a new value is good
 121  	// enough.
 122  	heapEnd = newHeapEnd
 123  }
 124