Как сборщики мусора могут быть быстрее, чем явное освобождение памяти? - PullRequest
11 голосов
/ 28 апреля 2011

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

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

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

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

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

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

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

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

Ответы [ 4 ]

28 голосов
/ 14 января 2012

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

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

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

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

  4. Использование дополнительных статических данных, таких как неизменяемые типы.

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

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

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

15 голосов
/ 28 апреля 2011

Один из способов сделать GC быстрее, чем явное освобождение, - это неявное освобождение:

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

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

11 голосов
/ 28 апреля 2011

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

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

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

...