Сборщик мусора не сразу собирает законченную нить
Вкратце: у меня есть поток, который завершен, но не сбор мусора.
В длинном: см. следующий пример кода:
public void saveSomething() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// heavy memory access (~400 MB), finishes after ~10sec
}
});
thread.start();
}
Хорошо, так что этот поток запускается и заканчивается через некоторое время. За это время используется большая память, и все в порядке. Но моя проблема:
По завершении потока память не устанавливается
И я не знаю, как я могу это обеспечить.
Готовые потоки, которые больше не используются, должны получить сбор мусора, насколько мне известно; но это, похоже, не происходит здесь: (
Смотрите этот скриншот от jvisualVM:
![enter image description here]()
1: я запускаю запуск потока, вызывая saveSomething()
2: Thread был закончен давно (я видел его путем отладки), и я нажал "Выполнить GC" в jvisualvm
Как вы можете видеть, после принудительного включения GC все работает так, как я хочу. Но это должно произойти автоматически. Как я могу это сделать, что я делаю неправильно?
Если вам нужна дополнительная информация, пожалуйста, спросите.
Примечание. Кажется, что через день (надеюсь, короче) память вернулась к нормальной жизни, может быть, GC просто "медленный" или, скорее, не очень часто приурочен?
Ответы
Ответ 1
Может быть, вы задаете неправильный вопрос? Я имею в виду: есть ли тот факт, что сбор мусора не происходит "немедленно" для вас?
Я уверен - когда вы начинаете другой такой поток, который нуждается в большой памяти, GC будет пинаться сам по себе.
Если эта "задержка" на самом деле является проблемой для вас, вы можете подумать о "глубоком погружении", чтобы понять, как GC действительно работает для вашей версии JVM; чтобы затем использовать множество опций командной строки, которые существуют для точной настройки поведения GC в соответствии с вашими потребностями.
Но если "задержка" в освобождении памяти (для других объектов Java) не является проблемой; то не начинайте его исправлять.
Ответ 2
Сбор мусора не происходит, как только поток завершается. Сбор мусора произойдет, когда это произойдет. Тот факт, что при принудительной сборке мусора с помощью visualvm поток и его ресурсы собираются правильно, все в порядке. Если вы подождете достаточно долго или делаете больше вещей, которые потребляют кучу, тогда в конечном итоге ваша нить будет GCed.
ИЗМЕНИТЬ
Единственное предостережение в том, что работающий поток, даже без ссылок, не будет собирать мусор.
Ответ 3
Я согласен с предыдущими ответами. Эта программа демонстрирует тот факт, что "проблема" не специфична для создания потоков. В общем случае gc выполняется по необходимости или при запросе.
public class Test {
public static long memInUse(){
Runtime r = Runtime.getRuntime();
return r.totalMemory()-r.freeMemory();
}
public static void main(String[] args){
System.out.println("Initial memory: "+memInUse());
long[] bigArray = new long[1000000];
System.out.println("Memory after array allocation: "+memInUse());
long endTime = System.currentTimeMillis()+10000;
while(System.currentTimeMillis() < endTime){
System.out.println("While array exists: "+memInUse());
try{
Thread.sleep(1000);
}catch(InterruptedException e){
// Deliberately ignore the exception.
}
}
bigArray = null;
System.out.println("After null assignment: "+memInUse());
endTime = System.currentTimeMillis()+10000;
while(System.currentTimeMillis() < endTime){
System.out.println("While array reference null: "+memInUse());
try{
Thread.sleep(1000);
}catch(InterruptedException e){
// Deliberately ignore the exception.
}
}
System.gc();
System.out.println("After gc: "+memInUse());
}
}
Вывод:
Initial memory: 2684536
Memory after array allocation: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
While array exists: 10684552
After null assignment: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
While array reference null: 10684552
After gc: 1622584
Ответ 4
Вполне возможно, что JVM просто не нужно делать GC после завершения потока, поскольку в любом случае у него достаточно свободной кучи.
Он будет делать только GC, когда ему нужно освобождать память.
Лично я бы не стал беспокоиться об этом. Пока вы знаете, что память будет очищена, когда GC произойдет, тогда это будет хорошо.
Если вы действительно хотите это сделать, вам нужно будет выполнить явную System.GC - либо после завершения потока, либо в качестве последней строки в методе run() (если не будет никаких ссылок на большие данные на этом этапе).
Ответ 5
-
Свободная память - это потерянная память. Если у вас есть, скажем, 2 ГБ свободной памяти, то у вас также может быть меньше 2 ГБ. Освобождение памяти само по себе не выгодно, оно не будет автоматически ускорять работу.
-
Сбор мусора не является бесплатным. На самом деле это может быть очень интенсивно. Чем реже это происходит, тем лучше (с точки зрения загрузки процессора).
С учетом этого вы обычно не хотите, чтобы сборщик мусора сразу же удалялся, когда поток завершен. Если в данный момент вам не требуется больше ОЗУ, тогда нет смысла записывать циклы CPU на GC. Большую часть времени вы можете доверять JVM для запуска GC, когда это необходимо.
Бонус: ОЗУ, которое не используется программами, может использоваться для кеша вашей ОС. В Windows эта часть ОЗУ отображается как бесплатная, в Linux она появляется как используемая, но на самом деле она находится где-то посередине. Кэш не нужен, но он может повысить производительность. ОС будет управлять им автоматически: когда будет доступно больше бесплатной ОЗУ, больше можно выделить для кеша, а когда у вас мало памяти, размер кеша будет уменьшен. Поэтому иногда наличие большей свободной памяти может быть хорошим для производительности, но большую часть времени вам не нужно беспокоиться об этом.
Ответ 6
Как правило, объект становится пригодным для сбора мусора в Java в следующих случаях:
1) Все ссылки этого объекта явно заданы равными нулю, например. object = null
2) Объект создается внутри блока, а ссылка выходит из области действия после того, как элемент управления выйдет из этого блока.
3) Объект родительского объекта имеет значение null, если объект имеет ссылку на другой объект, и когда вы устанавливаете ссылку на объект контейнера null, дочерний или содержащий объект автоматически становится пригодным для сбора мусора.
4) Если у объекта есть только живые слабые ссылки через WeakHashMap, он будет иметь право на сбор мусора.
Существуют такие методы, как System.gc() и Runtime.gc(), которые используются для отправки запроса коллекции Мусора в JVM, но не гарантируют, что сбор мусора произойдет.
Чтобы ответить на ваш вопрос. вам нужно принудительно собрать мусор.
System.gc ();
System.runFinalization ();
Другой пример
public class GCTest {
public static void main(String[] args) throws InterruptedException {
A a = new A("white");
a = null;
Runtime.getRuntime().gc();
}
}
class A {
private String color;
public A(String color) {
this.color = color;
}
@Override
public void finalize() {
System.out.println(this.color + " cleaned");
}
}
Предупреждение. Это ужасная практика использования сборщика мусора, поскольку его использование может привести к перегрузке программного обеспечения, которое может быть даже хуже, чем в памяти, сборщик мусора имеет свои собственные поток, который невозможно контролировать плюс в зависимости от алгоритма, используемого gc, может занять больше времени и считается очень неэффективным, вы должны проверить свое программное обеспечение, если это худшее с помощью gc, потому что оно определенно сломано, хорошее решение должно не зависит от gc.