Почему объекты Java не удаляются сразу после того, как на них больше нет ссылок?

77

В Java, как только у объекта больше нет ссылок, он становится пригодным для удаления, но JVM решает, когда объект фактически удален. Чтобы использовать терминологию Objective C, все ссылки на Java по своей природе являются "сильными". Однако в Objective-C, если у объекта больше нет сильных ссылок, объект немедленно удаляется. Почему это не так в Java?

moonman239
источник
46
Вас не должно волновать, когда объекты Java действительно удаляются. Это деталь реализации.
Василий Старынкевич,
154
@BasileStarynkevitch Вы должны абсолютно заботиться и проверить, как работает ваша система / платформа. Задавать вопросы «как» и «почему» - это один из лучших способов стать лучшим программистом (и, в более широком смысле, более умным человеком).
Артур Бисиадовский
6
Что Цель C делает, когда есть круговые ссылки? Я полагаю, это просто утечка их?
Мердад
45
@ArturBiesiadowksi: Нет, спецификация Java не сообщает, когда объект удаляется (и аналогично для R5RS ). Вы могли бы и, вероятно, должны разрабатывать свою программу на Java так, как если бы такого удаления никогда не происходило (а для недолговечных процессов, таких как мир приветствия Java, этого действительно не происходит). Вы можете заботиться о наборе живых объектов (или потреблении памяти), что является другой историей.
Василий Старынкевич
28
Однажды новичок сказал мастеру: «У меня есть решение нашей проблемы распределения. Мы дадим каждому распределению счетчик ссылок, а когда оно достигнет нуля, мы можем удалить объект». Мастер ответил: «Однажды новичок сказал мастеру:« У меня есть решение ... »
Эрик Липперт,

Ответы:

79

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

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

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

Карл Билефельдт
источник
58
Жена программиста отправляет его в супермаркет. Она говорит ему: «Купи буханку хлеба, а если увидишь яйца, возьми дюжину». Позже программист возвращается с дюжиной буханок хлеба под мышкой.
Нил
7
Я предлагаю упомянуть, что время gc нового поколения, как правило, пропорционально количеству живых объектов, поэтому большее количество удаленных объектов означает, что во многих случаях их стоимость вообще не будет оплачена. Удалить так же просто, как перевернуть указатель на оставшийся в живых объект и, при необходимости, обнулить все пространство памяти в одном большом наборе памяти (не уверен, что это сделано в конце gc или амортизировано при выделении таблиц или самих объектов в текущих jvms)
Артур Бисиадовский
64
@ Нейл, разве это не должно быть 13 хлебов?
JAD
67
«Выключен на одну ошибку на проходе 7»
joeytwiddle
13
@JAD Я бы сказал 13, но большинство не склонны это понимать. ;)
Нил
86

Потому что правильно знать что-то, на которое больше не ссылаются, нелегко. Даже близко не легко.

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

как зовут
источник
7
Или вы можете использовать подход Python, когда вы используете как можно больше пересчетов, прибегая к GC, когда ожидаете утечки памяти из-за циклических зависимостей. Я не понимаю, почему они не могли пересчитать в дополнение к GC?
Мердад
27
@ Mehrdad Они могли. Но, вероятно, это будет медленнее. Ничто не мешает вам реализовать это, но не ожидайте, что вы превзойдете GC в Hotspot или OpenJ9.
Иосиф
21
@ jpmc26, потому что если вы удаляете объекты, как только они больше не используются, высока вероятность того, что вы удалите их в ситуации высокой нагрузки, которая еще больше увеличивает нагрузку. GC может работать при меньшей нагрузке. Подсчет ссылок сам по себе является небольшой накладной на каждую ссылку. Также с помощью GC вы можете часто отбрасывать большую часть памяти без ссылок, не обрабатывая отдельные объекты.
Иосиф
33
@Josef: правильный подсчет ссылок тоже не бесплатный; Обновление счетчика ссылок требует атомарных приращений / уменьшений, что удивительно дорого , особенно в современных многоядерных архитектурах. В CPython это не является большой проблемой (CPython сам по себе очень медленный, а GIL ограничивает свою многопоточную производительность до одноядерных уровней), но на более быстром языке, который также поддерживает параллелизм, это может быть проблемой. Не случайно PyPy полностью избавляется от подсчета ссылок и просто использует GC.
Matteo Italia
10
@ Mehrdad, как только вы внедрили GC для Java для подсчета ссылок, я с удовольствием протестирую его, чтобы найти случай, когда он работает хуже, чем любая другая реализация GC.
Иосиф
45

