Почему методы сборки мусора в Java и Python различны? - PullRequest
50 голосов
/ 22 августа 2008

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

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

Почему Java выбирает эту стратегию и в чем ее выгода?

Это лучше, чем подход Python?

Ответы [ 9 ]

44 голосов
/ 22 августа 2008

Существуют недостатки использования подсчета ссылок. Одна из наиболее упоминаемых - это циклические ссылки. Предположим, ссылки A на ссылки B, B ссылаются на C и C. B. Если A отбросит ссылку на B, то и B, и C все равно будут иметь счетчик ссылок 1 и не будут удалены. с традиционным подсчетом ссылок. CPython (подсчет ссылок не является частью самого Python, но является частью его реализации на C) ловит циклические ссылки с помощью отдельной процедуры сборки мусора, которую он периодически запускает ...

Еще один недостаток: подсчет ссылок может замедлить выполнение. Каждый раз, когда на объект ссылаются и разыменовываются, интерпретатор / ВМ должен проверить, не уменьшилось ли количество до 0 (и затем освободить, если это так). Сборщик мусора не должен делать это.

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

26 голосов
/ 13 октября 2008

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

Существует два широких подхода к отслеживанию мертвых объектов: отслеживание и подсчет ссылок. Трассировка GC начинается с «корней» - таких как ссылки на стек и отслеживает все достижимые (живые) объекты. Все, чего нельзя достичь, считается мертвым. При подсчете ссылок каждый раз при изменении ссылки обновляется счетчик объекта. Любой объект, для которого счетчик ссылок установлен в ноль, считается мертвым.

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

Кроме того, ГХ для подсчета ссылок требует, чтобы детектор цикла очищал любые объекты в цикле, которые не будут отслеживаться только их счетчиком ссылок. Perl 5 не имел детектора циклов в своей реализации GC и мог вытекать из памяти, которая была циклической.

Также были проведены исследования, чтобы получить лучшее из обоих миров (малое время паузы, высокая пропускная способность): http://cs.anu.edu.au/~Steve.Blackburn/pubs/papers/urc-oopsla-2003.pdf

13 голосов
/ 22 августа 2008

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

Например, я могу написать неаккуратный непереносимый код в CPython, например

def parse_some_attrs(fname):
    return open(fname).read().split("~~~")[2:4]

и дескриптор файла для этого файла, который я открыл, будет немедленно очищен, потому что, как только ссылка на открытый файл исчезнет, ​​файл будет удален, а дескриптор файла освобожден. Конечно, если я запускаю Jython или IronPython или, возможно, PyPy, то сборщик мусора не обязательно будет работать намного позже; возможно, сначала у меня закончатся файловые дескрипторы, и моя программа потерпит крах.

Так что вы ДОЛЖНЫ писать код, который выглядит как

def parse_some_attrs(fname):
    with open(fname) as f:
        return f.read().split("~~~")[2:4]

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

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

8 голосов
/ 22 августа 2008

Я думаю, что статья " Теория и практика Java: краткая история сборки мусора " от IBM должна помочь объяснить некоторые вопросы, которые у вас есть.

5 голосов
/ 16 сентября 2008

Сборка мусора происходит быстрее (более эффективно по времени), чем подсчет ссылок, если у вас достаточно памяти. Например, копирующий gc пересекает «живые» объекты и копирует их в новое пространство и может вернуть все «мертвые» объекты за один шаг, пометив всю область памяти. Это очень эффективно, , если у вас достаточно памяти. Коллекции поколений используют знания о том, что «большинство предметов умирают молодыми»; часто только несколько процентов объектов должны быть скопированы.

[Это также причина, по которой gc может работать быстрее, чем malloc / free]

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

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

3 голосов
/ 19 октября 2011

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

Я предполагаю, что это на самом деле является основной причиной резкого ощущения Android (на базе Java) даже на самых дорогих мобильных телефонах по сравнению с плавностью iOS (на основе ObjectiveC и с использованием RC).

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

2 голосов
/ 06 сентября 2008

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

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

2 голосов
/ 23 августа 2008

Последние версии Sun Java VM на самом деле имеют несколько алгоритмов GC, которые вы можете настроить. В спецификациях Java VM преднамеренно не указывается фактическое поведение GC, чтобы разрешить разные (и несколько) алгоритмы GC для разных виртуальных машин.

Например, для всех людей, которым не нравится подход "остановка мира", связанный с поведением Sun Java VM GC по умолчанию, есть такие виртуальные машины, как IBM WebSphere Real Time , которые позволяют работать в режиме реального времени. приложение для запуска на Java.

Поскольку спецификация Java VM общедоступна, (теоретически) ничто не может помешать реализации Java VM, использующей алгоритм CPython GC.

0 голосов
/ 22 октября 2009

Поздно в игре, но я думаю, что одно существенное обоснование RC в python - это его простота. См. Это электронное письмо Алекса Мартелли , например.

(Я не смог найти ссылку вне кеша Google, дату электронного письма от 13 октября 2005 года в списке Python).

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