Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

Как "альтернативный" сбор мусора, С++ специально имеет интеллектуальные указатели. boost:: shared_ptr < > (или std:: tr1:: shared_ptr < > ) работает точно так же, как подсчитанная сборка мусора с помощью Python. На мой взгляд, сборщик мусора shared_ptr IS. (хотя вам может понадобиться сделать несколько слабых_файлов, чтобы убедиться, что циклические ссылки не выполняются)

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

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

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

Ответ 5

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

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

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

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

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

С современными языками, современными GC и т.д. управление памятью было отброшено и стало проблемой "10%". Теперь мы довольно неряшливы, создавая мусор, копируя память и т.д. И т.д., Потому что GCs и др. Облегчают нам неаккуратность. И для 90% программ это не проблема, поэтому мы не волнуемся. В настоящее время это проблема настройки, в конце процесса.

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

Ответ 6

Если вы действительно не хотите никаких пауз вообще, запретите все распределение памяти, кроме распределения стека, буферов на основе регионов и статического распределения. Несмотря на то, что вам сказали, malloc() может привести к серьезным паузам, если бесплатный список будет фрагментирован, и если вы часто обнаруживаете, что создаете массивные графы объектов, наивное бесплатное руководство может потерять и прекратить копирование; единственный способ действительно избежать этого - это амортизировать по предварительно выделенным страницам, таким как стек или пул, распределенный по пулам, который освобождается сразу. Я не знаю, насколько это полезно, но я знаю, что собственный графический язык программирования LabVIEW по умолчанию выделяет статическую область памяти для каждого подпрограммы-эквивалента, требуя от программистов вручную включить распределение стека; это то, что полезно в условиях жесткого реального времени, где вам нужны абсолютные гарантии использования памяти.

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

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

Ответ 7

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

Ответ 8

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

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

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