AFAIK, спецификация JVM (написана на английском языке) не упоминает, когда именно объект (или значение) должен быть удален, и оставляет это для реализации (аналогично для R5RS ). Это как-то требует или предлагает сборщик мусора, но оставляет детали для реализации. И аналогично для спецификации Java.

Помните, что языки программирования - это спецификации ( синтаксиса , семантики и т. Д.), А не программные реализации. Язык как Java (или его JVM) имеет много реализаций. Его спецификация опубликована , доступна для скачивания (чтобы вы могли ее изучить) и написана на английском языке. §2.5.3 В куче спецификации JVM упоминается сборщик мусора:

Куча хранилища для объектов восстанавливается автоматической системой управления хранилищем (известной как сборщик мусора); объекты никогда не освобождаются явно. Виртуальная машина Java не предполагает конкретного типа автоматической системы управления хранением

(акцент мой; финализация BTW упоминается в §12.6 спецификации Java, а модель памяти - в §17.4 спецификации Java)

Поэтому (в Java) вас не должно волновать, когда объект удаляется , и вы можете кодировать как-будто это не происходит (рассуждая в абстракции, где вы игнорируете это). Конечно, вам нужно заботиться о потреблении памяти и множестве живых объектов, это другой вопрос. В нескольких простых случаях (например, программа «Здравствуй, мир») вы можете доказать - или убедить себя - что выделенная память довольно мала (например, меньше гигабайта), и тогда вам все равно удаление отдельных объектов. В большем количестве случаев вы можете убедить себя, что живые объекты(или достижимые, которые являются надмножеством - проще для рассуждений о живых) никогда не превышают разумный предел (и тогда вы полагаетесь на GC, но вам все равно, как и когда происходит сборка мусора). Читайте о космической сложности .

Я предполагаю, что в нескольких реализациях JVM, работающих с недолговечной Java-программой, такой как hello world, сборщик мусора вообще не запускается и удаление не происходит. AFAIU, такое поведение соответствует многочисленным спецификациям Java.

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

Если бы спецификация JVM требовала, чтобы каждый объект был удален точно как можно скорее (или просто наложил бы больше ограничений на удаление объекта), эффективные методы генерации GC были бы запрещены, и разработчики Java и JVM были бы мудры избегать этого.

Кстати, вполне возможно, что наивная JVM, которая никогда не удаляет объекты и не освобождает память, может соответствовать спецификациям (букве, а не духу) и, безусловно, способна на практике использовать мир приветствия (обратите внимание, что большинство крошечные и недолговечные Java-программы, вероятно, не выделяют более нескольких гигабайт памяти). Конечно, о такой JVM не стоит упоминать, и это просто игрушка (как в этой реализации mallocдля C). См. Epsilon NoOp GC для получения дополнительной информации. Реальные виртуальные машины Java являются очень сложными программными компонентами и сочетают в себе несколько методов сбора мусора.

Кроме того, Java отличается от JVM, и у вас есть реализации Java, работающие без JVM (например, опережающие компиляторы Java, среда выполнения Android ). В некоторых случаях (в основном академических) вы можете себе представить (так называемые методы «сборки мусора во время компиляции»), что Java-программа не выделяет и не удаляет во время выполнения (например, потому что оптимизирующий компилятор достаточно умен, чтобы использовать только стек вызовов и автоматические переменные ).

Почему объекты Java не удаляются сразу после того, как на них больше нет ссылок?

Потому что спецификации Java и JVM этого не требуют.


