github.com/higress-group/nottinygc@v0.0.0-20231101025119-e93c4c2f8520/gc.go (about) 1 // Copyright wasilibs authors 2 // SPDX-License-Identifier: MIT 3 4 //go:build gc.custom 5 6 package nottinygc 7 8 import ( 9 "runtime" 10 "unsafe" 11 ) 12 13 /* 14 #include <stddef.h> 15 16 void* GC_malloc_ignore_off_page(unsigned int size); 17 void* GC_malloc(unsigned int size); 18 void* GC_malloc_atomic(unsigned int size); 19 void* GC_malloc_explicitly_typed(unsigned int size, unsigned int gc_descr); 20 void* GC_calloc_explicitly_typed(unsigned int nelements, unsigned int element_size, unsigned int gc_descr); 21 unsigned int GC_make_descriptor(void* bm, unsigned int len); 22 void GC_free(void* ptr); 23 void GC_gcollect(); 24 void GC_set_on_collection_event(void* f); 25 26 size_t GC_get_gc_no(); 27 void GC_get_heap_usage_safe(size_t* heap_size, size_t* free_bytes, size_t* unmapped_bytes, size_t* bytesSinceGC, size_t* totalBytes); 28 size_t GC_get_obtained_from_os_bytes(); 29 void mi_process_info(size_t *elapsed_msecs, size_t *user_msecs, size_t *system_msecs, size_t *current_rss, size_t *peak_rss, size_t *current_commit, size_t *peak_commit, size_t *page_faults); 30 31 void GC_ignore_warn_proc(char* msg, unsigned int arg); 32 void GC_set_warn_proc(void* p); 33 void GC_set_max_heap_size(unsigned int n); 34 35 void onCollectionEvent(); 36 */ 37 import "C" 38 39 const ( 40 gcEventStart = 0 41 ) 42 43 const ( 44 gcDsBitmap = uintptr(1) 45 // Bdwgc recommend that use GC_malloc_ignore_off_page when alloc memory more than 100KB. 46 // see more info: https://github.com/ivmai/bdwgc/blob/master/README.md 47 bigObjsz = 100 * 1024 48 // WASM vm's max memory usage in Envoy is 1GB 49 maxHeapsz = 1024 * 1024 * 1024 50 ) 51 52 var descriptorCache = newIntMap() 53 54 //export onCollectionEvent 55 func onCollectionEvent(eventType uint32) { 56 switch eventType { 57 case gcEventStart: 58 markStack() 59 } 60 } 61 62 // Initialize the memory allocator. 63 // 64 //go:linkname initHeap runtime.initHeap 65 func initHeap() { 66 C.GC_set_on_collection_event(C.onCollectionEvent) 67 // We avoid overhead in calling GC_make_descriptor on every allocation by implementing 68 // the bitmap computation in Go, but we need to call it at least once to initialize 69 // typed GC itself. 70 C.GC_make_descriptor(nil, 0) 71 C.GC_set_warn_proc(C.GC_ignore_warn_proc) 72 C.GC_set_max_heap_size(maxHeapsz) 73 } 74 75 // alloc tries to find some free space on the heap, possibly doing a garbage 76 // collection cycle if needed. If no space is free, it panics. 77 // 78 //go:linkname alloc runtime.alloc 79 func alloc(size uintptr, layoutPtr unsafe.Pointer) unsafe.Pointer { 80 var buf unsafe.Pointer 81 82 if size >= bigObjsz { 83 buf = C.GC_malloc_ignore_off_page(C.uint(size)) 84 } else { 85 buf = C.GC_malloc(C.uint(size)) 86 } 87 if buf == nil { 88 panic("out of memory") 89 } 90 return buf 91 } 92 93 //go:linkname free runtime.free 94 func free(ptr unsafe.Pointer) { 95 C.GC_free(ptr) 96 } 97 98 //go:linkname markRoots runtime.markRoots 99 func markRoots(start, end uintptr) { 100 // Roots are already registered in bdwgc so we have nothing to do here. 101 } 102 103 //go:linkname markStack runtime.markStack 104 func markStack() 105 106 // GC performs a garbage collection cycle. 107 // 108 //go:linkname GC runtime.GC 109 func GC() { 110 C.GC_gcollect() 111 } 112 113 //go:linkname ReadMemStats runtime.ReadMemStats 114 func ReadMemStats(ms *runtime.MemStats) { 115 var heapSize, freeBytes, unmappedBytes, bytesSinceGC, totalBytes C.size_t 116 C.GC_get_heap_usage_safe(&heapSize, &freeBytes, &unmappedBytes, &bytesSinceGC, &totalBytes) 117 118 var peakRSS C.size_t 119 C.mi_process_info(nil, nil, nil, nil, &peakRSS, nil, nil, nil) 120 121 gcOSBytes := C.GC_get_obtained_from_os_bytes() 122 123 ms.Sys = uint64(peakRSS + gcOSBytes) 124 ms.HeapSys = uint64(heapSize) 125 ms.HeapIdle = uint64(freeBytes) 126 ms.HeapReleased = uint64(unmappedBytes) 127 ms.TotalAlloc = uint64(totalBytes) 128 }