Альтернатива для сборщика "мусора"

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

Я работаю над безопасным языком (т.е. языком без висячих указателей, проверяя границы, и т.д.), и сборка "мусора" или ее альтернатива должны использоваться.

14
задан nbro 8 September 2017 в 23:00
поделиться

7 ответов

Я подозреваю , что вам лучше всего будет придерживаться сборки мусора (согласно JVM), если у вас нет очень веской причины в противном случае. Современные ГХ чрезвычайно быстры, универсальны и безопасны. Если вы не можете спроектировать свой язык так, чтобы использовать преимущества очень специфического особого случая (как в одном из вышеупомянутых распределителей), вы вряд ли превзойдете JVM.

Единственная действительно веская причина, по которой я сейчас вижу аргумент против современных сборщиков мусора, - это проблемы с задержкой, вызванные паузами сборщика мусора. Они небольшие, редкие и не являются проблемой для большинства целей (например, я успешно написал 3D-движки на Java), но они все же могут вызывать проблемы в очень жестких ситуациях в реальном времени.

При этом все же могут быть некоторые особые случаи, когда другая схема распределения памяти может иметь смысл, поэтому я перечислил несколько интересных вариантов ниже:

Примером очень быстрого специализированного подхода к управлению памятью является Распределитель «на кадр» , используемый во многих играх. Это работает путем увеличения одного указателя для выделения памяти, и в конце периода времени (обычно визуального «кадра») все объекты сразу отбрасываются, просто устанавливая указатель обратно на базовый адрес и перезаписывая их при следующем выделении. . Это может быть «безопасно», однако ограничения на время жизни объекта будут очень строгими. Может быть победителем, если вы можете гарантировать, что вся выделенная память ограничена по размеру и действительна только для объема обработки, например. один запрос к серверу.

Другой очень быстрый подход - иметь выделенные пулы объектов для разных классов объектов.Выпущенные объекты можно просто повторно использовать в пуле, используя что-то вроде связанного списка свободных слотов для объектов. Операционные системы часто использовали такой подход для общих структур данных. Опять же, вам нужно следить за временем жизни объекта и явно обрабатывать удаление, возвращая объекты в пул.

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

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

Классический malloc / free работает довольно быстро, и его можно сделать безопасным, если у вас есть достаточные ограничения на создание и время жизни объекта, которые вы можете установить на своем языке. Примером может быть, если, например, вы наложили значительные ограничения на использование указателей.

В любом случае - надеюсь, это полезная пища для размышлений!

15
ответ дан 1 December 2019 в 13:21
поделиться

В C ++ можно сделать выделение кучи ОДИН РАЗ для ваших объектов, а затем повторно использовать эту память для последующих объектов, я видел, как это работает, и это было невероятно быстро .

Это применимо только к определенному набору проблем, и сделать это правильно сложно, но возможно.

Одна из радостей C ++ заключается в том, что у вас есть полный контроль над управлением памятью, вы можете решить использовать классические команды new / delete или реализовать собственный подсчет ссылок или сборку мусора.

Однако - вот драконы - вам действительно, действительно нужно знать, что вы делаете.

1
ответ дан 1 December 2019 в 13:21
поделиться

Если скорость имеет значение, а память - нет, то самая быстрая и простая стратегия распределения - никогда не освобождать. Выделение - это просто поднятие указателя вверх. Вы не можете быть быстрее этого.

Конечно, если ничего не выпускать, это может привести к переполнению доступной памяти. Очень редко память действительно «не важна». Обычно имеется большой, но конечный объем доступной памяти. Одна из стратегий называется «распределением по регионам». А именно, вы распределяете память в нескольких больших блоках, называемых «регионами», с помощью стратегии смещения указателя. Выпуск происходит только по целым регионам. Эта стратегия может быть применена с некоторым успехом, если рассматриваемая проблема может быть структурирована в последовательные «задачи», каждая из которых имеет свой собственный регион.

Для более общих решений, если вы хотите распределение в реальном времени (т. Е. Гарантированные ограничения на время ответа от запросов на выделение памяти), тогда сборка мусора - это правильный выбор. Сборщик мусора в реальном времени может выглядеть так: объекты выделяются с помощью стратегии смещения указателя. Кроме того, при каждом выделении памяти распределитель выполняет небольшую сборку мусора, при которой «живые» объекты копируются в другое место. В некотором смысле сборщик мусора работает «одновременно» с приложением. Это подразумевает небольшую дополнительную работу для доступа к объектам, потому что вы не можете переместить объект и обновить все указатели, чтобы они указывали на новое местоположение объекта, сохраняя при этом обещание «реального времени». Решения могут включать барьеров , например дополнительное косвенное обращение. Сборщик мусора поколений обеспечивает беспрепятственный доступ к большинству объектов, сохраняя при этом время паузы в строгих пределах.

Эту статью необходимо прочитать всем, кто хочет изучить распределение памяти, в частности сборку мусора.

4
ответ дан 1 December 2019 в 13:21
поделиться

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

-1
ответ дан 1 December 2019 в 13:21
поделиться

