Last active
September 24, 2025 17:02
-
-
Save ZacharyThompson/0ca99f73b9a3a02d1e1cc00afc97fbe8 to your computer and use it in GitHub Desktop.
Example of reserving a very large amount of virtual address space in linux. You may want to do this when, for example, implementing an arena allocator in order to easily extend the capacity. As long as you don't reserve anything near 256 TiB (on a 64 bit system) there should be little downside afaik.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <stdint.h> | |
#include <sys/mman.h> | |
#include <string.h> | |
#define KIBI(x) (1024LL*(x)) | |
#define MEBI(x) (1024LL*1024LL*(x)) | |
#define GIBI(x) (1024LL*1024LL*1024LL*(x)) | |
#define ASSERT(x) do {if (!(x)) { fprintf(stderr, "assert failed: %s:%d: %s\n", __FILE__, __LINE__, #x); exit(1); } } while(0) | |
#define PRINTSLEEP(x) \ | |
do { \ | |
printf("sleeping for %d seconds\n", x); \ | |
for (uint32_t i = x; i != 0; i--) { \ | |
printf("%d...\n", i); \ | |
sleep(1); \ | |
} \ | |
printf("\n0\n"); \ | |
} while(0) | |
int main(void) { | |
// Reserve a very large amount of virtual memory. | |
// The total address space is 256 Tebibytes (~281 Terabytes) large, so 50 GiB is basically nothing. | |
const size_t bufferSize = GIBI(50); | |
// The PROT_NONE flag in mmap is the key to reserving such a large amount of memory without causing an error. | |
uint8_t *buffer = (uint8_t *)mmap(NULL, bufferSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
ASSERT(buffer != MAP_FAILED); | |
// Tell the OS that we are going to use 1 GiB of the memory we reserved. | |
const size_t myStringSize = GIBI(1); | |
uint8_t *uncomittedStart = buffer; | |
// sizeof(char) is 1, but I'm gonna include it anyways. | |
ASSERT(mprotect(uncomittedStart, myStringSize * sizeof(char), PROT_READ | PROT_WRITE) == 0); // mprotect returns 0 on success. | |
char *myString = (char *)uncomittedStart; | |
uncomittedStart += myStringSize / sizeof(char); | |
// Experiment: write to all of the string buffer, but only read from 1/100th of it. | |
// How much memory will be used? | |
// Run `watch free --kilo` in the background to see. Look at the 'used' column. | |
for (size_t i = 0; i < myStringSize; i++) { | |
myString[i] = 'a' + (i%26); | |
} | |
PRINTSLEEP(5); | |
myString[myStringSize/100] = '\0'; | |
printf("String length: %zd\n", strlen(myString)); | |
printf("%s\n", myString); | |
// I technically don't need to call madvise or munmap here since the program | |
// is gonna exit right after, but I'll do it anyways. | |
// Tell the OS that we don't need this memory anymore. | |
ASSERT(madvise(myString, myStringSize*sizeof(char), MADV_DONTNEED) == 0); | |
PRINTSLEEP(5); // look at `watch free --kilo` again. Memory used should go back to normal. | |
ASSERT(munmap(buffer, bufferSize) == 0); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment