Почему методы сбора мусора Java и Python отличаются?

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

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

Почему Java выбирает эту стратегию и какая польза от этого?

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

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

Например, я могу писать sloppy, не переносимый код в 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, который может работать в отдельном потоке и имеет все эти безумные оптимизации и т.д. Различия как вы пишете свой код, должны быть незначительными и в идеале несуществующими.

Ответ 5

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

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

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

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

Ответ 6

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

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

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

Ответ 7

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

Например, для всех людей, которым не нравится подход "stop-the-world" поведения по умолчанию Java Java по умолчанию, существуют VM, такие как IBM WebSphere Real Time, которая позволяет в реальном времени запускать приложение на Java.

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

Ответ 8

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

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

Ответ 9

Поздно в игре, но я думаю, что одним из существенных аргументов для RC в python является его простота. См. письмо от Alex Martelli, например.

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