Прочтите руководство GC для получения дополнительной информации (и спецификации JVM ). Обратите внимание, что наличие объекта (или полезного для будущих вычислений) для объекта является свойством всей программы (немодульным).

Objective-C предпочитает подход подсчета ссылок к управлению памятью . И это также имеет подводные камни (например, Objective-C программист должен заботиться о циклических ссылках на expliciting слабых ссылок, но JVM обрабатывает циклические ссылки хорошо на практике , не требуя внимания со стороны программиста Java).

В программировании и разработке языков программирования не существует «Серебряной пули» (помните о проблеме остановки ; быть полезным живым объектом в целом неразрешимо ).

Вы также можете прочитать SICP , Прагматика языка программирования , Книга Дракона , Lisp In Small Pieces и Операционные системы: Три простых компонента . Они не о Java, но они откроют вам разум и должны помочь понять, что должна делать JVM и как она может практически работать (с другими компонентами) на вашем компьютере. Вы также можете потратить много месяцев (или несколько лет) на изучение сложного исходного кода существующих реализаций JVM с открытым исходным кодом (например, OpenJDK , который имеет несколько миллионов строк исходного кода).

Василий Старынкевич
источник
20
«Вполне возможно, что наивная JVM, которая никогда не удаляет объекты и не освобождает память, может соответствовать спецификациям». Это наверняка соответствует спецификации! В Java 11 фактически добавлен неиспользуемый сборщик мусора , среди прочего, для очень недолговечных программ.
Майкл
6
«Вам не важно, когда объект будет удален». Не согласен. Во-первых, вы должны знать, что RAII больше не является возможным шаблоном и что вы не можете зависеть ни от finalizeкакого управления ресурсами (файловых дескрипторов, соединений db, ресурсов gpu и т. Д.).
Александр
4
@Michael Это имеет смысл для пакетной обработки с использованным потолком памяти. ОС может просто сказать "вся память, используемая этой программой, теперь ушла!" в конце концов, что довольно быстро. Действительно, многие программы на C были написаны именно так, особенно в раннем мире Unix. У Паскаля было чудовищно ужасное «сбросить указатель стека / кучи на предварительно сохраненную контрольную точку», что позволило вам сделать почти то же самое, хотя это было довольно небезопасно - пометить, запустить подзадачу, сбросить.
Луаан
6
@ Александр в целом за пределами C ++ (и нескольких языков, которые намеренно унаследованы от него), предполагая, что RAII будет работать на основе только финализаторов, является анти-паттерном, который следует предупреждать и заменять явным блоком управления ресурсами. Весь смысл GC в том, что время жизни и ресурс, в конце концов, не связаны.
Леушенко
3
@Leushenko Я бы категорически не согласился с тем, что «время жизни и ресурс разделены» - это «весь смысл» GC. Это отрицательная цена, которую вы платите за основной смысл GC: простое и безопасное управление памятью. «Предполагается, что RAII будет работать только на основе финализаторов, это анти-паттерн» В Java? Может быть. Но не в CPython, Rust, Swift или Objective C. "Предупрежден против и заменен явным блоком управления ресурсами" Нет, они строго более ограничены. Объект, который управляет ресурсом через RAII, дает вам ручку, чтобы обойти ограниченную жизнь. Блок try-with-resource ограничен одной областью действия.
Александр
23

Чтобы использовать терминологию Objective C, все ссылки на Java по своей природе являются "сильными".

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

В Objective-C, если у объекта больше нет сильных ссылок, объект немедленно удаляется.

Это также не обязательно правильно - некоторые версии Objective C действительно использовали сборщик мусора поколений. В других версиях сборка мусора вообще отсутствовала.

Это правда, что в более новых версиях Objective C используется автоматический подсчет ссылок (ARC), а не GC на основе трассировки, и это (часто) приводит к тому, что объект «удаляется», когда этот счетчик ссылок достигает нуля. Однако обратите внимание, что реализация JVM также может быть совместимой и работать именно таким образом (черт возьми, она может быть совместимой и вообще не иметь GC).

Так почему же большинство реализаций JVM не делают этого, а вместо этого используют алгоритмы GC на основе трассировки?

