Memory
At the lowest level, there's virtual memory, which can be mapped to physical memory using the underlying operating system.
The heap sits directly above that, dividing memory into variable-sized blocks that can be dynamically allocated and freed by the application as needed. Higher-level containers for managing memory, like String, Array, Map, Owned and Reference, are described in later sections.
Heap
Plywood contains its own heap, which lets you allocate and free variable-sized blocks of memory.
| Low-level allocation | |
static void* | alloc(uptr numBytes) |
static void* | realloc(void* ptr, uptr numBytes) |
static void* | free(void* ptr) |
static void* | allocAligned(uptr numBytes, u32 alignment) |
| Creating and destroying objects | |
static T* | create<T>(Args&& args) |
static void | destroy(T* obj) |
| Monitoring the heap | |
static void | setOutOfMemoryHandler(Functor<void()> handler) |
static Heap::Stats | getStats() |
static void | validate() |
The Plywood heap is separate from the C Standard Library's heap. Both heaps can coexist in the same program, but memory allocated from a specific heap must always be freed using the same heap.
Heap backend selection is controlled by PLY_USE_NEW_ALLOCATOR:
PLY_USE_NEW_ALLOCATOR=1(default): Uses Plywood's bespoke allocator inply::HeapImpl.PLY_USE_NEW_ALLOCATOR=0: Uses the legacy dlmalloc backend.
For internal implementation details of the bespoke allocator, see Heap Design.
Heap is thread-safe. All member functions can be called concurrently from separate threads.
Low-level Allocation
static void* alloc(uptr numBytes)Allocates a block of memory from the Plywood heap. Equivalent to
malloc. Always returns 16-byte aligned memory, suitable for SIMD vectors. Returnsnullptrif allocation fails.static void* realloc(void* ptr, uptr numBytes)Resizes a previously allocated block. The contents are preserved up to the smaller of the old and new sizes. May return a different pointer.
static void* free(void* ptr)Frees a previously allocated block, returning the memory to the heap.
static void* allocAligned(uptr numBytes, u32 alignment)Allocates memory with a specific alignment. Use for alignments greater than 16 bytes.
Creating and Destroying Objects
By default, Plywood will override C++ new and delete to use the Plywood heap. If you don't want this behavior, perhaps because you're integrating Plywood into an existing application, define PLY_OVERRIDE_NEW=0.
You can create and destroy C++ objects in the Plywood heap directly using Heap::create and Heap::destroy, which essentially work like new and delete:
template <typename Type> static Type* create<Type>(Args&& args)Allocates heap memory for an object of type
Typeand calls the constructor. The provided arguments are passed to the constructor using perfect forwarding.template <typename Type> static void destroy(Type* obj)Invokes the destructor of an object and frees its memory back to the heap.
Owned<Foo> createFoo() {
return Heap::create<Foo>();
}
void destroy(Foo* foo) {
Heap::destroy(foo);
}
Monitoring the Heap
static void setOutOfMemoryHandler(Functor<void()> handler)Sets an out-of-memory handler.
handlerwill be called if an allocation fails due to insufficient system memory.Out-of-memory events are usually unrecoverable. There's really no ideal way to handle them, other than to collect a report when the event occurs so that the issue can be investigated. In general, developers should establish a memory budget and aim to stay within it.
static Heap::Stats getStats()Returns statistics about heap usage.
numBytesAllocatedis the sum of the sizes of all allocated blocks.virtualMemorySize, a larger number, is the total amount of system memory used to store those blocks, including bookkeeping overhead and unused space.struct Heap::Stats { uptr totalBytesConsumed; // Total number of bytes consumed by heap allocations including chunk headers. uptr totalSystemMemoryUsed; // Total number of bytes currently committed via the system's VirtualMemory API. }static void validate()Validates the heap's internal consistency. Useful for debugging. Will force an immediate crash if the heap is corrupted, which is usually caused by a memory overrun or dangling pointer. Inserting calls to
validatecan help track down the cause of the corruption.validateperforms checks only whenPLY_WITH_ASSERTSis enabled.
VirtualMemory
The VirtualMemory class is a platform-independent wrapper for mapping virtual memory to physical memory.
| System Information | |
static Properties | getProperties() |
static SystemStats | getSystemStats() |
| Managing Pages | |
static void* | reserveRegion(uptr numBytes) |
static void | unreserveRegion(void* addr, uptr numReservedBytes, uptr numCommittedBytes) |
static void | commitPages(void* addr, uptr numBytes) |
static void | decommitPages(void* addr, uptr numBytes) |
| Allocating Large Blocks | |
static void* | allocRegion(uptr numBytes) |
static void | freeRegion(void* addr, uptr numBytes) |
| Usage Stats | |
static Atomic<uptr> | totalReservedBytes |
static Atomic<uptr> | totalCommittedBytes |
In C++ applications, memory is represented as a 32-bit or 64-bit address space known as virtual memory, which is divided into fixed-sized pages. Most pages are initially unusable and will cause an access violation or segmentation fault if accessed. To make pages usable, they must be mapped to physical memory by the underlying operating system.
System Information
static VirtualMemory::Properties getProperties()Returns information about the system's virtual memory page size and allocation alignment.
VirtualMemory::Propertieshas these members:{table caption="
VirtualMemory::Propertiesmembers"}uptr|regionAlignment|reserveRegionandallocRegionsizes must be a multiple of thisuptr|pageSize|commitPagessizes must be a multiple of this {/table}static VirtualMemory::SystemStats getSystemStats()Returns statistics about the current process's virtual memory usage.
VirtualMemory::SystemStatshas platform-specific members:On Windows:
{table caption="
VirtualMemory::SystemStatsmembers (Windows)"}uptr|privateUsageuptr|workingSetSize {/table}On other platforms:
{table caption="
VirtualMemory::SystemStatsmembers (POSIX)"}uptr|virtualSizeuptr|residentSize {/table}
Managing Pages
static void* reserveRegion(uptr numBytes)Reserves a region of address space. Memory pages are initially uncommitted. Returns
nullptron failure.numBytesmust be a multiple ofregionAlignment.static void unreserveRegion(void* addr, uptr numReservedBytes, uptr numCommittedBytes)Unreserves a region of address space.
numReservedBytesmust match the argument passed toreserveRegion. Caller is responsible for passing the correctnumCommittedBytes, otherwise stats will get out of sync.static void commitPages(void* addr, uptr numBytes)Commits a subregion of reserved address space, making it legal to read and write to the subregion.
addrmust be aligned topageSizeandnumBytesmust be a multiple ofpageSize.static void decommitPages(void* addr, uptr numBytes)Decommits a subregion of previously committed memory.
addrmust be aligned topageSizeandnumBytesmust be a multiple ofpageSize.
Allocating Large Blocks
static void* allocRegion(uptr numBytes)Reserves and commits a region of address space. Returns
nullptron failure. Free usingfreeRegion. Don't decommit any pages in the returned region, otherwise stats will get out of sync.numBytesmust be a multiple ofregionAlignment.static void freeRegion(void* addr, uptr numBytes)Decommits and unreserves a region of address space.
numBytesmust match the argument passed toallocRegion.
Usage Stats
static Atomic<uptr> totalReservedBytesThe current total amount of address space that was reserved using
allocRegionorreserveRegion.static Atomic<uptr> totalCommittedBytesThe current total amount of memory that was committed using
allocRegionorcommitPages.