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

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

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

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

Таким образом, вопрос является на самом деле двойным:

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

317
задан Community 23 May 2017 в 01:55
поделиться

7 ответов

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

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

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

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


EDIT, чтобы ответить на несколько вопросов из другой темы:

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

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

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

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

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

241
ответ дан 23 November 2019 в 01:02
поделиться

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

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

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

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

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

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

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

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

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

Примечание: в приведенном выше объяснении умалчивается тот факт, что типичный современный сборщик мусора разбивает кучу на «пробелы», сборщик мусора может динамически расширять кучу, рабочий набор приложений, не являющихся мусорными, может меняться и т. Даже в этом случае один и тот же базовый принцип применяется ко всем истинным сборщикам мусора 2 . Заставлять запускать сборщик мусора неэффективно.


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

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

145
ответ дан 23 November 2019 в 01:02
поделиться

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

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

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

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

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

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

31
ответ дан 23 November 2019 в 01:02
поделиться

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

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

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

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

9
ответ дан 23 November 2019 в 01:02
поделиться

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

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

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

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

1
ответ дан 23 November 2019 в 01:02
поделиться

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

3
ответ дан 23 November 2019 в 01:02
поделиться

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

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

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

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

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

45
ответ дан 23 November 2019 в 01:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: