Почему плохой практикой является вызов System.gc()?

После ответа на вопрос о объектах без силы в Java (парень очищал 1.5GB HashMap) с System.gc(), мне сказали, что плохая практика вызывает System.gc() вручную, но комментарии не были полностью убедительными. Кроме того, никто, казалось, не осмелился вырвать и не опросить мой ответ.

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

Я понимаю, что JVM обычно лучше вас знает, когда ему нужно вернуть память. Я также понимаю, что беспокоиться о нескольких килобайтах данных глупо. Я также понимаю, что даже мегабайты данных не то, что было несколько лет назад. Но все же, 1,5 гигабайта? И вы знаете, что в памяти около 1,5 ГБ данных; ему не нравится выстрел в темноте. Является System.gc() систематически плохо, или есть какой-то момент, когда он становится в порядке?

Итак, вопрос на самом деле двойной:

  • Почему или нет плохой практики вызывать System.gc()? Это действительно просто намек на JVM при определенных реализациях, или это всегда полный цикл сбора? Существуют ли действительно сборщики мусора, которые могут выполнять свою работу, не останавливая мир? Прошу прояснить различные утверждения, высказанные людьми в комментариях к моему .
  • Где порог? Разве не рекомендуется звонить System.gc(), или есть время, когда оно приемлемо? Если да, то каковы те времена?

Ответ 1

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

Вы не знаете, какой сборщик мусора вы используете. Конечно, есть некоторые, которые не "останавливают мир", как вы утверждаете, но некоторые JVM не настолько умны или по разным причинам (возможно, они есть на телефоне?) Этого не делают. Вы не знаете, что он собирается делать.

Кроме того, он не гарантирует ничего. JVM может просто полностью игнорировать ваш запрос.

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


ИЗМЕНИТЬ, чтобы решить несколько проблем из другого потока:

После прочтения темы, которую вы связали, еще несколько вещей, которые я хотел бы указать. Во-первых, кто-то предположил, что вызов gc() может вернуть память в систему. Это, конечно, не обязательно верно - сама куча Java растет независимо от распределений Java.

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

Чтобы показать, что возможно, что System.gc() ничего не делает, view:

http://bugs.sun.com/view_bug.do?bug_id=6668279

и, в частности, существует опция - XX: DisableExplicitGC.

Ответ 2

Уже объяснено, что вызов system.gc() может ничего не делать и что любой код, который "нуждается" для запуска сборщика мусора, нарушен.

Однако прагматичная причина, по которой плохой практикой вызывать System.gc() заключается в том, что она неэффективна. И в худшем случае это ужасно неэффективно ! Позволь мне объяснить.

Типичный алгоритм GC идентифицирует мусор, перемещая все объекты без мусора в куче и выводя, что любой объект, который не был посещен, должен быть мусором. Из этого можно смоделировать общую работу мусорной коллекции, состоящей из одной части, пропорциональной количеству живых данных, и другой части, которая пропорциональна количеству мусора; т.е. work = (live * W1 + garbage * W2).

Теперь предположим, что вы делаете следующее в однопоточном приложении.

System.gc(); System.gc();

Первый звонок будет (мы прогнозируем) сделать (live * W1 + garbage * W2) работать и избавиться от выдающегося мусора.

Второй вызов будет работать (live* W1 + 0 * W2) и ничего не восстанавливать. Другими словами, мы сделали (live * W1) работу и ничего не добились.

Мы можем моделировать эффективность коллектора как объема работы, необходимой для сбора единицы мусора; т.е. efficiency = (live * W1 + garbage * W2)/garbage. Поэтому, чтобы сделать GC максимально эффективным, нам нужно максимизировать ценность garbage когда мы запускаем GC; т.е. дождитесь, пока куча будет заполнена. (А также сделайте кучу как можно больше, но это отдельная тема.)

Если приложение не вмешивается (вызывая System.gc()), GC будет ждать, пока куча будет заполнена до запуска, что приведет к эффективному сбору мусора 1. Но если приложение заставляет GC работать, есть вероятность, что куча не будет заполнена, и результатом будет то, что мусор собирается неэффективно. И чем чаще приложение подает GC, тем более неэффективным становится GC.

Примечание: приведенное выше объяснение говорит о том, что типичный современный GC разбивает кучу на "пробелы", GC может динамически расширять кучу, рабочий набор приложений, не связанных с мусором, может меняться и так далее. Тем не менее, один и тот же базовый принцип применяется по всей доске ко всем истинным сборщикам мусора 2. Неэффективно заставить GC работать.


1 - Вот как работает сборщик "пропускной способности".Параллельные коллекторы, такие как CMS и G1, используют разные критерии, чтобы решить, когда начать сборщик мусора.

2 - Я также исключаю менеджеров памяти, которые используют только подсчет ссылок, но никакая текущая реализация Java не использует этот подход... по уважительной причине.

Ответ 3

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

  • У вас много объектов, недоступных и, возможно, не были gc'ed. и
  • Вы думаете, что пользователь может смириться с небольшим замедлением на этом этапе.

нет вреда при вызове System.gc(). Я рассматриваю его как ключевое слово c/С++ inline. Это всего лишь намек на gc, что вы, разработчик, решили, что время/производительность не так важны, как обычно, и что некоторые из них могут быть использованы для восстановления памяти.

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

Есть один раз, когда я буду force: при попытке выяснить это утечка определенного объекта (либо собственный код, либо большой, комплексное взаимодействие обратного вызова. Oh и любой компонент пользовательского интерфейса, который так сильно смотрит на Matlab.) Это никогда не должно использоваться в производственном коде.

Ответ 4

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

(Следующие комментарии относятся к Hotspot, работающему на Linux, с CMS-коллекционером, где я уверен, что System.gc() действительно всегда вызывает полную сборку мусора).

  • После первоначальной работы по запуску приложения вы можете быть ужасным состоянием использования памяти. Половина вашего поколенного поколения может быть полна мусора, а это значит, что вы намного ближе к своей первой CMS. В приложениях, где это имеет значение, неплохо было бы назвать System.gc() "reset" вашей кучей исходным состоянием живых данных.

  • В тех же строках, что и # 1, если вы внимательно изучаете свое использование кучи, вы хотите точно прочитать, что такое использование базовой памяти. Если первые 2 минуты времени работы приложения будут инициализированы, ваши данные будут перепутаны, если вы не заставите (ах... "предлагать" ) полный фронт gc.

  • У вас может быть приложение, предназначенное для того, чтобы никогда не продвигать что-либо к поколенному поколению во время его работы. Но, возможно, вам нужно инициализировать некоторые данные, которые не настолько велики, чтобы автоматически переходить в поколение поколений. Если вы не назовете System.gc() после того, как все настроено, ваши данные могут сидеть в новом поколении, пока не наступит время для его продвижения. Внезапно ваше супер-пуперное приложение с низкой задержкой и низким уровнем GC получает удар с ОГРОМНЫМ (условно говоря, конечно) латентным штрафом за продвижение этих объектов во время нормальной работы.

  • Иногда полезно иметь вызов System.gc в производственном приложении для проверки существования утечки памяти. Если вы знаете, что набор живых данных в момент времени X должен существовать в определенном соотношении к набору живых данных в момент времени Y, тогда было бы полезно вызвать System.gc() время X и время Y и сравнить использование памяти.

Ответ 5

Эффективность GC зависит от ряда эвристик. Например, общая эвристика заключается в том, что записи на объекты, обычно возникающие на объектах, созданных не так давно. Другим является то, что многие объекты очень недолговечны (некоторые объекты будут использоваться в течение длительного времени, но многие из них будут отброшены через несколько микросекунд после их создания).

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

Чтобы использовать System.gc() надежно (*), вам нужно знать, как работает GC во всех деталях. Такие детали, как правило, немного меняются, если вы используете JVM от другого поставщика или к следующей версии от того же поставщика или к той же JVM, но с немного отличающимися параметрами командной строки. Поэтому редко бывает хорошей идеей, если вы не хотите решать конкретную проблему, в которой вы контролируете все эти параметры. Следовательно, понятие "плохая практика": это не запрещено, метод существует, но он редко окупается.

(*) Я говорю об эффективности здесь. System.gc() никогда не нарушит правильную программу Java. Это не вызовет дополнительной памяти, которую JVM не смогла бы получить иначе: перед тем, как бросить OutOfMemoryError, JVM выполняет работу System.gc(), даже если это последнее средство.

Ответ 6

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

Много лет назад я работаю над генератором отчетов, который

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

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

Взглянув на вышеописанный процесс, ясно, что.

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

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

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

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

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

Но, давайте ясно, что это не обычный случай.

Ответ 7

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

Тот факт, что вы не можете доверять "System.gc" делать что-либо, невероятно сложно, и может легко вызвать "Страх, неопределенность, сомнение" на язык.

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

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

Позвольте мне просмотреть аргументы этого потока.

  • Это неэффективно:

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

  1. Это всегда плохая практика и указывает на неработающий код.

Я не согласен, неважно, какой у вас сборщик мусора. Его задача - отслеживать мусор и очищать его.

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

Конечно, это может быть не так, как вы хотите или ожидаете, но когда вы хотите называть его, вы знаете, что ничего не происходит, и пользователь готов терпеть медлительность/простои. Если System.gc работает, отлично! Если это не так, по крайней мере, вы пробовали. Там просто нет ни одной стороны, если сборщик мусора не имеет неотъемлемых побочных эффектов, которые делают что-то ужасно неожиданное для того, как, по-видимому, должен вести себя сборщик мусора, если он вызывается вручную, и это само по себе вызывает недоверие.

  1. Это не обычный случай использования:

Это прецедент, который не может быть надежно выполнен, но может быть, если система была разработана таким образом. Это как сделать светофор и сделать его таким, чтобы некоторые/все кнопки светофора ничего не делали, это заставляет вас задаться вопросом, почему кнопка для начала, javascript не имеет функции сбора мусора, поэтому мы надеваем "Тщательно его изучите.

  1. В спецификации указано, что System.gc() - это подсказка о том, что GC должен работать, и виртуальная машина может игнорировать ее.

что такое "подсказка"? что такое "игнорировать"? компьютер не может просто принимать намеки или что-то игнорировать, существуют строгие пути поведения, которые могут быть динамическими, которые руководствуются намерением системы. Правильный ответ будет включать то, что фактически делает сборщик мусора, на уровне реализации, который заставляет его не выполнять сбор, когда вы его запрашиваете. Является ли функция просто nop? Есть ли какие-то условия, которые я должен был встретить? Каковы эти условия?

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

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

Подводя итог, есть неотъемлемая проблема с ответами: "эта функция может ничего не делать, и я не буду вдаваться в подробности, как сказать, когда она что-то делает, а когда нет, и почему она не будет или будет, часто подразумевая, что это просто против философии, чтобы попытаться это сделать, даже если намерение позади этого разумно".

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

Ответ 8

Возможно, я пишу дерьмовый код, но я понял, что щелчок на значке корзины на eclipse и netbeans IDE является "хорошей практикой".

Ответ 9

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

Вызов GC поставляется с нетривиальными накладными расходами на вызов, и если вы сделаете это в какой-то случайный момент времени, вероятно, вы не увидите вознаграждения за свои усилия. С другой стороны, естественно сгенерированная коллекция, скорее всего, окупит затраты на вызов. Если у вас есть информация, указывающая, что GC должен быть запущен, вы можете позвонить в System.gc(), и вы увидите преимущества. Однако, мой опыт показывает, что это происходит только в нескольких случаях, так как маловероятно, что у вас будет достаточно информации, чтобы понять, когда и когда следует вызывать System.gc().

Один из приведенных здесь примеров, попавший в мусорную корзину в вашу среду IDE. Если вы отправитесь на встречу, почему бы не ударить ее. Накладные расходы не повлияют на вас, и куча может быть очищена, когда вы вернетесь. Сделайте это в производственной системе, и частые звонки для сбора приведут к остановке! Даже случайные звонки, такие как сделанные RMI, могут быть разрушительными для производительности.

Ответ 10

Да, вызов System.gc() не гарантирует, что он будет запущен, это запрос к JVM, который может быть проигнорирован. Из документов:

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

Это почти всегда плохая идея назвать это, потому что автоматическое управление памятью обычно лучше, чем вы, когда к gc. Он будет делать это, когда его внутренний пул свободной памяти будет низким, или если ОС потребует вернуть некоторую память.

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

Ответ 11

По моему опыту, использование System.gc() - это эффективная форма оптимизации платформы (где "платформа" представляет собой комбинацию аппаратной архитектуры, ОС, версии JVM и возможных дополнительных параметров времени выполнения, таких как доступная оперативная память), потому что его поведение, хотя и предсказуемое на конкретной платформе, может (и будет) значительно варьироваться между платформами.

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

Однако, может ли это помочь или повредить (или ничего не делать), сильно зависит от платформы (как определено выше).

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

Ответ 12

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

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

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

Ответ 13

мои 2 цента: я загружаю некоторые AnimationDrawables в действие и воспроизвожу их. Я загружаю, играю, а затем устанавливаю фон изображения в нуль, по одному в то время. Если я выйду из этой операции, а затем вернусь снова быстро, то через 3 или 4 раза объем памяти будет слишком большим, пока я не получу исключение из памяти.

Явным образом вызывая сборщик мусора после установки фона изображения изображения null, я вижу, что на Eclipse logcat память хранится достаточно свободно - и в моем случае gc фактически запущен - и я больше не перестаю работать.

Очевидно, что система может решить отложить выполнение gc, но если вы знаете более или менее о том, как работает gc, вы можете доверять в случае, подобном моему, это будет вызвано как можно скорее, по той причине, что система замечает память использовал выращивание и приложил все усилия, чтобы попросить больше о системе. Я думаю, что это работает как контейнеры библиотеки С++ std: вы получаете некоторую начальную память, и каждый раз, когда этого недостаточно, она удваивается.

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