Рубрики
Без рубрики

Руководство по системе.gc()

Узнайте о методе System.gc (), чтобы понять, есть ли какие-либо варианты использования, когда вызов этого метода может быть полезен

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы рассмотрим метод System.gc () , расположенный в пакете java.lang .

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

2. Сбор Мусора

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

  • Старое поколение (арендованное пространство) заполнено, что вызывает незначительный GC
  • Новое поколение (Eden + Survivor 0 + Survivor 1) заполнено, что запускает основной/полный GC

Единственное, что не зависит от реализации GC, – это право объекта на сбор мусора.

Теперь мы рассмотрим сам метод System.gc () .

3. System.gc()

Вызов метода прост:

System.gc()

В официальной документации Oracle |/говорится, что:

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

Нет никакой гарантии, что фактический GC будет запущен .

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

Существование явного вызова сборки мусора должно быть серьезным красным флагом для всех.

Мы можем запретить System.gc() выполнять какую-либо работу, используя флаг -XX:DisableExplicitGC JVM.

3.1. Настройка производительности

Стоит отметить, что непосредственно перед тем, как выбросить OutOfMemoryError, JVM выполнит полный GC. Поэтому явный вызов System.gc() не спасет нас от сбоя .

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

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

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

-XX:+ExplicitGCInvokesConcurrent

или:

-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses

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

В следующей главе мы увидим практический пример, когда явный вызов System.gc() кажется полезным.

4. Пример использования

4.1. Сценарий

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

Незначительный сбор мусора происходит чаще, чем основной. Поэтому, вероятно, нам следует сосредоточиться на последнем. Один объект перемещается в арендуемое пространство, если он “пережил” несколько коллекций и все еще доступен из корней GC.

Давайте представим, что у нас есть огромная коллекция объектов, которые живут в течение некоторого времени. Затем, в какой-то момент, мы очищаем коллекцию объектов. Может быть, это хороший момент для запуска System.gc() ?

4.2. Демонстрационное приложение

Мы создадим простое консольное приложение, которое позволит нам смоделировать этот сценарий:

public class DemoApplication {

    private static final Map cache = new HashMap();

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNext()) {
            final String next = scanner.next();
            if ("fill".equals(next)) {
                for (int i = 0; i < 1000000; i++) { 
                    cache.put(randomUUID().toString(), randomUUID().toString()); 
                } 
            } else if ("invalidate".equals(next)) {
                cache.clear();
            } else if ("gc".equals(next)) {
                System.gc();
            } else if ("exit".equals(next)) {
                System.exit(0);
            } else {
                System.out.println("unknown");
            }
        }
    }
}

4.3. Запуск демо-версии

Давайте запустим наше приложение с несколькими дополнительными флагами:

-XX:+PrintGCDetails -Xloggc:gclog.log -Xms100M -Xmx500M -XX:+UseConcMarkSweepGC

Первые два флага необходимы для регистрации информации о GC. Следующие два флага задают начальный размер кучи, а затем максимальный размер кучи. Мы хотим сохранить размер кучи низким, чтобы заставить GC быть более активным. Наконец, мы решили использовать CMS – Параллельный сборщик мусора Mark и Sweep. Пришло время запустить наше приложение!

Во-первых, давайте попробуем заполнить арендованное пространство . Введите заполнить.

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

197.057: [GC (Allocation Failure) 197.057: [ParNew: 67498K->40K(75840K), 0.0016945 secs] 
  168754K->101295K(244192K), 0.0017865 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] secs]

Как мы видим, память заполнена.

Далее, давайте force System.gc() наберем gc . Мы видим, что использование памяти существенно не изменилось:

238.810: [Full GC (System.gc()) 238.810: [CMS: 101255K->101231K(168352K); 0.2634318 secs] 
  120693K->101231K(244192K), [Metaspace: 32186K->32186K(1079296K)], 0.2635908 secs] 
  [Times: user=0.27 sys=0.00, real=0.26 secs]

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

Давайте очистим кэш , введя invalidate . Мы не увидим больше строк журнала в файле gc log.log .

Мы можем попытаться заполнить кэш еще несколько раз, но никакого GC не происходит. Это момент, когда мы можем перехитрить сборщика мусора . Теперь, после форсирования GC, мы увидим строку типа:

262.124: [Full GC (System.gc()) 262.124: [CMS: 101523K->14122K(169324K); 0.0975656 secs] 
  103369K->14122K(245612K), [Metaspace: 32203K->32203K(1079296K)], 0.0977279 secs]
  [Times: user=0.10 sys=0.00, real=0.10 secs]

Мы выпустили впечатляющий объем памяти! Но было ли это действительно необходимо прямо сейчас? Что случилось?

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

5. Другие обычаи

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

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

Другим является анализ утечки памяти это скорее практика отладки, чем то, что мы хотели бы сохранить в производственном коде. Вызов System.gc() и то, что объем кучи все еще высок, может указывать на утечку памяти .

6. Резюме

В этой статье мы исследовали метод System.gc() и то, когда он может показаться полезным.

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

Как обычно, код, используемый в этой статье, можно найти на GitHub .