Как заставить сборку мусора в Java?


225

Можно ли форсировать сборку мусора в Java, даже если это сложно сделать? Я знаю , о System.gc();и , Runtime.gc();но они только предлагают сделать GC. Как я могу заставить GC?


30
Возможно, было бы полезно предоставить некоторую справку о том, почему вам нужно заставить GC. Как правило, на языке сборки мусора плохая практика явно вызывать сборщик.
Джастин Этьер

3
Данная JVM может предоставлять несколько методов сбора мусора, каждый из которых имеет свои преимущества и недостатки, и часто данной ситуации можно избежать, просто намекая JVM во время запуска. Пожалуйста, уточните сценарий.
Турбьерн Равн Андерсен

3
jmap -histo: живая <PID> stackoverflow.com/questions/6418089/...

5
Вот пример использования для принудительного сбора мусора: у меня есть сервер с кучей 30 ГБ, из которых обычно используется ~ 12 ГБ (~ 5 миллионов объектов). Каждые 5 минут сервер тратит примерно одну минуту на выполнение сложной задачи, в которой используется примерно 35 миллионов дополнительных объектов. Полный сборщик мусора запускается пару раз в час, неизменно во время выполнения сложной задачи, и останавливает виртуальную машину на 10–15 секунд. Я хотел бы заставить полный GC запускаться в то время, когда сложная задача не выполняется; тогда он будет манипулировать 5M живыми объектами, а не 40M.
Стив

3
@JustinEthier Есть один довольно очевидный случай, когда вам может потребоваться принудительно установить GC, это модульное тестирование любого поведения, включающего иерархию типов java.lang.ref.Reference.
Элиас Василенко

Ответы:


168

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


28
Там должен быть. non-deterministic == trouble
Pacerier

7
Сборщик мусора может быть недетерминированным и по-прежнему предлагать способ немедленного сбора. Например, обычно .NET-коллектор недетерминирован, но вызов GC.Collect () заставляет его работать. Просто Java выбирает не выставлять эту функцию.
Петр Худечек

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

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

@ PetrHudeček В реальных приложениях .NET GC.Collect()не собирает. В Java это gc()делает.
августа

53

Библиотека jlibs имеет хороший служебный класс для сборки мусора . Вы можете форсировать сборку мусора, используя хитрый трюк с объектами WeakReference .

RuntimeUtil.gc () из jlibs:

   /**
    * This method guarantees that garbage collection is
    * done unlike <code>{@link System#gc()}</code>
    */
   public static void gc() {
     Object obj = new Object();
     WeakReference ref = new WeakReference<Object>(obj);
     obj = null;
     while(ref.get() != null) {
       System.gc();
     }
   }

1
Этот код нарушен, потому что слабый ref очищается, как только его референт становится слабо доступным, то есть до того, как он удаляется из памяти.
Марко Топольник

1
Возможно, вы путаете значение «GC закончилась» с «память восстановлена». Объект живет и еще даже не завершен, но вы не можете получить к нему доступ через слабую ссылку. Несколько лучшим способом было бы использовать PhantomReferenceс, ReferenceQueueи вы получите уведомление после завершения, но еще до очистки. Наконец, даже если вы успешно обнаружите, что память для этого объекта была восстановлена, это все равно будет означать очень мало в поколенческом GC, подобном HotSpot. Обычно это совпадает с уборкой молодого поколения.
Марко Топольник

20
ОП запросил, и вы заявили, что предоставили решение "принудительного сбора мусора". Запуск подсистемы GC - это одно, а сбор мусора - другое. Приведенный вами пример кода явно предназначен для того, чтобы гарантировать сбор мусора. Во всяком случае, это очень старый вопрос, очевидно, он не о желаниях ОП, а полезен для широкой публики. Никто не заинтересован в том, чтобы «заставить подсистему GC работать» самостоятельно, без сбора мусора. На самом деле люди обычно хотят гарантировать, что весь мусор собран.
Марко Топольник

4
Вы, вероятно, не сравнивали это с эффективностью System.gc(); System.gc();, но было бы интересно узнать, работает ли это когда-либо лучше, чем это. На самом деле, достаточно просто напечатать, сколько раз он звонил System.gc(). Шанс когда-либо достичь 2 довольно мал.
Марко Топольник

3
@MarkoTopolnik: «Никто не заинтересован в том, чтобы« заставить подсистему GC работать »самостоятельно, без сбора мусора»… На самом деле, меня сегодня интересовало только такое поведение. Я благодарен, что этот ответ присутствовал. Моей целью было проверить вращательное поведение обработки журнала GC и получить формат вывода GC. Этот маленький трюк помог мне быстро заполнить журналы GC.
erik.weathers

