Header: ghostmem/GhostMemoryManager.h
The central singleton class that manages virtual memory allocation, page fault handling, compression/decompression, and LRU eviction. Supports both in-memory compression and optional disk-backed storage modes.
Gets the singleton instance of GhostMemoryManager.
Returns: Reference to the singleton instance.
Thread Safety: Thread-safe. First call initializes the instance.
Example:
auto& manager = GhostMemoryManager::Instance();Initializes the memory manager with custom configuration.
Parameters:
config: Configuration structure with settings for disk backing, page limits, etc.
Returns:
trueon successfalseif disk file cannot be opened (when disk backing is enabled)
Thread Safety: AllocateGhost() calls.
Behavior:
- Stores the configuration settings
- If
config.use_disk_backingis true, opens the disk file - Prints initialization status to stdout
Example:
GhostConfig config;
config.use_disk_backing = true;
config.disk_file_path = "myapp_ghost.swap";
config.compress_before_disk = true;
config.max_memory_pages = 256; // 1MB physical RAM limit
if (!GhostMemoryManager::Instance().Initialize(config)) {
std::cerr << "Failed to initialize GhostMem" << std::endl;
return 1;
}Notes:
- If
Initialize()is not called, default configuration is used (in-memory backing, 5 pages) - Calling
Initialize()multiple times is not recommended
Allocates virtual memory that will be managed by GhostMem's compression system.
Parameters:
size: Number of bytes to allocate (will be rounded up to page boundaries)
Returns:
- Pointer to allocated memory on success
nullptron failure
Thread Safety: Thread-safe with internal mutex locking.
Behavior:
- Rounds
sizeup to nearest page boundary (4096 bytes) - Reserves virtual address space (no physical RAM committed initially)
- Registers the memory block in internal tracking structures
- Physical RAM is committed on first access (via page fault)
Example:
// Allocate 8KB of ghost memory (2 pages)
void* ptr = GhostMemoryManager::Instance().AllocateGhost(8192);
if (ptr != nullptr) {
int* data = static_cast<int*>(ptr);
data[0] = 42; // First access triggers page fault and commits RAM
}Memory Layout:
Virtual Address Space: [=========== size bytes ===========]
Physical RAM: [not allocated until accessed]
After first access: [== 4KB page ==][remaining pages...]
Allocation Tracking:
Each allocation is tracked with metadata:
struct AllocationInfo {
void* page_start; // Page-aligned base address
size_t offset; // Offset within page (0-4095)
size_t size; // Original allocation size
};This enables proper deallocation and reference counting for shared pages.
Notes:
- Memory is NOT zeroed initially (only zeroed on first access)
- Multiple allocations can share the same page
- Must call
DeallocateGhost()to free memory (or useGhostAllocator)
Deallocates memory previously allocated by AllocateGhost().
Parameters:
ptr: Pointer returned byAllocateGhost()size: Size passed toAllocateGhost()(must match original size)
Returns: Nothing (void)
Thread Safety: Thread-safe with internal mutex locking.
Status: ✅ Fully Implemented - Properly releases all resources including compressed data and OS memory.
Behavior:
- Validates pointer is tracked in allocation metadata
- Decrements reference count for all pages in the allocation
- For each page with reference count reaching zero:
- Removes from active_ram_pages LRU list
- Removes compressed data from backing_store (in-memory mode)
- Removes disk location from disk_page_locations (disk-backed mode)
- Releases physical memory via
VirtualFree(Windows) ormunmap(Linux) - Releases virtual memory reservation
- Removes allocation from metadata tracking
Example:
// Allocate memory
void* ptr = GhostMemoryManager::Instance().AllocateGhost(8192);
if (ptr) {
// Use memory
int* data = static_cast<int*>(ptr);
data[0] = 42;
// Deallocate when done
GhostMemoryManager::Instance().DeallocateGhost(ptr, 8192);
}Special Cases:
- nullptr: Safe to pass - function returns immediately without error
- Double-free: Logs warning but doesn't crash - untracked pointer is ignored
- Multi-page allocations: All pages are properly cleaned up
- Evicted pages: Compressed data is cleaned up even if page was swapped out
Reference Counting:
Multiple allocations can share the same 4KB page. GhostMem uses reference counting to track active allocations per page:
Page 0x10000:
├─ Allocation A (refcount=1)
├─ Allocation B (refcount=2)
└─ Page freed when refcount reaches 0
Only when all allocations in a page are freed does the page get fully released to the OS.
Memory Lifecycle:
Allocate → [Virtual Reserved] → First Access → [Physical Committed]
↓
Active in RAM
↓
Evict if needed
↓
[Compressed in Backing Store]
↓
Access again?
↓
Decompress & Restore
↓
Deallocate Called
↓
[Fully Released to OS]
Notes:
- Accessing memory after deallocation causes access violation (expected behavior)
- Deallocation removes both compressed and uncompressed data
- Thread-safe - can be called concurrently with allocations
- Compatible with page fault handling and eviction
Evicts the least recently used page from physical RAM.
Thread Safety: Must be called with mutex already held.
Behavior:
- Identifies least recently used page from active list
- Compresses page data using LZ4
- Stores compressed data in backing store
- Decommits physical RAM for that page
- Moves page to frozen state
Called automatically when:
- Physical page limit (
MAX_PHYSICAL_PAGES) is reached - New page needs to be committed to RAM
Restores a previously evicted page to physical RAM.
Parameters:
page_addr: Address of the frozen page to restore
Thread Safety: Must be called with mutex already held.
Behavior:
- Finds compressed data in backing store
- Decompresses using LZ4
- Commits physical RAM for the page
- Copies decompressed data back
- Updates page state to active
- May trigger eviction if at physical page limit
Called automatically by:
- Page fault handlers when accessing frozen pages
Vectored exception handler for Windows page faults.
Registered at: Startup via AddVectoredExceptionHandler()
Handles: EXCEPTION_ACCESS_VIOLATION exceptions
Behavior:
- Checks if fault address is in managed ghost memory range
- If yes: Restores the page and returns
EXCEPTION_CONTINUE_EXECUTION - If no: Returns
EXCEPTION_CONTINUE_SEARCH(let OS handle it)
POSIX signal handler for Linux page faults.
Registered at: Startup via sigaction(SIGSEGV, ...)
Handles: SIGSEGV signals
Behavior:
- Checks if fault address is in managed ghost memory range
- If yes: Restores the page
- If no: Terminates process (fault not from ghost memory)
Tracks all allocated ghost memory blocks.
Key: Starting address of allocation
Value: Size in bytes
LRU list of currently active (uncompressed) pages in physical RAM.
Front: Most recently used
Back: Least recently used (first to be evicted)
Compressed storage for evicted pages.
Key: Page address
Value: Compressed page data (LZ4 compressed)
Protects all internal data structures.
Type: std::recursive_mutex (allows same thread to lock multiple times)
Locked by:
AllocateGhost()DeallocateGhost()- Page fault handlers (via
VectoredHandler/SignalHandler)
Header: ghostmem/GhostAllocator.h
STL-compatible allocator that uses GhostMemoryManager for allocation.
T: Type of objects to allocate
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
template<typename U>
struct rebind {
using other = GhostAllocator<U>;
};Default constructor.
Example:
GhostAllocator<int> alloc;Copy constructor for different types (rebinding).
Example:
GhostAllocator<int> alloc1;
GhostAllocator<double> alloc2(alloc1); // RebindingAllocates memory for n objects of type T.
Parameters:
n: Number of objects to allocate space for
Returns: Pointer to allocated memory
Throws: std::bad_alloc if allocation fails
Behavior:
- Calculates bytes needed:
n * sizeof(T) - Calls
GhostMemoryManager::Instance().AllocateGhost(bytes) - Returns pointer cast to
T*
Example:
GhostAllocator<int> alloc;
int* ptr = alloc.allocate(100); // Space for 100 integersDeallocates memory previously allocated with allocate().
Parameters:
ptr: Pointer to deallocaten: Number of objects (currently unused)
Example:
alloc.deallocate(ptr, 100);#include "ghostmem/GhostAllocator.h"
#include <vector>
std::vector<int, GhostAllocator<int>> vec;
vec.push_back(42);#include "ghostmem/GhostAllocator.h"
#include <string>
using GhostString = std::basic_string<char, std::char_traits<char>, GhostAllocator<char>>;
GhostString str = "Hello, GhostMem!";#include "ghostmem/GhostAllocator.h"
#include <map>
using GhostMap = std::map<int, std::string,
std::less<int>,
GhostAllocator<std::pair<const int, std::string>>>;
GhostMap map;
map[1] = "one";#include "ghostmem/GhostAllocator.h"
#include <list>
std::list<double, GhostAllocator<double>> list;
list.push_back(3.14);Header: ghostmem/GhostMemoryManager.h
Configuration structure for customizing GhostMemoryManager behavior, including optional disk-backed storage for compressed pages.
| Field | Type | Default | Description |
|---|---|---|---|
use_disk_backing |
bool |
false |
Enable disk storage instead of in-memory compression |
disk_file_path |
std::string |
"ghostmem.swap" |
Path to disk file for storing compressed pages |
max_memory_pages |
size_t |
0 |
Max physical pages in RAM (0 = use constant) |
compress_before_disk |
bool |
true |
Compress pages before writing to disk |
enable_verbose_logging |
bool |
false |
Enable detailed console debug output |
Enable disk-backed storage instead of in-memory backing store.
Default: false (in-memory compression)
Behavior:
- When
true: Compressed pages are written to disk - When
false: Compressed pages remain in process memory - Disk backing reduces memory footprint at the cost of I/O latency
Example:
GhostConfig config;
config.use_disk_backing = true; // Enable disk backingPath to the disk file for storing compressed pages.
Default: "ghostmem.swap"
Behavior:
- Only used when
use_disk_backingistrue - File is created if it doesn't exist, truncated if it does
- Relative paths are relative to working directory
- Automatically deleted on clean shutdown
Examples:
config.disk_file_path = "ghostmem_swap.dat"; // Current directory
config.disk_file_path = "/tmp/myapp_ghost.swap"; // Linux absolute path
config.disk_file_path = "C:\\Temp\\ghostmem.swap"; // Windows absolute pathMaximum number of physical pages allowed in RAM.
Default: 0 (uses MAX_PHYSICAL_PAGES constant = 5)
Behavior:
- When set to non-zero, overrides the
MAX_PHYSICAL_PAGESconstant - When limit is reached, least recently used pages are evicted
- Each page is 4096 bytes (4KB)
Example calculations:
config.max_memory_pages = 256; // 256 * 4KB = 1MB RAM limit
config.max_memory_pages = 1024; // 1024 * 4KB = 4MB RAM limit
config.max_memory_pages = 262144; // 262144 * 4KB = 1GB RAM limitCompress page data before writing to disk.
Default: true (compress before disk write)
Behavior:
- Only applies when
use_disk_backingistrue - When
true: LZ4 compression applied before disk writes (smaller file, slower) - When
false: Raw uncompressed pages written (larger file, faster) - Typical compression ratios: 2-10x for text/structured data
Example:
config.compress_before_disk = true; // Compress (recommended)
config.compress_before_disk = false; // No compression (faster I/O)Enable verbose debug logging to console.
Default: false (silent operation)
Behavior:
- When
false(default): GhostMem operates silently with no console output - When
true: Outputs detailed operational messages including:- Initialization status (disk backing, configuration)
- Page eviction events
- Memory allocation/deallocation messages
- Disk I/O operations
- Error and warning messages
Performance Impact:
- Negligible when disabled (default)
- Minor overhead when enabled due to console I/O
Use Cases:
- Development and debugging
- Monitoring memory behavior
- Performance analysis
- Troubleshooting issues
Example:
// Silent mode (production)
config.enable_verbose_logging = false;
// Verbose mode (development/debugging)
config.enable_verbose_logging = true;Sample Output (when enabled):
[GhostMem] Disk backing enabled: ghostmem.swap (compress=yes)
[GhostMem] Page fully freed: 0x7fff12340000
[GhostMem] Zombie page freed during eviction: 0x7fff12341000
#include "ghostmem/GhostMemoryManager.h"
int main() {
// Configure disk-backed storage with 1MB RAM limit
GhostConfig config;
config.use_disk_backing = true;
config.disk_file_path = "myapp_ghost.swap";
config.compress_before_disk = true;
config.max_memory_pages = 256; // 1MB physical RAM
config.enable_verbose_logging = false; // Silent operation (default)
// Initialize before any allocations
if (!GhostMemoryManager::Instance().Initialize(config)) {
std::cerr << "Failed to initialize GhostMem" << std::endl;
return 1;
}
// Now use GhostAllocator normally...
std::vector<int, GhostAllocator<int>> vec;
// ...
}| Mode | Memory Usage | Speed | Disk I/O | Use Case |
|---|---|---|---|---|
In-Memory (use_disk_backing=false) |
Higher | Fastest | None | Default, sufficient RAM available |
Disk + Compression (use_disk_backing=true, compress_before_disk=true) |
Lowest | Slower | Read/Write | Memory-constrained systems |
Disk + No Compression (use_disk_backing=true, compress_before_disk=false) |
Low | Medium | Read/Write | Fast storage (SSD), CPU-constrained |
Ghost memory pages transition through three states:
Description: Virtual address space allocated, no physical RAM used.
Characteristics:
- Returned by
AllocateGhost() - Zero physical memory footprint
- Accessing triggers page fault
Transitions to: Committed when first accessed
Description: Page backed by physical RAM, actively in use.
Characteristics:
- Listed in
active_pagesLRU - Consumes physical RAM
- Fast access (no page fault)
- Moved to front of LRU on each access
Transitions to: Frozen when evicted (LRU reaches limit)
Description: Page compressed and stored in backing store, physical RAM freed.
Characteristics:
- Stored in
backing_storemap (compressed) - Zero physical RAM footprint
- Accessing triggers restoration
- LZ4 compressed data in process memory
Transitions to: Committed when accessed again
┌───────────┐
│ │ AllocateGhost()
│ Reserved │◄───────────────────────┐
│ │ │
└─────┬─────┘ │
│ │
│ First Access │
│ (Page Fault) │
│ │
┌─────▼─────┐ │
│ │ │
│ Committed │ │
│ (Active) │ │
│ │ │
└─────┬─────┘ │
│ │
│ LRU Eviction │
│ (Compress + Decommit) │
│ │
┌─────▼─────┐ │
│ │ │
│ Frozen │ │
│(Compressed) │
│ │ │
└─────┬─────┘ │
│ │
│ Subsequent Access │
│ (Page Fault + Decompress) │
│ │
└──────────────────────────────┘
File: src/ghostmem/GhostMemoryManager.h
const size_t PAGE_SIZE = 4096;Description: Size of memory pages in bytes.
Default: 4096 (4KB, typical for x86/x64)
Usage: All allocations are rounded up to page boundaries.
Modify when: Porting to architectures with different page sizes (rare).
const size_t MAX_PHYSICAL_PAGES = 5;Description: Maximum number of pages that can be simultaneously active (uncompressed) in physical RAM.
Default: 5 pages = 20KB of physical RAM
Usage: Controls the physical memory limit. When exceeded, LRU page is evicted.
Tune for your workload:
- Low memory systems (IoT): 3-10 pages
- Desktop applications: 100-1000 pages
- Server applications: 1000-10000 pages
Formula:
Physical RAM used = MAX_PHYSICAL_PAGES × PAGE_SIZE
Effective virtual RAM = (allocated pages) × (compression ratio)
Example tuning:
// IoT device with 16MB RAM, allow 1MB for ghost memory
const size_t MAX_PHYSICAL_PAGES = 256; // 256 × 4KB = 1MB
// Desktop app with 8GB RAM, allow 100MB for ghost memory
const size_t MAX_PHYSICAL_PAGES = 25600; // 25600 × 4KB = 100MBHeader: ghostmem/Version.h
#define GHOSTMEM_VERSION_MAJOR 0
#define GHOSTMEM_VERSION_MINOR 9
#define GHOSTMEM_VERSION_PATCH 0Returns the major version number.
Returns the minor version number.
Returns the patch version number.
Returns the version as a string (e.g., "0.9.0").
Returns the version as an integer (e.g., 900 for version 0.9.0).
Example:
#include "ghostmem/Version.h"
std::cout << "GhostMem version: "
<< GhostMem::GetVersionString() << std::endl;AllocateGhost() returns nullptr when:
- Virtual memory reservation fails (OS limit reached)
- Out of address space
Check for nullptr:
void* ptr = GhostMemoryManager::Instance().AllocateGhost(size);
if (ptr == nullptr) {
// Handle allocation failure
throw std::bad_alloc();
}Page faults are handled internally:
- Windows: Vectored exception handler
- Linux: SIGSEGV signal handler
If a page fault occurs outside ghost memory ranges:
- Windows: Exception continues to other handlers
- Linux: Process terminates with SIGSEGV
✅ Good use cases:
- Data with temporal locality (hot/cold access patterns)
- Compressible data (text, repeated values, sparse matrices)
- Memory-constrained environments
- Avoiding disk I/O
❌ Poor use cases:
- Uniform random access patterns (no cold data to compress)
- Already compressed data (JPEG, MP3, encrypted data)
- Real-time systems requiring guaranteed latency
- Small allocations (< 1 page overhead)
- Batch operations: Access related data together to keep pages hot
- Tune MAX_PHYSICAL_PAGES: Balance between physical RAM usage and page fault frequency
- Data locality: Structure data for sequential access when possible
- Compression-friendly formats: Use plain text, uncompressed formats where possible
| Feature | Windows | Linux |
|---|---|---|
| Virtual Memory API | VirtualAlloc/VirtualFree | mmap/munmap |
| Page Fault Handler | Vectored Exception Handler | SIGSEGV Signal Handler |
| Page Protection | PAGE_NOACCESS / PAGE_READWRITE | PROT_NONE / PROT_READ|PROT_WRITE |
| Thread Safety | Yes (recursive_mutex) | Yes (recursive_mutex) |
| Signal Safety | N/A |
- Getting Started Guide - Quick start with examples
- Integration Guide - How to use GhostMem in your project
- Thread Safety - Detailed thread safety documentation
- Main README - Project overview and quick start