как понять корень GC по используемому объекту монитора?

#java #garbage-collection #jvm

#java #сбор мусора #jvm

Вопрос:

Я хочу проверить Monitor Used объект как корень GC, код, подобный следующему

 package cn.jast.jvm.gcroot;

/**
 * Monitor Used - objects used as a monitor for synchronization
 *
 * Created by jast90 on 2021/1/9
 */
public class GCRootMonitorUsed {
    BigClass bigClass = new BigClass();
    BigClass lock = new BigClass();

    public static void main(String[] args) {
        PrintMemoryUtil.printMemory();
        GCRootMonitorUsed gcRootMonitorUsed = new GCRootMonitorUsed();
        System.out.println("after create big class");
        PrintMemoryUtil.printMemory();
        System.gc();
        System.out.println("first gc");
        PrintMemoryUtil.printMemory();
        synchronized (gcRootMonitorUsed.lock){
            gcRootMonitorUsed = null;
            System.gc();
            System.out.println("second gc");
            PrintMemoryUtil.printMemory();
            System.gc();
            System.out.println("third gc");
            PrintMemoryUtil.printMemory();
        }
    }

    /*
    // move to a java file as not inner class
    class BigClass{
        private int _10MB = 10 * 1024 * 1024;
        private byte[] memory = new byte[8 * _10MB];
    }*/
}
 
 package cn.jast.jvm.gcroot;

/**
 * Created by jast90 on 2021/1/9
 */
public class PrintMemoryUtil {

    public static void printMemory(){
        System.out.println("free is :" Runtime.getRuntime().freeMemory()/1024/1024   "M");
        System.out.println("total is :" Runtime.getRuntime().totalMemory()/1024/1024   "M");
    }
}

 

и результат:

 free is :120M
total is :123M
after create big class
free is :40M
total is :203M
first gc
free is :42M
total is :203M
second gc
free is :42M
total is :203M
third gc
free is :42M
total is :203M
 

ссылка

Комментарии:

1. Поток удерживает блокировку на поле экземпляра, поэтому, возможно, оно не может быть собрано. Что происходит без синхронизированного блока?

2. (хотя сборщик мусора не должен заботиться о том, что происходит с родительским объектом… возможно, его просто не беспокоит сбор этого объекта, поскольку в куче достаточно места)

3. > Поток удерживает блокировку на поле экземпляра, поэтому, возможно, оно не может быть собрано. Что происходит без синхронизированного блока? Это то, что я тестирую.

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

Ответ №1:

Экземпляры внутреннего класса BigClass имеют неявную ссылку на внешний класс GCRootMonitorUsed . Пока вы сохраняете ссылку на BigClass экземпляр, вы также сохраняете неявную ссылку на заключающий GCRootMonitorUsed экземпляр.

Синхронизация при включении gcRootMonitorUsed.lock создает эту сильную ссылку gcRootMonitorUsed.lock на все время выполнения синхронизированного блока и, следовательно, на GCRootMonitorUsed экземпляр.

Вся картина изменилась бы, если BigClass бы был статический вложенный класс, который не содержал бы ссылку на внешний класс.

Комментарии:

1. Возможно, стоит отметить, что это техническое ограничение, а не формальное. Синхронизация не требуется, чтобы иметь эффект, когда нет последующей синхронизации другим потоком (и класс не имеет finalize() метода, синхронизирующегося с объектом). Поведение может измениться, когда этот метод будет оптимизирован JVM.

Ответ №2:

Из вашей собственной ссылки:

введите описание изображения здесь

Предполагая, что эта ссылка верна, я бы интерпретировал ее как:

 synchronized(obj) {
  // obj used as monitor
}
// obj free to collect