Подтвердить что ты не робот

Сборщик мусора не сразу собирает законченную нить

Вкратце: у меня есть поток, который завершен, но не сбор мусора.

В длинном: см. следующий пример кода:

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 просто "медленный" или, скорее, не очень часто приурочен?

4b9b3361

Ответ 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.