Проще говоря, ARC не так утопичен, как кажется на первый взгляд:

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

Конечно, у ARC есть свои преимущества - его легко реализовать, а его сбор является детерминированным. Но вышеперечисленные недостатки, среди прочего, являются причиной того, что большинство реализаций JVM будут использовать GC, основанный на трассировке поколений.

berry120
источник
1
Самое смешное, что Apple переключилась на ARC именно потому, что увидела, что на практике она значительно превосходит другие GC (в частности, поколения). Честно говоря, это в основном верно для платформ с ограниченным объемом памяти (iPhone). Но я бы опроверг ваше утверждение о том, что «ARC не настолько утопичен, как кажется на первый взгляд», сказав, что поколения (и другие недетерминированные) GC не так утопичны, как кажется на первый взгляд: детерминированное уничтожение, вероятно, является лучшим вариантом в Подавляющее большинство сценариев.
Конрад Рудольф
3
@KonradRudolph, хотя я также являюсь большим поклонником детерминированного уничтожения, я не думаю, что «лучший вариант в подавляющем большинстве сценариев» оправдывает себя. Это, безусловно, лучший вариант, когда задержка или память важнее, чем средняя пропускная способность, и, в частности, когда логика достаточно проста. Но это не значит, что не так много сложных приложений, которые требуют много циклических ссылок и т. Д. И требуют быстрой усредненной работы, но на самом деле не заботятся о задержке и имеют много доступной памяти. Для них сомнительно, что ARC - хорошая идея.
оставил около
1
@leftaroundabout В «большинстве сценариев» ни пропускная способность, ни нагрузка на память не являются узким местом, поэтому в любом случае это не имеет значения. Ваш пример - один конкретный сценарий. Конечно, это не редкость, но я бы не стал утверждать, что это более распространено, чем в других сценариях, где ARC лучше подходит. Кроме того, ARC может иметь дело с циклами просто отлично. Это просто требует некоторого простого ручного вмешательства программиста. Это делает его менее идеальным, но вряд ли нарушает условия сделки. Я утверждаю, что детерминистическое завершение является гораздо более важной характеристикой, чем вы претендуете.
Конрад Рудольф
3
@KonradRudolph Если ARC требует простого ручного вмешательства со стороны программиста, то он не работает с циклами. Если вы начинаете интенсивно использовать двусвязные списки, то ARC переходит к ручному выделению памяти. Если у вас большие произвольные графы, ARC заставляет вас писать сборщик мусора. Аргумент GC заключается в том, что ресурсы, которые должны быть уничтожены, не являются задачей подсистемы памяти, и для отслеживания относительно немногих из них они должны быть окончательно детализированы с помощью простого ручного вмешательства программиста.
просфилаес
2
@KonradRudolph ARC и циклы в основном приводят к утечкам памяти, если они не обрабатываются вручную. В достаточно сложных системах могут произойти серьезные утечки, если, например, некоторый объект, хранящийся в карте, хранит ссылку на эту карту, изменение, которое может быть сделано программистом, не отвечающим за разделы кода, создающие и уничтожающие эту карту. Большие произвольные графики не означают, что внутренние указатели не являются сильными, что это нормально для элементов, связанных с исчезнуть. Я не скажу, является ли устранение некоторых утечек памяти меньшей проблемой, чем необходимость вручную закрывать файлы, но это реально.
Просфилаес
5

Java не указывает точно, когда объект собирается, потому что это дает реализациям свободу выбора, как обрабатывать сборку мусора.

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

В многопоточном коде стоимость выше. Он либо требует атомарных приращений / уменьшений, либо блокировок, которые могут быть дорогими. На современном процессоре атомарная операция может быть в 20 раз дороже, чем простая операция с регистром (очевидно, варьируется от процессора к процессору). Это может увеличить стоимость.

