Как заставить сборку мусора в Java?
Можно ли форсировать сборку мусора в Java, даже если это сложно сделать? Я знаю о System.gc();
а также Runtime.gc();
но они только предлагают сделать GC. Как я могу заставить GC?
28 ответов
Ваш лучший вариант - позвонить System.gc()
это просто намек сборщику мусора, что вы хотите, чтобы он делал сбор. Нет никакого способа принудительного и немедленного сбора, хотя сборщик мусора недетерминирован.
Библиотека 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();
}
}
Лучший (если не единственный) способ заставить GC - написать пользовательскую JVM. Я считаю, что сборщики мусора являются подключаемыми, так что вы можете просто выбрать одну из доступных реализаций и настроить ее.
Примечание: это не простой ответ.
Используя интерфейс виртуальной машины Java™ (JVM TI), функцию
jvmtiError ForceGarbageCollection(jvmtiEnv* env)
"Принудительно заставит виртуальную машину выполнить сборку мусора". TI JVM является частью архитектуры отладчика платформы JavaTM (JPDA).
ДА, почти возможно заставить вас вызывать методы в том же порядке, и в то же время это:
System.gc ();
System.runFinalization ();
даже если для очистки этих двух методов используется только один объект, вынудите сборщик мусора использовать finalise()
метод недоступного объекта, освобождающего назначенную память и делающего то, что finalize()
метод состояний.
ОДНАКО это ужасная практика использовать сборщик мусора, потому что его использование может привести к перегрузке программного обеспечения, которая может быть даже хуже, чем к памяти, сборщик мусора имеет свой собственный поток, который невозможно контролировать плюс в зависимости от Алгоритм, используемый gc, может занять больше времени и считается очень неэффективным, вы должны проверить свое программное обеспечение, если оно хуже с помощью gc, потому что оно определенно сломано, и хорошее решение не должно зависеть от gc.
ПРИМЕЧАНИЕ: просто помните, что это будет работать только в том случае, если в методе finalize нет переназначения объекта, если это произойдет, объект останется в живых и произойдет воскресение, что технически возможно.
В документации для OutOfMemoryError он заявляет, что он не будет выброшен, если виртуальная машина не смогла восстановить память после полной сборки мусора. Поэтому, если вы продолжите выделять память до тех пор, пока не получите ошибку, вы уже принудительно выполните полную сборку мусора.
Предположительно, вопрос, который вы действительно хотели задать, заключался в том, "как я могу восстановить память, которую, как мне кажется, я должен вернуть, путем сбора мусора?"
Полезно для партии / crontab:
jdk1.7.0 / bin / jcmd
Увидеть:
Чтобы вручную запросить GC (не из System.gc()):
- Перейдите в папку bin в JDK, например, -C:\Program Files\Java\jdk1.6.0_31\bin
- Откройте jconsole.exe
- Подключитесь к нужному локальному процессу.
- Перейдите на вкладку памяти и нажмите выполнить GC.
Как заставить Java GC
Хорошо, вот несколько разных способов заставить Java GC.
- Нажмите JConsole
Perform GC
кнопка - Используйте JMap
jmap -histo:live 7544
команда, где 7544 - идентификатор - Вызовите диагностическую консоль Java
jcmd 7544 GC.run
команда - Вызов
System.gc();
в вашем коде - Вызов
Runtime.getRuntime().gc()
; в вашем коде
Ни одна из этих работ
Вот маленький грязный секрет. Ни один из них не будет работать гарантированно. Вы действительно не можете заставить Java GC.
Алгоритмы сборки мусора Java не являются детерминированными, и хотя все эти методы могут мотивировать JVM выполнять сборку мусора, на самом деле вы не можете заставить это сделать. Если в JVM происходит слишком много событий и операция остановки мира невозможна, эти команды либо выдадут ошибку, либо запустятся, но сборщик мусора на самом деле не произойдет.
if (input.equalsIgnoreCase("gc")) {
System.gc();
result = "Just some GC.";
}
if (input.equalsIgnoreCase("runtime")) {
Runtime.getRuntime().gc();
result = "Just some more GC.";
}
Исправить чертову проблему
Если у вас есть проблема с утечкой памяти или выделением объектов, исправьте ее. Сидя пальцем на Java Mission Control
Force Java GC
кнопка только пинает банку по дороге. Профилируйте свое приложение с помощью Java Flight Recorder, просмотрите результаты в VisualVM или JMC и устраните проблему. Попытка заставить Java GC - игра для дураков.
.gc - кандидат на исключение в будущих выпусках - инженер Sun однажды заметил, что, возможно, менее двадцати человек в мире действительно знают, как использовать.gc() - вчера вечером я несколько часов работал над центральным / критическим структура данных, использующая данные, сгенерированные SecureRandom, где-то чуть дальше 40000 объектов, виртуальная машина замедлится, как если бы в ней не было указателей. Очевидно, что он подавлял 16-битные таблицы указателей и демонстрировал классическое поведение "сбойного механизма".
Я пытался -Xms и так далее, продолжал немного вертеться, пока не достигнет 57, ххх что-то. Затем он будет запускать gc, скажем, с 57 127 до 57 128 после gc () - примерно в темпе вздутия кода в лагере Easy Money.
Ваш дизайн нуждается в фундаментальной переработке, возможно, в подходе с раздвижными окнами.
Спецификация JVM не говорит ничего конкретного о сборке мусора. Благодаря этому поставщики могут свободно внедрять GC по-своему.
Таким образом, эта неопределенность вызывает неопределенность в поведении сборки мусора. Вы должны проверить детали JVM, чтобы узнать о подходах / алгоритмах сбора мусора. Также есть опции для настройки поведения.
Если вам нужно форсировать сборку мусора, возможно, вам следует подумать о том, как вы управляете ресурсами. Вы создаете большие объекты, которые сохраняются в памяти? Вы создаете большие объекты (например, графические классы), которые имеют Disposable
интерфейс и не звонить dispose()
когда с этим покончили? Вы объявляете что-то на уровне класса, что вам нужно только в одном методе?
Было бы лучше, если бы вы описали причину, по которой вам нужна сборка мусора. Если вы используете SWT, вы можете распоряжаться такими ресурсами, как Image
а также Font
освободить память. Например:
Image img = new Image(Display.getDefault(), 16, 16);
img.dispose();
Есть также инструменты для определения нераспределенных ресурсов.
Мы можем вызвать
jmap -histo:live <pid>
используя среду выполнения java. Это заставит полный сборщик мусора в куче пометить все живые объекты.
public static void triggerFullGC() throws IOException, InterruptedException {
String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
Process process = Runtime.getRuntime().exec(
String.format("jmap -histo:live %s", pid)
);
System.out.println("Process completed with exit code :" + process.waitFor());
}
Другой вариант - не создавать новые объекты.
Объединение объектов не требуется, чтобы уменьшить необходимость GC в Java.
Пул объектов обычно не будет быстрее создания объектов (особенно для легких объектов), но он быстрее, чем сборщик мусора. Если вы создали 10000 объектов, и каждый объект был 16 байтов. Это 160 000 байтов, которые GC должен восстановить. С другой стороны, если вам не нужны все 10000 одновременно, вы можете создать пул для переработки / повторного использования объектов, что устраняет необходимость в создании новых объектов и устраняет необходимость в 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) { }
}
Использование Runtime.getRuntime().gc()
или используйте служебный метод System.gc()
Я хотел заставить gc, потому что мой код был заморожен в течение длительного времени, когда это произошло. Цель - сгладить заряд, регулярно вызывая gc. Перечисленные решения ничего не форсируют в моей среде.
Так:
- Я запрашиваю память для временной переменной,
- просто, по приращениям,
- и контролировать память и останавливать операцию, как только gc срабатывает.
Это работает легко, но вы должны настроить.
(Извините, я не могу добавить это как комментарий, так как у меня недостаточно очков репутации.)
Однажды я взял интервью у компании по обработке изображений для работы на Java (хотя у меня не было никакого / большого опыта Java).
Их история о горе, которая была мне интересна, заключалась в том, что их программа выделяла ОГРОМНЫЙ буфер для изображения. Однажды, они закончили с изображением и памятью, они будут его уважать. Зная, что им действительно необходимо восстановить память раньше, чем позже, они пытались использовать явный вызов Java gc() в тихое время программы. К сожалению, gc() является просто предложением для среды выполнения Java и не имеет никакого эффекта.
Насколько я помню, им пришлось самостоятельно перерабатывать память.
Я провел несколько экспериментов (см. https://github.com/mikenakis/ForcingTheJvmToGarbageCollect), испробовав около дюжины различных способов выполнения сборки мусора, включая способы, описанные в этом ответе, и другие, и я обнаружил, что нет абсолютно никакого способа чтобы детерминистически заставить JVM выполнить полную сборку мусора. Даже лучшие ответы на этот вопрос лишь частично успешны, поскольку лучшее, чего они достигают, — это некоторая сборка мусора, но никогда не гарантируется полная сборка мусора.
Мои эксперименты показали, что следующий фрагмент кода дает наилучшие (наименее плохие) результаты:
public static void ForceGarbageCollection()
{
long freeMemory = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
for( ; ; )
{
Runtime.getRuntime().gc();
Runtime.getRuntime().runFinalization();
long newFreeMemory = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
if( newFreeMemory == freeMemory )
break;
freeMemory = newFreeMemory;
sleep( 10 );
}
}
Где
private static void sleep( int milliseconds )
{
try
{
Thread.sleep( milliseconds );
}
catch( InterruptedException e )
{
throw new RuntimeException( e );
}
}
К сожалению, это число в том
При этом в контролируемой среде, где это
На мой взгляд, тот факт, что виртуальная машина Java не предоставляет средств для принудительной, безусловной, детерминированной, абсолютно тщательной сборки мусора по требованию, делает ее СЛОМАННОЙ.
Иными словами, создатели JVM настолько полны высокомерия, что думают, будто они лучше нас знают, хотим ли мы этого или должны хотеть этого. Не будь таким высокомерным. Если что-то работает как по волшебству, то должны быть предусмотрены какие-то средства обхода волшебства.
Я хотел бы добавить кое-что здесь. Обратите внимание, что Java работает на виртуальной машине, а не на реальной машине. Виртуальная машина имеет свой собственный способ связи с машиной. Это может меняться от системы к системе. Теперь, когда мы вызываем GC, мы просим виртуальную машину Java вызвать сборщик мусора.
Поскольку сборщик мусора работает с виртуальной машиной, мы не можем заставить ее выполнить очистку тут же. Скорее, мы ставим наш запрос в очередь на сборщик мусора. Это зависит от виртуальной машины, после определенного времени (это может измениться от системы к системе, как правило, когда пороговая память, выделенная для JVM заполнена), фактическая машина освободит пространство.:D
Следующий код взят из метода 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
Есть некоторый косвенный способ заставить сборщик мусора. Вам просто нужно заполнить кучу временными объектами до момента запуска сборщика мусора. Я сделал класс, который заставляет сборщик мусора таким образом:
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.
Если у вас заканчивается память и вы получаете OutOfMemoryException
Вы можете попробовать увеличить объем кучи, доступной для Java, запустив программу с java -Xms128m -Xmx512m
вместо просто java
, Это даст вам начальный размер кучи 128 МБ и максимум 512 МБ, что намного больше, чем стандартные 32 МБ /128 МБ.
В OracleJDK 10 с G1 GC один вызов System.gc()
заставит GC очистить старую коллекцию. Я не уверен, что GC запускается немедленно. Тем не менее, GC не очистит коллекцию Young, даже если System.gc()
вызывается много раз в цикле. Чтобы получить сборщик мусора для очистки коллекции Young, вы должны выделить в цикле (например, new byte[1024]
) без звонка System.gc()
, призвание System.gc()
по какой-то причине мешает GC очистить коллекцию Young.
На самом деле, я не понимаю тебя. Но чтобы быть ясным в "Создании бесконечных объектов", я имел в виду, что в моей большой системе есть какой-то кусок кода, создающий объекты, которые обрабатывают и живые в памяти, на самом деле я не мог получить этот кусок кода, просто жест!
Это правильно, только жест. У вас есть почти стандартные ответы, которые уже даны несколькими авторами. Давайте возьмем это по одному:
- Я не мог получить этот кусок кода на самом деле
Правильно, фактического jvm нет - такова только спецификация, связка компьютерных наук, описывающая желаемое поведение... Я недавно начал копаться в инициализации объектов Java из нативного кода. Чтобы получить то, что вы хотите, единственный способ сделать то, что называется агрессивным обнулением. Ошибки, если они сделаны неправильно, настолько плохи, что мы должны ограничиться первоначальным объемом вопроса:
- какой-то кусок кода в моей большой системе делает создание объектов
Большинство постеров здесь предполагают, что вы говорите, что работаете с интерфейсом, если это так, мы должны были бы увидеть, передается ли вам весь объект или один элемент за раз.
Если вам больше не нужен объект, вы можете присвоить объекту нулевое значение, но если вы ошиблись, возникнет исключение нулевого указателя. Бьюсь об заклад, вы можете добиться лучшей работы, если вы используете NIO
Каждый раз, когда вы, я или кто-либо еще получаете: "Пожалуйста, мне это ужасно нужно". Это почти универсальный предвестник почти полного уничтожения того, над чем вы пытаетесь работать… напишите нам небольшой пример кода, очищающий от него любое Фактический код используется и покажите нам свой вопрос.
Не расстраивайся. Часто это решает то, что ваш dba использует купленный где-то пакет, и оригинальный дизайн не настроен для массивных структур данных.
Это очень распространено.
FYI
Вызов метода System.runFinalizersOnExit(true) гарантирует, что методы финализатора будут вызваны до завершения работы Java. Однако этот метод по своей сути небезопасен и является устаревшим. Альтернативой является добавление "ловушек отключения" с помощью метода Runtime.addShutdownHook.
Масаррат Сиддики
Лучший (если не единственный) способ заставить GC
было бы написать обычай JVM
, Я верю Garbage collectors
являются подключаемыми, так что вы можете просто выбрать одну из доступных реализаций.
Если вы используете JUnit и Spring, попробуйте добавить это в каждый тестовый класс:
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)