Возможно, вы захотите сделать шаг назад и посмотреть, откуда и почему появились эти существующие модели. Когда процесс создается, ему просто дается плоская область хранения, которая просто индексируется от 0 до N. Поскольку эта область хранения (здесь речь идет об ОЗУ) поддерживается специальным оборудованием и некоторыми модными полупроводниками, это происходит довольно быстро, но это не единственный в своем роде. Другие устройства, такие как жесткие диски, по сути одно и то же, плоское пространство, адресуемое индексом, но на много порядков медленнее.
Причина, по которой существует «куча», заключается в том, что для каждого приложения было бы нецелесообразно пытаться самостоятельно управлять использованием ОЗУ. Еще в тот день, именно так и происходило, программисты заранее планировали, для чего будет использоваться каждая ячейка памяти. По мере того, как программное обеспечение становилось все более сложным, кто-то говорил, не было бы неплохо, если бы я просто пошел к черному ящику и сказал: «Мне нужно 10 байтов, так что дай мне», и мне не нужно было бы беспокоиться обо всех сложных деталях, где и как эти 10 байтов приходят или как они исправлены. Вот что такое куча, на самом деле не получается более простой, чем эта.
Каждый раз, когда создается поток, существуют некоторые структуры данных (и стек), которые получаются с использованием той же «операции gimme», которую я только что описал. Стек используется практически повсеместно, потому что он идеально подходит для фреймов стека вызовов функций и их природы LIFO. Теоретически, каждый вызов функции и локальные переменные могут быть размещены в куче, но это будет слишком дорого по сравнению с несколькими инструкциями по сборке, необходимыми для обновления регистра указателя стека (ESP на x86).
Локальное хранилище потоков (TLS) также строится поверх кучи. Когда поток создается, как часть поездки в кучу для выделения памяти для структур управления, отдельное пространство для TLS также выделяется из кучи.
Таким образом, в конце концов, все, что у вас есть, - это общий распределитель памяти (то есть куча), а все остальное - это специализированная форма. Другими словами, если вы готовы отказаться от какого-то аспекта «я хочу выделять столько (или так мало), сколько хочу, сохраняйте его так долго, как я хочу, и бесплатно, когда я хочу», вы можете уйти от торговли от общего распределителя кучи для другой модели, которая предлагает скорость, но за счет некоторых других ограничений.
Возьми стек. Это невероятно быстро по сравнению с кучей, но есть два компромисса: 1) вы не контролируете, когда освобождается память; вместо этого, после выхода из функции, что бы вы ни выделяли, и 2) поскольку стеки обычно имеют ограниченный размер, вы должны быть осторожны при размещении больших объемов данных непосредственно в стеке.
Другим типом «модели памяти» является Virtual Memory Manager (VMM), предлагаемый практически каждой крупной ОС через системные вызовы. VMM очень похож на кучу в том смысле, что вы можете запросить любой объем памяти и хранить его столько, сколько хотите. Тем не менее, ограничение заключается в том, что вы можете выделять память только в кратных размерах страниц (например, 4 КБ), поэтому непосредственное использование VMM приведет к большим издержкам в типичном приложении, которое часто выделяет 8-24 байта за раз. Фактически, почти каждая реализация кучи построена поверх VMM специально для целей обеспечения очень общего, специализированного, небольшого выделения блоков. Куча отправляется в VMM всякий раз, когда ей требуется больше памяти, а затем выдает приложению множество маленьких кусочков этой памяти.
Если у вас есть приложение, которое нуждается в выделении больших блоков, вы можете рассмотреть возможность перехода непосредственно к VMM, хотя в некоторых кучах есть оператор if внутри malloc (), и если размер блока превышает некоторый порог, они просто переходят к VMM. для вас.
Другой формой распределителей, вместо непосредственного использования кучи, были бы пулы. Пул - это специализированный распределитель, в котором все блоки имеют одинаковый размер. Пулы (как и стек и TLS) создаются поверх кучи или VMM. Пулы полезны в местах, где вы выделяете много (миллионы) недолговечных небольших объектов одинакового размера. Представьте себе сетевой сервис, обрабатывающий входящие запросы. Каждый клиентский запрос может привести к тому, что одна и та же N-байтовая структура будет выделена для обработки этого запроса. Обмен с использованием пулов заключается в том, что каждый пул обрабатывает только один размер блока (но вы можете создать несколько пулов). Преимущество пулов состоит в том, что, поскольку все объекты имеют одинаковый размер, это не требует сложной логики. Вместо этого всякий раз, когда вам нужен новый блок, он просто дает вам тот, который был недавно освобожден.
И, наконец, помните ту вещь о жестком диске, о которой я упоминал, наверху. У вас может быть модель памяти, которая ведет себя как файловая система и дублирует ту же идею записей каталога и i-узлов, что позволяет вам иерархически распределять блоки данных, где каждый блок данных соответствует адресу с путем. Это именно то, что делает tmpfs .
Помимо того, что я упомянул, я уверен, что есть и другие, более специализированные модели, но, в конце концов, поскольку все основано на плоском адресном пространстве (до тех пор, пока некоторые genuis не придумают какое-то странное-$$ неплоское пространство) ), все это восходит к тому универсальному распределителю «gimme», который является либо VMM, либо кучей.