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  }