Можливо, ви захочете відступити назад і побачити, звідки і чому беруться ці існуючі моделі. Коли процес створюється, йому просто надається плоска область зберігання, яка просто індексується від 0 до N. Оскільки ця область зберігання (тут йдеться про оперативну пам’ять) підтримується спеціальним обладнанням та деякими химерними напівпровідниками, це відбувається досить швидко, але це не єдиний у своєму роді. Інші пристрої, такі як жорсткі диски, це по суті те саме, плоский простір, який можна адресувати індексом, але на багато порядків повільніше.
Причина існування "купи" полягає в тому, що для кожної програми було б недоцільно намагатися керувати використанням оперативної пам'яті самостійно. Зрештою, саме так сталося, програмісти достроково планували, для чого використовуватиметься кожне місце оперативної пам'яті. Оскільки програмне забезпечення стало складнішим, хтось сказав, чи не було б добре, якби я міг просто зайти до якоїсь чорної скриньки і сказати "мені потрібно 10 байтів, щоб придумати", і не потрібно турбуватися про всі складні деталі, де і як ці 10 байт походять від того, як їх відновлюють. Ось, що це купа, насправді не стає більш простим, ніж це.
Кожен раз, коли створюється потік, є деякі структури даних (і стек), які отримуються за допомогою тієї самої операції gimme, яку я тільки що описав. Стек майже універсально використовується, тому що він ідеально підходить до кадрів стека функціональних викликів та їх природи LIFO. Теоретично кожне виклик функції та локальні змінні можуть бути розподілені на купі, але це було б занадто дорого, порівняно з лише кількома інструкціями зі збирання, необхідними для оновлення реєстру покажчика стека (ESP на x86).
Місцевий накопичувач ниток (TLS) також побудований поверх купи. Коли нитка створюється в рамках поїздки до купи, щоб виділити пам'ять для структур управління, окремий простір для TLS також виділяється з купи.
Отже, зрештою, все, що у вас є насправді, - це загальний розподільник пам'яті (тобто купа), а все інше - це спеціалізована форма. Іншими словами, якщо ви готові відмовитись від якогось аспекту "я хочу виділити стільки (або як мало), скільки я хочу, зберігайте його стільки, скільки я хочу, і безкоштовно, коли я хочу", ви можете піти з торгівлі вимкнути загальний розподільник купи для іншої моделі, яка пропонує швидкість, але ціною якогось іншого обмеження.
Візьміть стек. Це надзвичайно швидко в порівнянні з купою, але два компроміси - 1) ви не контролюєте, коли звільняється пам'ять; замість того, як тільки функція завершується, все, що ви виділили, вже немає і 2), оскільки стеки, як правило, обмежені за розміром, вам слід обережно розподіляти велику кількість даних безпосередньо у стеку.
Інший тип «моделі пам’яті» - це менеджер віртуальної пам’яті (VMM), який пропонується майже всіма основними ОС через системні дзвінки. VMM дуже схожий на купу в тому сенсі, що ви можете попросити будь-яку кількість пам'яті і зберегти її скільки завгодно. Однак обмеження полягає в тому, що ви можете виділяти пам'ять лише у кратних розмірах сторінки (наприклад, 4 КБ), тому використання VMM безпосередньо спричинить великі накладні витрати в типовому додатку, який часто виділяє 8-24 байти одночасно. Насправді, майже кожна реалізація купи будується на основі VMM спеціально для того, щоб забезпечити дуже загальний, неспеціалізований розподіл з невеликим блоком. Купа переходить до VMM кожного разу, коли їй потрібно більше пам’яті, а потім виділяє в програму багато невеликих шматочків цієї пам’яті.
Якщо у вас є додаток, який потребує розподілу великих блоків, ви можете розглянути можливість прямого переходу до VMM, хоча деякі купи мають вираз if всередині malloc (), і якщо розмір блоку перевищує деякий поріг, вони просто переходять до VMM для вас.
Іншою формою алокаторів замість того, щоб безпосередньо використовувати купу, будуть пули. Пул - це спеціалізований розподільник, де всі блоки однакового розміру. Басейни (так само, як стек і TLS) будуються поверх купи або VMM. Басейни корисні в місцях, де ви виділяєте багато (мільйони) короткочасних, невеликих об’єктів однакового розміру. Подумайте про послугу мережі, що обробляє вхідні запити. Кожен клієнтський запит може призвести до того, що для обробки запиту буде виділена одна і та ж байтна структура. Компроміс із використанням пулів полягає в тому, що кожен пул обробляє лише один розмір блоку (але ви можете створити кілька пулів). Перевага пулів полягає в тому, що оскільки всі об'єкти однакового розміру, йому не потрібна складна логіка. Натомість, коли вам потрібен новий блок, він просто дає вам той, який нещодавно був звільнений.
І, нарешті, пам’ятайте ту річ на жорсткому диску, про яку я згадував. У вас може бути модель пам'яті, яка поводиться як файлова система і дублює ту саму ідею записів каталогів і i-вузлів, щоб дозволити вам ієрархічно розподіляти блоки даних, де кожен блок даних відповідає адресі шляху. Саме це робить tmpfs .
Окрім речей, про які я згадував, я впевнений, що є інші більш спеціалізовані моделі, але врешті-решт, оскільки все базується на просторі адресного простору (тобто, поки деякі генуї не придумають якийсь дивний - не плоский простір $$ ), все повертається до того загального розподільника "gimme", який є або VMM, або купу.