From bf376a616010df00f39741d6554eddfd0a649e92 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 26 Feb 2026 08:56:23 +0900 Subject: [PATCH] memory.md: document mrb_gc_add_region() heap region API Co-authored-by: Claude --- doc/guides/memory.md | 97 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/doc/guides/memory.md b/doc/guides/memory.md index 28dd0a5c4..c48945daf 100644 --- a/doc/guides/memory.md +++ b/doc/guides/memory.md @@ -1,4 +1,4 @@ - + # Memory Allocation @@ -114,3 +114,98 @@ If you are moving from the old API: mrb_state *mrb = mrb_open_core(); ``` + +--- + +## 3. Heap Regions: Contiguous Memory for GC + +By default, mruby allocates GC heap pages individually via `malloc()`. +On embedded targets with multiple memory banks (e.g., STM32 CCM+SRAM, +ESP32 PSRAM+IRAM), you may want to place heap pages in a specific +memory region. `mrb_gc_add_region()` lets you provide a contiguous +buffer that mruby carves into heap pages. + +### API + +```c +#include + +int mrb_gc_add_region(mrb_state *mrb, void *start, size_t size); +``` + +- **`start`**: pointer to a contiguous memory buffer. +- **`size`**: size of the buffer in bytes. +- **Returns**: number of heap pages carved from the buffer, or 0 if + the buffer is too small. + +The buffer is aligned internally to pointer size. Each page is +approximately 40 KB on 64-bit systems (24 KB on 32-bit). The caller +retains ownership of the buffer and must keep it valid for the +lifetime of the `mrb_state`. + +### Example: Static buffer + +```c +#include +#include + +/* 256 KB static buffer -- about 6 pages on 64-bit */ +static char heap_buf[256 * 1024]; + +int main(void) +{ + mrb_state *mrb = mrb_open(); + int pages = mrb_gc_add_region(mrb, heap_buf, sizeof(heap_buf)); + /* pages are immediately available for object allocation */ + + /* ... use mrb ... */ + + mrb_close(mrb); /* region pages are cleaned up; buffer is not freed */ + return 0; +} +``` + +### Example: MCU with multiple RAM banks + +```c +/* STM32 with 64 KB CCM and 128 KB SRAM */ +extern char __ccm_start[], __ccm_end[]; /* linker symbols */ +extern char __sram_start[], __sram_end[]; + +mrb_state *mrb = mrb_open(); +mrb_gc_add_region(mrb, __ccm_start, __ccm_end - __ccm_start); +mrb_gc_add_region(mrb, __sram_start, __sram_end - __sram_start); +``` + +### How it works + +When `mrb_gc_add_region()` is called, mruby: + +1. Aligns the buffer start to pointer size. +2. Divides the buffer into `mrb_heap_page`-sized chunks. +3. Initializes each page's freelist and links it into the GC heap. +4. Records the region in a descriptor for O(1) pointer-to-page mapping. + +Region pages participate in the normal GC cycle (mark-and-sweep) like +any other heap page. The only differences are: + +- **Never freed**: the GC will not call `free()` on region pages, even + if all objects on a page are dead. The page stays in the heap with an + empty freelist, ready for reuse. +- **Fallback**: when all region pages are full, mruby falls back to + `malloc()` for new pages as usual. +- **Cleanup**: `mrb_close()` frees the internal region descriptor but + does not free the buffer itself. + +### Sizing + +The page size is controlled by `MRB_HEAP_PAGE_SIZE` (default: 1024 slots). +Each page occupies: + +| Platform | Slot size | Page size (approx) | +|----------|-----------|---------------------| +| 64-bit | 40 bytes | ~41 KB | +| 32-bit | 24 bytes | ~25 KB | + +To estimate pages for a given buffer: `pages = buffer_size / sizeof(mrb_heap_page)`. +Each page provides `MRB_HEAP_PAGE_SIZE` object slots.