Таким образом, с этим мы можем рассмотреть компромиссы, сделанные несколькими моделями.

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

  • CPython использует гибридный механизм. Они используют счетчики ссылок, но у них также есть сборщик мусора, который идентифицирует циклы и освобождает их. Это обеспечивает преимущества обоих миров за счет обоих подходов. CPython должен поддерживать количество ссылок ивести бухгалтерский учет, чтобы обнаружить циклы. CPython сходит с рук двумя способами. Во-первых, CPython не является полностью многопоточным. Он имеет блокировку, известную как GIL, которая ограничивает многопоточность. Это означает, что CPython может использовать обычные инкременты / декременты, а не атомарные, что намного быстрее. CPython также интерпретируется, что означает, что такие операции, как присваивание переменной, уже занимают несколько инструкций, а не просто 1. Дополнительные затраты на выполнение приращений / уменьшений, которые выполняются быстро в коде C, меньше проблем, потому что мы ' Мы уже оплатили эту стоимость.

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

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

Корт Аммон
источник
4

Хотя finalizeсборка мусора была поддержана Java GC, сборка мусора по своей сути интересует не мертвые объекты, а живые. В некоторых системах GC (возможно, включая некоторые реализации Java) единственное, что отличает группу битов, представляющих объект, от группы хранилищ, которая ни для чего не используется, - это наличие ссылок на первую. Хотя объекты с финализаторами добавляются в специальный список, другие объекты могут не иметь ничего в юниверсе, в котором говорится, что их хранилище связано с объектом, за исключением ссылок, хранящихся в пользовательском коде. Когда последняя такая ссылка будет перезаписана, битовая комбинация в памяти немедленно перестанет распознаваться как объект, независимо от того, осознает ли это что-либо во вселенной или нет.

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

  1. Признать недействительными слабые ссылки, которые идентифицируют объекты, которые не имеют сильно достижимых ссылок, связанных с ними.

  2. Выполните поиск в системном списке объектов с помощью финализаторов, чтобы выяснить, не связаны ли какие-либо из них с сильно достижимыми ссылками.

  3. Определите и консолидируйте области хранения, которые не используются никакими объектами.

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

Supercat
источник
5
На самом деле, у gc есть только одна цель: симуляция бесконечной памяти. Все, что вы назвали целью, является либо несовершенством абстракции, либо деталями реализации.
дедупликатор
@Deduplicator: Слабые ссылки предлагают полезную семантику, которая не может быть достигнута без помощи GC.
суперкат
Конечно, слабые ссылки имеют полезную семантику. Но понадобится ли эта семантика, если симуляция будет лучше?
дедупликатор
@ Дедупликатор: Да. Рассмотрим коллекцию, которая определяет, как обновления будут взаимодействовать с перечислением. Такая коллекция может содержать слабые ссылки на любых живых перечислителей. В системе с неограниченной памятью коллекция, которая повторялась многократно, имела бы свой список заинтересованных перечислителей, растущий без ограничений. Память, необходимая для этого списка, не будет проблемой, но время, необходимое для его перебора, ухудшит производительность системы. Добавление GC может означать разницу между алгоритмом O (N) и O (N ^ 2).
суперкат
2
Почему вы хотите уведомить счетчики, вместо того, чтобы добавлять их в список и разрешать им искать себя, когда они используются? И любая программа, зависящая от своевременной обработки мусора, а не от нагрузки на память, в любом случае живет в состоянии греха, если вообще движется.
Дедупликатор
4

Позвольте мне предложить переписку и обобщение вашего вопроса:

Почему Java не дает строгих гарантий относительно своего процесса GC?

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

Это твой ответ.

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

Конечно, даже в этом решении есть компромисс: поддерживая контракт, Java в основном * лишает программистов возможности полагаться на деструкторы. Это то, что программисты C ++, в частности, часто упускают ([цитата нужна];)), так что это не незначительный компромисс. Я не видел обсуждения этого конкретного мета-решения, но, вероятно, люди Java решили, что преимущества наличия большего количества опций GC перевешивают преимущества возможности точно указать программистам, когда объект будет уничтожен.


* Существует finalizeметод, но по разным причинам, которые выходят за рамки этого ответа, на него трудно и не стоит полагаться.

yshavit
источник
3

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

