Как сборщики мусора быстрее, чем явное освобождение памяти?

Я читал этот html, сгенерированный (может истечь, Вот оригинальный файл ps.)

GC Myth 3: Сборщики мусора всегда медленнее, чем явное освобождение памяти.
GC Myth 4: сборщики мусора всегда быстрее, чем явное освобождение памяти.

Это был большой WTF для меня. Как GC будет быстрее, чем явное освобождение памяти? не является ли это по существу вызовом явного освобождения памяти, когда он освобождает память/снова делает ее для использования? так.... wtf.... что это на самом деле означает?

Очень маленькие объекты и большие разреженные heaps == > GC обычно дешевле, особенно с потоками

Я до сих пор этого не понимаю. Его, как говорят, С++ быстрее, чем машинный код (если вы не понимаете wtf в этом предложении, остановите программирование. Пусть начнется -1). После быстрого google один источник предложил его быстрее, когда у вас много памяти. Я думаю, что это означает, что это не позаботится о свободе вообще. Уверен, что это может быть быстро, и я написал собственный распределитель, который делает именно это, а не бесплатно (void free(void*p){}) в ОДНОМ приложении, которое не освобождает какие-либо объекты (оно только освобождается в конце, когда оно завершается), и имеет определение в основном в случае libs и что-то вроде stl. Итак... я уверен, что это будет быстрее GC. Если я все еще хочу освободить, я думаю, я могу использовать распределитель, который использует deque или свою собственную реализацию, которая существенно

if (freeptr < someaddr) {
    *freeptr=ptr;
    ++freeptr; 
}
else
{
    freestuff();
    freeptr = freeptrroot;
}

который, я уверен, будет очень быстрым. Я уже ответил на мой вопрос. Дело в том, что коллекционер GC никогда не вызывается, так это будет быстрее, но... я уверен, что это не то, что означает документ, поскольку он упоминает два сборщика в своем тесте. я уверен, что то же самое приложение будет медленнее, если GC collector будет вызван даже однажды, независимо от того, какой GC используется. Если его известно, что он никогда не нужен, тогда пустое свободное тело может использоваться как одно приложение, которое у меня было.

В любом случае, я отправляю этот вопрос для дальнейшего понимания.

Ответ 1

Как GC быстрее, чем явное освобождение памяти?

  • GC могут размещать указатель-помеху в потоковом локальном генерации, а затем полагаться на копирование коллекции для обработки (относительно) необычного случая эвакуации оставшихся в живых. Традиционные распределители, такие как malloc, часто конкурируют за глобальные блокировки и деревья поиска.

  • GC могут освобождать многие мертвые блоки одновременно, сбросив буфер локального распределения потоков вместо вызова free для каждого блока по очереди, то есть O (1) вместо O (n).

  • Компактируя старые блоки, поэтому их количество вписывается в каждую строку кэша. Улучшенная локальность повышает эффективность кеширования.

  • Используя дополнительную статическую информацию, такую ​​как неизменяемые типы.

  • Используя дополнительную динамическую информацию, такую ​​как изменяющаяся топология кучи, посредством данных, записанных барьером записи.

  • Используя более эффективные методы, приемлемые, например. устраняя головную боль от ручного управления памятью от алгоритмов ожидания.

  • Отложив выделение на более подходящее время или выгрузив его в другое ядро. (спасибо Эндрю Хилу за эту идею!)

Ответ 2

Один из способов сделать GC быстрее, чем явное освобождение, заключается в том, чтобы разблокировать неявное: куча разделяется на разделы, а VM время от времени переключается между разделами (когда раздел становится слишком полным, например). Живые объекты копируются в новый раздел, и все мертвые объекты не освобождаются - их просто забывают. Таким образом, само освобождение не стоит ничего. Дополнительным преимуществом такого подхода является то, что дефрагментация кучи является бесплатным бонусом.

Обратите внимание, что это очень общее описание фактических процессов.

Ответ 3

Фокус в том, что базовый распределитель для сборщика мусора может быть намного проще, чем явный, и использовать некоторые ярлыки, которые явные не могут.

  • Если копирайтер копирует (java и .net, ocaml и haskell runtimes и многие другие фактически используют один), освобождение выполняется в больших блоках, а выделение - только шаг указателя, а стоимость выплачивается за каждый объект, оставшийся в живых коллекциях. Таким образом, это происходит быстрее, особенно когда есть много короткоживущих временных объектов, что довольно часто встречается на этих языках.
  • Даже для не копирующего коллектора (например, для Boehm) тот факт, что объекты освобождаются партиями, экономит много работы по объединению смежных свободных кусков. Поэтому, если сбор не нужно запускать слишком часто, он может быть быстрее.
  • И, ну, многие стандартные библиотеки malloc/free реализаций просто сосут. Вот почему существуют такие проекты, как umem и библиотеки, такие как glib имеют собственную облегченную версию.

Ответ 4

Фактор, который еще не упоминается, заключается в том, что при использовании распределения памяти вручную, даже если ссылки на объекты гарантируют, что они не образуют циклы, определение того, когда последний объект для хранения ссылки оставил, может быть дорогостоящим, что обычно требует использования счетчиков ссылок, списки ссылок или другие способы отслеживания использования объекта. Такие методы не так уж плохи для однопроцессорных систем, где стоимость атомного приращения может быть практически такой же, как и обычная, но они очень сильно ухудшаются на многопроцессорных системах, где операции с атомным приращением сравнительно дороги.