Собран мусор потока Java или нет


85

Этот вопрос был размещен на каком-то сайте. Я не нашел там правильных ответов, поэтому снова публикую здесь.

public class TestThread {
    public static void main(String[] s) {
        // anonymous class extends Thread
        Thread t = new Thread() {
            public void run() {
                // infinite loop
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                    // as long as this line printed out, you know it is alive.
                    System.out.println("thread is running...");
                }
            }
        };
        t.start(); // Line A
        t = null; // Line B
        // no more references for Thread t
        // another infinite loop
        while (true) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
            }
            System.gc();
            System.out.println("Executed System.gc()");
        } // The program will run forever until you use ^C to stop it
    }
}

Мой вопрос не в остановке потока. Разрешите перефразировать свой вопрос. Строка A (см. Код выше) запускает новый поток; и строка B делает ссылку на поток нулевой. Итак, JVM теперь имеет объект потока (который находится в рабочем состоянии), на который не существует ссылки (как t = null в строке B). Итак, у меня вопрос: почему этот поток (который больше не имеет ссылки в основном потоке) продолжает работать до тех пор, пока не будет запущен основной поток. Насколько я понимаю, объект потока должен был быть обработан сборщиком мусора после строки B. Я пытался запустить этот код в течение 5 минут и более, запрашивая среду выполнения Java для запуска GC, но поток просто не останавливается.

Надеюсь, на этот раз и код, и вопрос ясны.

Ответы:


124

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

Подумайте, почему ваш основной поток не собирает мусор, никто не ссылается на него.


17
Этот ответ в его нынешнем виде поднимает вопрос, можно ли вообще использовать сборку мусора (после завершения). Поскольку этот вопрос помечен как дубликат этого , следует отметить, что потоки больше не будут помечены как «корни сборки мусора» после их завершения, и, таким образом, они станут доступными для GC.
bluenote10

13
Последнее предложение является проницательным: «почему ваша основная ветка не является мусором ...».
Determinant

Итак, будет ли удаленный поток (который был присоединен) больше не считаться корневым? Я почти уверен, что да, но я вижу странные вещи в моем приложении под профилировщиком, и это заставило меня задуматься ...
Groostav

1
Чем больше ответов я читаю, тем больше я запутываюсь, но да, почему основной поток не собирает мусор, стоит задуматься. Но возвращаясь к основному вопросу, если дочерние потоки создают некоторые объекты, он не будет GCed, поскольку родительский поток все еще работает и содержит ссылки дочерних потоков, даже если они «закончились» (!! ??)
Рахул Кумар

@Groostav Присоединенный поток не запущен, поэтому он не является корнем сборки мусора. Но когда вызывается родительский поток child.join(), он ссылается на child. Если эта ссылка (и любая другая) не отбрасывается, дочерний поток не может быть GCed.
Blaisorblade

23

Как было объяснено, запущенные потоки по определению невосприимчивы к GC. GC начинает свою работу со сканирования «корней», которые считаются всегда достижимыми; Корни включают глобальные переменные («статические поля» в Java-talk) и стеки всех запущенных потоков (можно представить, что стек работающего потока ссылается на соответствующий Threadэкземпляр).

Однако вы можете сделать поток потоком «демона» (см Thread.setDaemon(boolean). Ресурсы ). Поток демона не более сборщик мусора, чем поток, не являющийся демоном, но JVM завершается, когда все запущенные потоки являются демонами. Один из способов представить это состоит в том, что каждый поток, когда он завершается, проверяет, остались ли какие-нибудь запущенные потоки, не являющиеся демонами; в противном случае завершающийся поток System.exit()вызывает вызов, который завершает JVM ( завершая запущенные потоки демона). Это не проблема, связанная с GC; в некотором смысле потоки распределяются вручную. Однако именно так JVM может терпеть полу-мошеннические потоки. Обычно это используется для Timerэкземпляров.


19

JVM имеет ссылку на все запущенные потоки.

Ни один поток (или то, на что он ссылается) не будет собираться сборщиком мусора, пока он еще выполняется.


13

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

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

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