49

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

Примечание: это не простой ответ.


40
+1 за лулз. ничто не делает разочаровывающую отладку лучше, чем у кого-то с чувством юмора. кроме действительно полезного ответа, то есть.
JS


25

ДА, почти возможно заставить вас вызывать методы в том же порядке, и в то же время это:

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

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

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

ПРИМЕЧАНИЕ: просто помните, что это будет работать только в том случае, если в методе finalize нет переназначения объекта, если это произойдет, объект останется в живых и произойдет воскресение, что технически возможно.


10
НЕТ , даже эти две команды НЕ будут форсировать сборку мусора. Как уже упоминалось другими, gc()это только подсказка для запуска сборки мусора. runFinalizers()финализаторы запускаются только на объектах, "которые были найдены как отброшенные". Если gc на самом деле не запускался, таких объектов может не быть ...
Штеффен Хейл

Кроме того, System.runFinalization () не гарантирует, что что-либо будет работать; Вполне возможно, что ничего не произойдет вообще. Это предложение - от Javadoc: « Вызов этого метода предполагает , что виртуальная машина Java расходуют усилия в направлении запуска методов ФИНАЛИЗОВ объектов , которые были найдены , чтобы быть отброшено , но чьи методы финализации еще не было запущенно »
Каано

21

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

Предположительно, вопрос, который вы действительно хотели задать, был: «Как я могу восстановить память, которую, как мне кажется, я должен вернуть, путем сбора мусора?»


18

Чтобы вручную запросить GC (не из System.gc ()):

  1. Перейти к: папке bin в JDK, например -C: \ Program Files \ Java \ jdk1.6.0_31 \ bin
  2. Откройте jconsole.exe
  3. Подключитесь к нужному локальному процессу.
  4. Перейдите на вкладку памяти и нажмите выполнить GC.

3
Опссс вводит в заблуждение. Пожалуйста, наведите курсор мыши на кнопку «Perform GC». Вы можете запросить JVM для выполнения GC, но никогда не форсировать.
Кумаран

@PinkeshSharma, это не сила . Это простая просьба, которую, вероятно, можно полностью игнорировать.
Pacerier

@Pacerier Да, в идеальном мире ... но если вы сделаете это, то увидите, что память мгновенно увеличивается ...
Pinkesh Sharma

11

.gc - кандидат на исключение в будущих выпусках - инженер Sun однажды заметил, что, возможно, менее двадцати человек в мире действительно знают, как использовать .gc () - вчера вечером я несколько часов работал над центральным / критическим структура данных, использующая данные, сгенерированные SecureRandom, где-то чуть дальше 40 000 объектов, виртуальная машина замедлится, как если бы в ней не было указателей. Ясно, что он подавлял 16-битные таблицы указателей и демонстрировал классическое поведение «сбойного механизма».

Я пробовал -Xms и так далее, продолжал немного вертеться, пока не достигнет 57, ххх что-то. Затем он будет запускать gc, скажем, с 57 127 до 57 128 после gc () - примерно с тем же темпом вздутия кода в лагере Easy Money.

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


1
У меня есть что-то подобное, много объектов в памяти, я не могу их освободить. Выдано исключение OutOfMemory, я хочу, чтобы Force GC проверил, существует ли какой-либо процесс создания бесконечного объекта или эти объекты используются моей системой.

Похоже, вы работаете над той же проблемой, что и я, пожалуйста, объясните: «Создание бесконечного объекта» ... хороший исследовательский проект, может быть, вы можете опубликовать вопрос или что-то в области Java здесь (я вроде новичок здесь и не знаю, как работает конечный автомат, как работает сайт. Вчера я попробовал и в итоге запустил файл file.dat, поскольку компилятор пожаловался на «слишком много кода» на 40000 base36 BigIntegers, закодированных как статическая конечная строка. и предположим, что вся JVM ограничена 16-разрядными указателями, я уверен, что то, что мы должны сделать, это агрессивно нулевое и читать с диска ...
Николас Джордан

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

5
Ерунда! Существует один очевидный случай, когда его следует использовать: тестирование кода, который использует слабые ссылки, чтобы мы могли убедиться в правильности поведения при устранении слабых ссылок.
Элиас Василенко


6

Спецификация JVM не говорит ничего конкретного о сборке мусора. Благодаря этому поставщики могут свободно внедрять GC по-своему.

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


4

Если вам нужно форсировать сборку мусора, возможно, вам следует подумать о том, как вы управляете ресурсами. Вы создаете большие объекты, которые сохраняются в памяти? Вы создаете большие объекты (например, графические классы), которые имеют Disposableинтерфейс и не вызывают, dispose()когда закончите с ним? Вы объявляете что-то на уровне класса, что вам нужно только в одном методе?


2

Было бы лучше, если бы вы описали причину, по которой вам нужна сборка мусора. Если вы используете SWT, вы можете использовать такие ресурсы, как Imageи Fontдля освобождения памяти. Например:

Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();

Есть также инструменты для определения нераспределенных ресурсов.


Что делать, если нет никакого метода утилизации?
ArifMustafa

1
Совершенно не имеет отношения к вопросу! Нет, я не использую SWT. Я вызываю метод JNI, который открывает окно .NET через нативный уровень Delphi. У меня также есть вычислительное ядро ​​FORTRAN, которое получает данные через нативный уровень C ++. Какое это имеет отношение к чему-нибудь? Могу ли я заставить GC или нет? Нет? :-(
Мостафа Зейнали

0

Если у вас заканчивается память и OutOfMemoryExceptionвы можете получить ее , попробуйте увеличить объем кучи, доступной для Java, запустив программу java -Xms128m -Xmx512mвместо просто java. Это даст вам начальный размер кучи 128 МБ и максимум 512 МБ, что намного больше, чем стандартные 32 МБ / 128 МБ.


Настройки памяти по умолчаниюjava -Xms512M -Xmx1024M
ThePyroEagle

0

Другой вариант - не создавать новые объекты.

Объединение объектов не требуется, чтобы уменьшить необходимость GC в Java.

Пул объектов обычно не будет быстрее создания объектов (особенно для легких объектов), но он быстрее, чем сборщик мусора. Если вы создали 10000 объектов, и каждый объект был 16 байтов. Это 160 000 байтов, которые GC должен восстановить. С другой стороны, если вам не нужны все 10 000 одновременно, вы можете создать пул для переработки / повторного использования объектов, что устраняет необходимость в создании новых объектов и устраняет необходимость в GC старых объектов.

Как то так (не проверено). И если вы хотите, чтобы он был потокобезопасным, вы можете поменять LinkedList на ConcurrentLinkedQueue.

public abstract class Pool<T> {
    private int mApproximateSize;
    private LinkedList<T> mPool = new LinkedList<>();

    public Pool(int approximateSize) {
        mApproximateSize = approximateSize;
    }

    public T attain() {
        T item = mPool.poll();
        if (item == null) {
            item = newInstance();
        }
        return item;
    }

    public void release(T item) {
        int approxSize = mPool.size(); // not guaranteed accurate
        if (approxSize < mApproximateSize) {
            recycle(item);
            mPool.add(item);
        } else if (approxSize > mApproximateSize) {
            decommission(mPool.poll());
        }
    }

    public abstract T newInstance();

    public abstract void recycle(T item);

    public void decommission(T item) { }

}

0

В OracleJDK 10 с G1 GC один вызов System.gc()заставит GC очистить старую коллекцию. Я не уверен, что GC запускается немедленно. Однако GC не будет очищать коллекцию Young, даже если она System.gc()вызывается многократно. Чтобы заставить GC очистить коллекцию Young, вы должны распределить в цикле (например, new byte[1024]без вызова) System.gc(). Призыв System.gc()по какой-то причине не позволяет GC очистить коллекцию Young.


0

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

Это правильно, только жест. У вас есть почти стандартные ответы, которые уже даны несколькими авторами. Давайте возьмем это по одному:

  1. Я не мог получить этот кусок кода на самом деле

Правильно, фактического jvm нет - такова только спецификация, связка компьютерных наук, описывающая желаемое поведение ... Недавно я начал копать инициализацию объектов Java из нативного кода. Чтобы получить то, что вы хотите, единственный способ сделать то, что называется агрессивным обнулением. Ошибки, если они сделаны неправильно, настолько плохи, что мы должны ограничиться первоначальным объемом вопроса:

  1. какой-то кусок кода в моей большой системе делает создание объектов

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

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

Каждый раз, когда вы, я или кто-либо еще получаете: « Пожалуйста, мне это ужасно нужно». Это почти универсальный предшественник почти полного уничтожения того, над чем вы пытаетесь работать… напишите нам небольшой пример кода, очищающий от него любое Фактический код используется и покажите нам свой вопрос.

Не расстраивайся. Часто это решает то, что ваш dba использует купленный где-то пакет, и оригинальный дизайн не настроен для массивных структур данных.

Это очень распространено.


-1

FYI

Вызов метода System.runFinalizersOnExit (true) гарантирует, что методы финализатора будут вызваны до завершения работы Java. Однако этот метод по своей сути небезопасен и является устаревшим. Альтернативой является добавление «ловушек отключения» с помощью метода Runtime.addShutdownHook.

Масаррат Сиддики


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

-1

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

class GarbageCollectorManager {

    private static boolean collectionWasForced;
    private static int refCounter = 0;

    public GarbageCollectorManager() {
        refCounter++;
    }

    @Override
    protected void finalize() {
        try {
            collectionWasForced = true;
            refCounter--;
            super.finalize();   
        } catch (Throwable ex) {
            Logger.getLogger(GarbageCollectorManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public int forceGarbageCollection() {
        final int TEMPORARY_ARRAY_SIZE_FOR_GC = 200_000;
        int iterationsUntilCollected = 0;
        collectionWasForced = false;

        if (refCounter < 2) 
            new GarbageCollectorManager();

        while (!collectionWasForced) {
            iterationsUntilCollected++;
            int[] arr = new int[TEMPORARY_ARRAY_SIZE_FOR_GC];
            arr = null;
        }

        return iterationsUntilCollected;
    }

}

Использование:

GarbageCollectorManager manager = new GarbageCollectorManager();
int iterationsUntilGcExecuted = manager.forceGarbageCollection();

Я не знаю, насколько этот метод полезен, потому что он постоянно заполняет кучу, но если у вас есть критически важное приложение, которое ДОЛЖНО форсировать GC - когда это может быть Java-способ форсировать GC.


Что такое "final int TEMPORARY_ARRAY_SIZE_FOR_GC = 200_000;"?
Корай Тугай

Размер массива - сколько временных объектов (int) будет сгенерировано, чтобы GC начал работать.
Агниус Василяускас

Это действительно: "_" в целом числе?
Корай Тугай

1
Да, подчеркивания в числовых литералах действительны, начиная с Java SE 7. Это полезно, например, в качестве разделителя тысяч в целых числах, как в этом случае.
Агний Василяускас

3
Вы никогда не должны запускать такой код в производственной системе. Хотя этот код выполняется в одном потоке, любой другой поток также может получить исключение OutOfMemoryException, полностью изменив намерение вызвать его в первую очередь ....
Штеффен Хейл

-1

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

Поскольку сборщик мусора работает с виртуальной машиной, мы не можем заставить ее выполнить очистку тут же. Скорее, мы ставим наш запрос в очередь на сборщик мусора. Это зависит от виртуальной машины, после определенного времени (это может измениться от системы к системе, как правило, когда пороговая память, выделенная для JVM заполнена), фактическая машина освободит пространство. : D


Первое предложение второго абзаца не является следствием .
маркиз Лорн

-1

Следующий код взят из метода assertGC (...). Он пытается заставить недетерминированный сборщик мусора собирать.

   List<byte[]> alloc = new ArrayList<byte[]>();
   int size = 100000;
   for (int i = 0; i < 50; i++) {
        if (ref.get() == null) {
             // Test succeeded! Week referenced object has been cleared by gc.
             return;
        }
        try {
             System.gc();
        } catch (OutOfMemoryError error) {
             // OK
        }
        try {
             System.runFinalization();
        } catch (OutOfMemoryError error) {
             // OK
        }

        // Approach the jvm maximal allocatable memory threshold
        try {
             // Allocates memory.
             alloc.add(new byte[size]);

             // The amount of allocated memory is increased for the next iteration.
             size = (int)(((double)size) * 1.3);
        } catch (OutOfMemoryError error) {
             // The amount of allocated memory is decreased for the next iteration.
             size = size / 2;
        }

        try {
             if (i % 3 == 0) Thread.sleep(321);
        } catch (InterruptedException t) {
             // ignore
        }
   }

   // Test failed! 

   // Free resources required for testing
   alloc = null;

   // Try to find out who holds the reference.
   String str = null;
   try {
        str = findRefsFromRoot(ref.get(), rootsHint);
   } catch (Exception e) {
        throw new AssertionFailedErrorException(e);
   } catch (OutOfMemoryError err) {
        // OK
   }
   fail(text + ":\n" + str);

Источник (я добавил некоторые комментарии для ясности): Пример NbTestCase


-1

Вы можете попробовать использовать Runtime.getRuntime().gc()или использовать служебный метод. System.gc()Примечание. Эти методы не гарантируют GC. И их область должна быть ограничена JVM, а не программно обрабатывать это в вашем приложении.


2
Как объяснено в других ответах, эти методы не вызывают (полный) запуск сборки мусора.
Flow

-2

Если вы используете JUnit и Spring, попробуйте добавить это в каждый тестовый класс:

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.