Если память не имеет значения, то применимо то, что говорит @Thomas. Учитывая гигантские объемы памяти современного оборудования, это вполне может быть жизнеспособным вариантом - все зависит от процесса.

Ручное управление памятью не обязательно решает ваши проблемы напрямую, но оно дает вам полный контроль над тем, КОГДА происходят события с памятью. Generic malloc, например, не является операцией O(1). Она делает всевозможные потенциально ужасные вещи, как в куче, управляемой самим malloc, так и операционной системой. Например, никогда не знаешь, когда "malloc(10)" может привести к тому, что виртуальная машина выгрузит что-то на страницу, и теперь ваши 10 байт оперативной памяти имеют неизвестный компонент дискового ввода-вывода - упс! Что еще хуже, эта страница может оказаться ВАШЕЙ памятью, которую вам нужно будет немедленно вернуть обратно! Теперь c = *p - это удар по диску. УРА!

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

С системой GC у вас может быть похожая возможность - это зависит от коллектора. Я не думаю, что в Sun JVM, например, есть возможность "отключения" на короткие периоды времени. Но если вы работаете с предварительно выделенными структурами и вызываете весь свой собственный код (или точно знаете, что происходит в библиотечной рутине, которую вы вызываете), у вас, вероятно, есть хорошие шансы не задеть менеджер памяти.

Потому что суть дела в том, что управление памятью - это большая работа. Если вы хотите избавиться от управления памятью, пишите на старом добром FORTRAN с ARRAYs и COMMON блоками (одна из причин, почему FORTRAN может быть таким быстрым). Конечно, вы можете написать "FORTRAN" почти на любом языке.

С современными языками, современными GC и т.д. управление памятью было отодвинуто на второй план и стало проблемой "10%". Сейчас мы довольно небрежно относимся к созданию мусора, копированию памяти и т.д. и т.п., потому что GC и т.д. облегчают нам задачу. И для 90% программ это не является проблемой, поэтому мы не беспокоимся об этом. Сейчас это проблема настройки, на поздних стадиях процесса.

Так что лучший вариант - установить все сразу, использовать, а потом выбросить. Часть "использовать" - это то, где вы получите последовательные, надежные результаты (при условии достаточного количества памяти в системе, конечно).

0
ответ дан 1 December 2019 в 13:21
поделиться

Это распространенное заблуждение, что управляемые языки не подходят для высокопроизводительных сценариев с низкой задержкой. Да, при ограниченных ресурсах (таких как встроенная платформа) и небрежном программировании вы можете прострелить себе ногу так же эффектно, как и в случае с C++ (и это может быть ОЧЕНЬ ОЧЕНЬ эффектно).

Эта проблема возникла при разработке игр на Java/C#, и решением было использовать пул памяти и не позволять объектам умирать, следовательно, не нужно запускать сборщик мусора, когда вы этого не ожидаете. На самом деле это тот же подход, что и в неуправляемых системах с низкой задержкой - ТРЕБОВАТЬ РЕАЛЬНО ОЧЕНЬ ТРУДНО НЕ РАСПРЕДЕЛЯТЬ ПАМЯТЬ.

Итак, учитывая тот факт, что реализация такой системы на Java/C# очень похожа на C++, преимущество в том, что вы делаете это по-мужски (управляемо), у вас есть "приятность" других особенностей языка, которые освобождают ваши умственные циклы, чтобы сосредоточиться на важных вещах.

-1
ответ дан 1 December 2019 в 13:21
поделиться

В качестве «альтернативы» сборке мусора C ++ имеет специальные указатели. boost :: shared_ptr <> (или std :: tr1 :: shared_ptr <>) работает точно так же, как сборка мусора с подсчетом ссылок в Python. На мой взгляд, shared_ptr - это сборка мусора. (хотя вам может потребоваться сделать несколько вещей weak_ptr <>, чтобы убедиться, что циклические ссылки не возникают)

Я бы сказал, что auto_ptr <> (или в C ++ 0x, unique_ptr <> ...) представляет собой жизнеспособную альтернативу со своим набором преимуществ и недостатков. Auto_ptr имеет неуклюжий синтаксис и не может использоваться в контейнерах STL ... но он выполняет свою работу. Во время компиляции вы «перемещаете» владение указателем с переменной на переменную. Если переменная владеет указателем, когда она выходит за пределы области видимости, она вызывает свой деструктор и освобождает память. Только один auto_ptr <> (или unique_ptr <>) может владеть настоящим указателем. (по крайней мере, если правильно использовать).

В качестве другой альтернативы вы можете хранить все в стеке и просто передавать ссылки на все нужные вам функции.

Эти альтернативы на самом деле не решают общую проблему управления памятью, которую решает сборка мусора. Тем не менее, они эффективны и хорошо протестированы. Auto_ptr не использует больше места, чем изначально указывал указатель ... и нет накладных расходов на разыменование auto_ptr. «Движение» (или присваивание в Auto_ptr) связано с небольшими накладными расходами, чтобы отслеживать владельца. Я не проводил никаких тестов, но уверен, что они быстрее, чем сборка мусора / shared_ptr.

1
ответ дан 1 December 2019 в 13:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: