Memory Management Implementation¶
This document provides detailed implementation information about Movian's memory management system, including low-level allocation mechanisms, platform-specific implementations, and advanced memory management techniques.
Overview¶
This document complements Memory Management Patterns by focusing on implementation details, platform-specific code, and advanced topics. For general memory management patterns and best practices, see the Memory Management Patterns document.
Platform-Specific Implementations¶
POSIX Platforms (Linux, macOS)¶
Standard Allocation:
// src/arch/posix/posix_malloc.c
void *mymalloc(size_t size) {
void *ptr = malloc(size);
if (ptr == NULL) {
fprintf(stderr, "Out of memory: failed to allocate %zu bytes\n", size);
abort(); // Fatal error
}
return ptr;
}
void *mycalloc(size_t nmemb, size_t size) {
void *ptr = calloc(nmemb, size);
if (ptr == NULL) {
fprintf(stderr, "Out of memory: failed to allocate %zu bytes\n",
nmemb * size);
abort();
}
return ptr;
}
void *myrealloc(void *ptr, size_t size) {
void *new_ptr = realloc(ptr, size);
if (new_ptr == NULL && size != 0) {
fprintf(stderr, "Out of memory: failed to reallocate to %zu bytes\n",
size);
abort();
}
return new_ptr;
}
void myfree(void *ptr) {
free(ptr);
}
Memory Alignment:
// Allocate aligned memory
void *mymemalign(size_t alignment, size_t size) {
void *ptr;
if (posix_memalign(&ptr, alignment, size) != 0) {
fprintf(stderr, "Failed to allocate %zu bytes with alignment %zu\n",
size, alignment);
abort();
}
return ptr;
}
Source Reference: src/arch/posix/posix_malloc.c
Windows Platform¶
Windows Allocation:
// src/arch/win32/win32_malloc.c
void *mymalloc(size_t size) {
void *ptr = HeapAlloc(GetProcessHeap(), 0, size);
if (ptr == NULL) {
fprintf(stderr, "Out of memory: failed to allocate %zu bytes\n", size);
abort();
}
return ptr;
}
void *mycalloc(size_t nmemb, size_t size) {
size_t total = nmemb * size;
void *ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, total);
if (ptr == NULL) {
fprintf(stderr, "Out of memory: failed to allocate %zu bytes\n", total);
abort();
}
return ptr;
}
void myfree(void *ptr) {
if (ptr != NULL) {
HeapFree(GetProcessHeap(), 0, ptr);
}
}
Source Reference: src/arch/win32/win32_malloc.c
Embedded Platforms (PS3, Android)¶
Custom Allocator:
// src/arch/ps3/ps3_malloc.c
// PS3 uses custom memory regions
#define MAIN_MEMORY_SIZE (256 * 1024 * 1024) // 256 MB
static void *memory_pool = NULL;
static size_t memory_used = 0;
void memory_init(void) {
memory_pool = sys_memory_allocate(MAIN_MEMORY_SIZE,
SYS_MEMORY_PAGE_SIZE_1M);
if (memory_pool == NULL) {
fprintf(stderr, "Failed to allocate main memory pool\n");
abort();
}
}
void *mymalloc(size_t size) {
// Custom allocation from pool
// Implementation details...
}
Source Reference: src/arch/ps3/ps3_malloc.c
Memory Pool Implementation¶
Pool Structure¶
// src/misc/pool.c
typedef struct mempool_chunk {
struct mempool_chunk *next;
void *memory;
} mempool_chunk_t;
typedef struct mempool {
size_t object_size;
size_t objects_per_chunk;
size_t alignment;
hts_mutex_t mutex;
void *free_list;
mempool_chunk_t *chunks;
// Statistics
atomic_t num_allocations;
atomic_t num_frees;
atomic_t peak_usage;
} mempool_t;
Pool Operations¶
Pool Creation:
mempool_t *mempool_create(size_t object_size, size_t objects_per_chunk) {
mempool_t *pool = mymalloc(sizeof(mempool_t));
// Align object size to cache line
pool->object_size = (object_size + 63) & ~63;
pool->objects_per_chunk = objects_per_chunk;
pool->alignment = 64; // Cache line size
hts_mutex_init(&pool->mutex);
pool->free_list = NULL;
pool->chunks = NULL;
atomic_set(&pool->num_allocations, 0);
atomic_set(&pool->num_frees, 0);
atomic_set(&pool->peak_usage, 0);
return pool;
}
Chunk Allocation:
static void mempool_add_chunk(mempool_t *pool) {
size_t chunk_size = pool->object_size * pool->objects_per_chunk;
// Allocate chunk
mempool_chunk_t *chunk = mymalloc(sizeof(mempool_chunk_t));
chunk->memory = mymemalign(pool->alignment, chunk_size);
// Add to chunk list
chunk->next = pool->chunks;
pool->chunks = chunk;
// Initialize free list
char *ptr = chunk->memory;
for (size_t i = 0; i < pool->objects_per_chunk; i++) {
void **free_node = (void **)ptr;
*free_node = pool->free_list;
pool->free_list = free_node;
ptr += pool->object_size;
}
}
Object Allocation:
void *mempool_alloc(mempool_t *pool) {
hts_mutex_lock(&pool->mutex);
// Allocate new chunk if needed
if (pool->free_list == NULL) {
mempool_add_chunk(pool);
}
// Get object from free list
void **free_node = pool->free_list;
pool->free_list = *free_node;
hts_mutex_unlock(&pool->mutex);
// Update statistics
atomic_inc(&pool->num_allocations);
// Zero memory
memset(free_node, 0, pool->object_size);
return free_node;
}
Object Deallocation:
void mempool_free(mempool_t *pool, void *ptr) {
if (ptr == NULL) {
return;
}
hts_mutex_lock(&pool->mutex);
// Add to free list
void **free_node = ptr;
*free_node = pool->free_list;
pool->free_list = free_node;
hts_mutex_unlock(&pool->mutex);
// Update statistics
atomic_inc(&pool->num_frees);
}
Pool Destruction:
void mempool_destroy(mempool_t *pool) {
// Free all chunks
mempool_chunk_t *chunk = pool->chunks;
while (chunk != NULL) {
mempool_chunk_t *next = chunk->next;
myfree(chunk->memory);
myfree(chunk);
chunk = next;
}
hts_mutex_destroy(&pool->mutex);
myfree(pool);
}
Source Reference: src/misc/pool.c
Reference Counting Implementation¶
Reference Count Structure¶
// src/misc/refcount.c
typedef struct refcount {
atomic_t count;
void (*destructor)(void *);
void *opaque;
} refcount_t;
#define REFCOUNT_HEADER \
refcount_t rc_header
Reference Count Operations¶
Initialization:
void refcount_init(refcount_t *rc, void (*destructor)(void *), void *opaque) {
atomic_set(&rc->count, 1);
rc->destructor = destructor;
rc->opaque = opaque;
}
Increment:
void refcount_inc(refcount_t *rc) {
int old_count = atomic_inc(&rc->count);
assert(old_count > 0); // Detect use-after-free
}
Decrement:
void refcount_dec(refcount_t *rc) {
int new_count = atomic_dec(&rc->count);
if (new_count == 0) {
// Last reference, destroy object
if (rc->destructor) {
rc->destructor(rc->opaque);
}
} else {
assert(new_count > 0); // Detect double-free
}
}
Source Reference: src/misc/refcount.c
Property Reference Counting¶
// src/prop/prop.c
typedef struct prop {
REFCOUNT_HEADER;
hts_mutex_t hp_mutex;
prop_t *hp_parent;
TAILQ_HEAD(, prop) hp_childs;
// ... other fields
} prop_t;
void prop_ref_inc(prop_t *p) {
refcount_inc(&p->rc_header);
}
void prop_ref_dec(prop_t *p) {
refcount_dec(&p->rc_header);
}
static void prop_destroy(void *opaque) {
prop_t *p = opaque;
// Destroy children
prop_t *child;
while ((child = TAILQ_FIRST(&p->hp_childs)) != NULL) {
TAILQ_REMOVE(&p->hp_childs, child, hp_link);
prop_ref_dec(child);
}
hts_mutex_destroy(&p->hp_mutex);
myfree(p);
}
prop_t *prop_create(prop_t *parent) {
prop_t *p = mymalloc(sizeof(prop_t));
refcount_init(&p->rc_header, prop_destroy, p);
hts_mutex_init(&p->hp_mutex);
TAILQ_INIT(&p->hp_childs);
p->hp_parent = parent;
if (parent) {
prop_ref_inc(parent);
TAILQ_INSERT_TAIL(&parent->hp_childs, p, hp_link);
}
return p;
}
Source Reference: src/prop/prop.c
Memory Debugging¶
Debug Allocator¶
// src/misc/debug_malloc.c
#ifdef DEBUG_MEMORY
typedef struct alloc_header {
size_t size;
const char *file;
int line;
uint32_t magic;
struct alloc_header *next;
struct alloc_header *prev;
} alloc_header_t;
#define ALLOC_MAGIC 0xDEADBEEF
#define ALLOC_FREED_MAGIC 0xFREEDBAD
static hts_mutex_t alloc_mutex;
static alloc_header_t *alloc_list = NULL;
static size_t total_allocated = 0;
static size_t peak_allocated = 0;
void *debug_malloc(size_t size, const char *file, int line) {
// Allocate with header and footer
size_t total_size = sizeof(alloc_header_t) + size + sizeof(uint32_t);
alloc_header_t *header = malloc(total_size);
if (header == NULL) {
fprintf(stderr, "Out of memory at %s:%d\n", file, line);
abort();
}
// Initialize header
header->size = size;
header->file = file;
header->line = line;
header->magic = ALLOC_MAGIC;
// Add to allocation list
hts_mutex_lock(&alloc_mutex);
header->next = alloc_list;
header->prev = NULL;
if (alloc_list) {
alloc_list->prev = header;
}
alloc_list = header;
total_allocated += size;
if (total_allocated > peak_allocated) {
peak_allocated = total_allocated;
}
hts_mutex_unlock(&alloc_mutex);
// Set footer magic
uint32_t *footer = (uint32_t *)((char *)(header + 1) + size);
*footer = ALLOC_MAGIC;
return header + 1;
}
void debug_free(void *ptr, const char *file, int line) {
if (ptr == NULL) {
return;
}
alloc_header_t *header = (alloc_header_t *)ptr - 1;
// Check header magic
if (header->magic != ALLOC_MAGIC) {
fprintf(stderr, "Invalid free at %s:%d (allocated at %s:%d)\n",
file, line, header->file, header->line);
if (header->magic == ALLOC_FREED_MAGIC) {
fprintf(stderr, "Double free detected!\n");
}
abort();
}
// Check footer magic
uint32_t *footer = (uint32_t *)((char *)ptr + header->size);
if (*footer != ALLOC_MAGIC) {
fprintf(stderr, "Buffer overflow detected at %s:%d (allocated at %s:%d)\n",
file, line, header->file, header->line);
abort();
}
// Remove from allocation list
hts_mutex_lock(&alloc_mutex);
if (header->prev) {
header->prev->next = header->next;
} else {
alloc_list = header->next;
}
if (header->next) {
header->next->prev = header->prev;
}
total_allocated -= header->size;
hts_mutex_unlock(&alloc_mutex);
// Mark as freed
header->magic = ALLOC_FREED_MAGIC;
// Fill with pattern to detect use-after-free
memset(ptr, 0xDD, header->size);
free(header);
}
void debug_dump_leaks(void) {
hts_mutex_lock(&alloc_mutex);
if (alloc_list == NULL) {
printf("No memory leaks detected\n");
} else {
printf("Memory leaks detected:\n");
alloc_header_t *header = alloc_list;
size_t leak_count = 0;
size_t leak_bytes = 0;
while (header) {
printf(" %zu bytes at %s:%d\n",
header->size, header->file, header->line);
leak_count++;
leak_bytes += header->size;
header = header->next;
}
printf("Total: %zu leaks, %zu bytes\n", leak_count, leak_bytes);
}
printf("Peak memory usage: %zu bytes\n", peak_allocated);
hts_mutex_unlock(&alloc_mutex);
}
#endif // DEBUG_MEMORY
Source Reference: src/misc/debug_malloc.c
Valgrind Integration¶
Valgrind Annotations:
#ifdef HAVE_VALGRIND
#include <valgrind/memcheck.h>
void *mymalloc(size_t size) {
void *ptr = malloc(size);
if (ptr) {
VALGRIND_MALLOCLIKE_BLOCK(ptr, size, 0, 0);
}
return ptr;
}
void myfree(void *ptr) {
if (ptr) {
VALGRIND_FREELIKE_BLOCK(ptr, 0);
free(ptr);
}
}
#endif
Suppression File (movian.supp):
{
OpenGL_driver_leak
Memcheck:Leak
...
obj:*/libGL.so*
}
{
FFmpeg_internal_buffer
Memcheck:Leak
...
obj:*/libavcodec.so*
}
Memory Optimization Techniques¶
Object Pooling¶
Widget Pool:
// src/ui/glw/glw.c
static mempool_t *widget_pool = NULL;
void glw_init(void) {
widget_pool = mempool_create(sizeof(glw_t), 256);
}
glw_t *glw_alloc(void) {
return mempool_alloc(widget_pool);
}
void glw_free(glw_t *w) {
mempool_free(widget_pool, w);
}
String Interning¶
String Pool:
// src/misc/strtab.c
typedef struct strtab {
hts_mutex_t mutex;
AVL_TREE(, strtab_entry) entries;
} strtab_t;
const char *strtab_get(strtab_t *st, const char *str) {
hts_mutex_lock(&st->mutex);
strtab_entry_t *entry = strtab_find(st, str);
if (entry) {
entry->refcount++;
hts_mutex_unlock(&st->mutex);
return entry->str;
}
// Create new entry
entry = mymalloc(sizeof(strtab_entry_t));
entry->str = mystrdup(str);
entry->refcount = 1;
AVL_INSERT(&st->entries, entry);
hts_mutex_unlock(&st->mutex);
return entry->str;
}
void strtab_release(strtab_t *st, const char *str) {
hts_mutex_lock(&st->mutex);
strtab_entry_t *entry = strtab_find(st, str);
if (entry && --entry->refcount == 0) {
AVL_REMOVE(&st->entries, entry);
myfree(entry->str);
myfree(entry);
}
hts_mutex_unlock(&st->mutex);
}
Source Reference: src/misc/strtab.c
Copy-on-Write¶
COW String:
typedef struct cow_string {
atomic_t refcount;
size_t length;
char data[];
} cow_string_t;
cow_string_t *cow_string_create(const char *str) {
size_t len = strlen(str);
cow_string_t *s = mymalloc(sizeof(cow_string_t) + len + 1);
atomic_set(&s->refcount, 1);
s->length = len;
memcpy(s->data, str, len + 1);
return s;
}
cow_string_t *cow_string_copy(cow_string_t *s) {
atomic_inc(&s->refcount);
return s;
}
cow_string_t *cow_string_modify(cow_string_t *s) {
if (atomic_get(&s->refcount) == 1) {
// Sole owner, can modify in place
return s;
}
// Shared, create copy
cow_string_t *copy = cow_string_create(s->data);
cow_string_release(s);
return copy;
}
void cow_string_release(cow_string_t *s) {
if (atomic_dec(&s->refcount) == 0) {
myfree(s);
}
}
Platform-Specific Optimizations¶
Linux: jemalloc¶
// Use jemalloc for better performance
#ifdef USE_JEMALLOC
#include <jemalloc/jemalloc.h>
void *mymalloc(size_t size) {
return je_malloc(size);
}
void myfree(void *ptr) {
je_free(ptr);
}
#endif
macOS: Zone Allocator¶
// Use malloc zones on macOS
#ifdef __APPLE__
#include <malloc/malloc.h>
static malloc_zone_t *movian_zone = NULL;
void memory_init(void) {
movian_zone = malloc_create_zone(0, 0);
malloc_set_zone_name(movian_zone, "Movian");
}
void *mymalloc(size_t size) {
return malloc_zone_malloc(movian_zone, size);
}
void myfree(void *ptr) {
malloc_zone_free(movian_zone, ptr);
}
#endif
Windows: Low Fragmentation Heap¶
// Enable low fragmentation heap on Windows
#ifdef _WIN32
void memory_init(void) {
HANDLE heap = GetProcessHeap();
ULONG heap_info = 2; // Enable LFH
HeapSetInformation(heap, HeapCompatibilityInformation,
&heap_info, sizeof(heap_info));
}
#endif
See Also¶
- Memory Management Patterns - High-level patterns and best practices
- Threading Model - Thread safety and synchronization
- Architecture Overview - System architecture
- Performance Optimization - Performance tuning