Преимущество сборки мусора в том, что она «работает», если разработчик не делает глупостей. С подсчетом ссылок вы можете иметь циклы ссылок, что означает, что он «работает», но разработчик иногда должен быть умным. Так что это плюс для сбора мусора.

При подсчете ссылок объект сразу исчезает, когда счетчик ссылок падает до нуля. Это преимущество для подсчета ссылок.

По быстрому сборка мусора происходит быстрее, если вы верите поклонникам сбора мусора, а подсчет ссылок - быстрее, если вы верите поклонникам подсчета ссылок.

Это всего лишь два разных метода для достижения одной и той же цели, Java выбрал один метод, Objective-C выбрал другой (и добавил много поддержки компилятора, чтобы изменить его с боли в заднице на что-то, что не требует больших усилий для разработчиков).

Переключение Java с сборки мусора на подсчет ссылок было бы серьезной задачей, потому что потребовалось бы много изменений кода.

Теоретически, в Java могла бы быть реализована смесь сбора мусора и подсчета ссылок: если счетчик ссылок равен 0, то объект недоступен, но не обязательно наоборот. Таким образом, вы могли бы вести подсчет ссылок и удалять объекты, когда их счетчик ссылок равен нулю (а затем запускать сборку мусора время от времени, чтобы ловить объекты в недостижимых ссылочных циклах). Я думаю, что мир разделен на 50/50 людей, которые считают, что добавление подсчета ссылок в сборщик мусора является плохой идеей, и люди, которые думают, что добавление сбора мусора в подсчет ссылок является плохой идеей. Так что этого не произойдет.

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

gnasher729
источник
С подсчетом ссылок финализация тривиальна, так как программист позаботился о циклах. С gc циклы тривиальны, но программист должен быть осторожен с финализацией.
дедупликатор
@Deduplicator В Java, это также можно создавать сильные ссылки на объекты дорабатывается ... В Objective-C и Swift, когда счетчик ссылок равен нулю, то объект будет исчезать (если вы положили бесконечный цикл в dealloc / деист).
gnasher729
Только что заметил глупую проверку правописания, заменяющую deinit на
deist
1
Есть причина, по которой большинство программистов ненавидят автоматическую коррекцию орфографии ... ;-)
Deduplicator
LOL ... Я думаю, что мир разделен на 0,1 / 0,1 / 99,8 между людьми, которые думают, что добавление подсчета ссылок в сборщик мусора - плохая идея, и людьми, которые думают, что добавление сборки мусора в счетчик ссылок - плохая идея, и людьми, которые продолжайте считать дни, пока не прибудет
сборщик
1

Все остальные аргументы производительности и дискуссии о сложности понимания, когда больше нет ссылок на объект, верны, хотя есть еще одна идея, о которой стоит упомянуть, - это то, что есть хотя бы одна JVM (azul), которая рассматривает что-то подобное тем, что он реализует параллельный gc, который по существу имеет поток vm, постоянно проверяющий ссылки, чтобы попытаться удалить их, что будет действовать не совсем так, как вы говорите. В основном он будет постоянно оглядываться в куче и пытаться восстановить любую память, на которую нет ссылок. Это приводит к очень небольшим затратам производительности, но приводит к практически нулевому или очень короткому времени GC. (То есть, если постоянно увеличивающийся размер кучи превышает системную оперативную память, а затем Azul запутывается, а затем появляются драконы)

TLDR Нечто подобное существует для JVM, это просто специальный jvm, и у него есть недостатки, как у любого другого инженерного компромисса.

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

Форд префект
источник
1

Максимизация постоянной пропускной способности или минимизация задержки gc находятся в динамическом напряжении, что, вероятно, является наиболее распространенной причиной, по которой GC не происходит немедленно. В некоторых системах, например в 911 приложениях для экстренных случаев, несоблюдение определенного порогового значения задержки может привести к запуску процессов аварийного переключения сайта. В других, таких как банковские и / или арбитражные сайты, гораздо важнее максимизировать пропускную способность.

barmid
источник
0

скорость

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

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