mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 19:43:44 +00:00
a4cbe3326e
For example, with -gc=none and -gc=leaking, no heap needs to be allocated when initializing the runtime. And some GCs (like -gc=custom) are responsible for allocating the heap themselves.
120 lines
2.9 KiB
Go
120 lines
2.9 KiB
Go
//go:build gc.leaking
|
|
|
|
package runtime
|
|
|
|
// This GC implementation is the simplest useful memory allocator possible: it
|
|
// only allocates memory and never frees it. For some constrained systems, it
|
|
// may be the only memory allocator possible.
|
|
|
|
import (
|
|
"internal/task"
|
|
"unsafe"
|
|
)
|
|
|
|
const needsStaticHeap = true
|
|
|
|
// Ever-incrementing pointer: no memory is freed.
|
|
var heapptr uintptr
|
|
|
|
// Total amount allocated for runtime.MemStats
|
|
var gcTotalAlloc uint64
|
|
|
|
// Total number of calls to alloc()
|
|
var gcMallocs uint64
|
|
|
|
// Heap lock for parallel goroutines. No-op when single threaded.
|
|
var gcLock task.PMutex
|
|
|
|
// Total number of objected freed; for leaking collector this stays 0
|
|
const gcFrees = 0
|
|
|
|
// Inlining alloc() speeds things up slightly but bloats the executable by 50%,
|
|
// see https://github.com/tinygo-org/tinygo/issues/2674. So don't.
|
|
//
|
|
//go:noinline
|
|
func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
|
|
// TODO: this can be optimized by not casting between pointers and ints so
|
|
// much. And by using platform-native data types (e.g. *uint8 for 8-bit
|
|
// systems).
|
|
gcLock.Lock()
|
|
size = align(size)
|
|
addr := heapptr
|
|
gcTotalAlloc += uint64(size)
|
|
gcMallocs++
|
|
heapptr += size
|
|
for heapptr >= heapEnd {
|
|
// Try to increase the heap and check again.
|
|
if growHeap() {
|
|
continue
|
|
}
|
|
// Failed to make the heap bigger, so we must really be out of memory.
|
|
runtimePanic("out of memory")
|
|
}
|
|
gcLock.Unlock()
|
|
|
|
pointer := unsafe.Pointer(addr)
|
|
zero_new_alloc(pointer, size)
|
|
return pointer
|
|
}
|
|
|
|
func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
|
newAlloc := alloc(size, nil)
|
|
if ptr == nil {
|
|
return newAlloc
|
|
}
|
|
// according to POSIX everything beyond the previous pointer's
|
|
// size will have indeterminate values so we can just copy garbage
|
|
memcpy(newAlloc, ptr, size)
|
|
|
|
return newAlloc
|
|
}
|
|
|
|
func free(ptr unsafe.Pointer) {
|
|
// Memory is never freed.
|
|
}
|
|
|
|
// ReadMemStats populates m with memory statistics.
|
|
//
|
|
// The returned memory statistics are up to date as of the
|
|
// call to ReadMemStats. This would not do GC implicitly for you.
|
|
func ReadMemStats(m *MemStats) {
|
|
gcLock.Lock()
|
|
|
|
m.HeapIdle = 0
|
|
m.HeapInuse = gcTotalAlloc
|
|
m.HeapReleased = 0 // always 0, we don't currently release memory back to the OS.
|
|
|
|
m.HeapSys = m.HeapInuse + m.HeapIdle
|
|
m.GCSys = 0
|
|
m.TotalAlloc = gcTotalAlloc
|
|
m.Mallocs = gcMallocs
|
|
m.Frees = gcFrees
|
|
m.Sys = uint64(heapEnd - heapStart)
|
|
// no free -- current in use heap is the total allocated
|
|
m.HeapAlloc = gcTotalAlloc
|
|
m.Alloc = m.HeapAlloc
|
|
|
|
gcLock.Unlock()
|
|
}
|
|
|
|
func GC() {
|
|
// No-op.
|
|
}
|
|
|
|
func SetFinalizer(obj interface{}, finalizer interface{}) {
|
|
// No-op.
|
|
}
|
|
|
|
func initHeap() {
|
|
// Initialize this bump-pointer allocator to the start of the heap.
|
|
// Needed here because heapStart may not be a compile-time constant.
|
|
heapptr = heapStart
|
|
}
|
|
|
|
// setHeapEnd sets a new (larger) heapEnd pointer.
|
|
func setHeapEnd(newHeapEnd uintptr) {
|
|
// This "heap" is so simple that simply assigning a new value is good
|
|
// enough.
|
|
heapEnd = newHeapEnd
|
|
}
|