Java Thread Сміття зібрано чи ні


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 (див. Код вище) розпочинає новий Thread; і рядок B роблять посилання на потік нульовим. Отже, JVM тепер має Thread Object (який знаходиться в робочому стані), на який не існує посилання (як t = null у рядку B). Отже, моє питання полягає в тому, чому цей потік (який більше не має посилання в основному потоці) продовжує працювати, поки не запущений основний потік. На моє розуміння, об’єктом потоку мав бути зібраний сміття після рядка Б. Я намагався запустити цей код протягом 5 хвилин і більше, вимагаючи Java Runtime для запуску GC, але потік просто не зупиняється.

Сподіваюся, і код, і питання цього разу зрозумілі.

Відповіді:


124

Запущений потік вважається так званим коренем збору сміття, і це одна з речей, що утримує речі від збирання сміття. Коли збирач сміття визначає, чи є ваш об’єкт « доступним » чи ні, він завжди робить це, використовуючи набір коренів збирача сміття як контрольні точки.

Розгляньте це, чому ваша основна нитка не збирається сміття, ніхто також не посилається на неї.


17
Ця відповідь, як вона існує на сьогоднішній день, порушує питання, чи можна взагалі обробляти потоки (після їх закінчення). Оскільки це питання позначено як дублікат цього , слід зазначити, що нитки більше не будуть позначатися як "коріння збору сміття" після їх закінчення, і, таким чином, вони стають доступними для GC.
bluenote10

13
Останнє речення проникливе: "чому ваша основна нитка не сміття ...".
Визначник

отже, як подальші дії, чи утилізована нитка (та, яка була об’єднана) більше не вважатиметься коренем? Я майже впевнений, що відповідь так, але я бачу дивні речі в моїй програмі під профілем, і це
викликає у

1
Чим більше відповідей я прочитаю, тим більше заплутаюсь, але так, чому основна тема - не збирання сміття - це над чим задуматись. Але повертаючись до основного питання, якщо дочірні потоки створюють деякі об’єкти, він не отримає GCed, оскільки батьківський потік все ще працює і містить посилання на дочірні потоки, навіть якщо вони „закінчились” (!! ??)
Рахул Кумар

@Groostav Потік, до якого було приєднано, не запущений, тому він не є коренем збору сміття. Але коли батьківський потік викликав child.join(), він мав посилання на child. Якщо це посилання (та будь-яке інше) не відкидається, дочірній потік не може бути GCed.
Blaisorblade

23

Як було пояснено, запущені потоки, за визначенням, не захищені від ГХ. GC починає свою роботу зі сканування "коренів", які вважаються завжди доступними; коріння включають глобальні змінні ("статичні поля" в Java-talk) і стеки всіх запущених потоків (можна уявити, що стек запущеного потоку посилається на відповідний Threadекземпляр).

Однак ви можете зробити нитку потоком "демона" (див. Thread.setDaemon(boolean)). Демон-потік не збирається більше сміття, ніж недемон-потік, але JVM виходить, коли всі поточні потоки є демонами. Одним із способів уявити це є те, що кожен потік, коли він закінчується, перевіряє, чи залишаються деякі недемонові поточні потоки; якщо ні, завершувальний потік примушує System.exit()виклик, який виходить із JVM (вбиваючи запущені потоки демона). Це не проблема, пов’язана з ГК; певним чином, потоки виділяються вручну. Однак саме так JVM може переносити напівзахищені нитки. Це зазвичай використовується для Timerекземплярів.


19

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

Жодна нитка (або речі, на які вона посилається) не буде збирати сміття, поки вона все ще працює.


13

Потік - це не зібране сміття, оскільки є посилання на потоки, які ви не можете побачити. Наприклад, у системі виконання є посилання.

Коли Thread створено, він додається до поточної групи потоків. Ви можете отримати список потоків у поточній групі потоків, так що це ще один спосіб отримати посилання на нього.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.