JVM (3) Определение жизни и смерти объекта и подробный алгоритм

Java JVM

Хорошая статья способна связать различные точки знаний логическими отношениями, делая людей одновременно просветленными и запоминающимися.

Введение. Имеют ли объекты другие состояния, кроме жизни и смерти? Настоящая смерть субъекта только переживает простое суждение? Как «спасти» умирающего субъекта от гибели? Каковы алгоритмы оценки жизни и смерти объекта? Эта статья поможет вам найти эти ответы вместе.

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

Введение в GC

ГК:Сборка мусора, в китайском переводе это сборка мусора.

История ГК

GC имеет долгую историю.Первоначальный алгоритм GC был выпущен в 1960 году (уже 60 лет) Джоном Маккарти, отцом Лиспа, очень известным хакером и отцом искусственного интеллекта, а также отцом GC.

Зачем изучать ГК?

1. Устранение проблем с переполнением и утечкой памяти.

2. Настройка системы для решения более узких мест параллелизма.

Роль ГК

1. Найдите мусор в пространстве памяти.

2. Перерабатывайте мусор.

Алгоритм суждения о жизни и смерти объекта

Первым шагом в сборке мусора является определение того, жив объект или нет.Сборщик мусора будет восстанавливать только «мертвые» объекты.

Алгоритм счетчика ссылок

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

преимущество:Простая реализация и высокая производительность.

недостаток:Обработка увеличения и уменьшения часто требует вычислений процессора, счетчик занимает много битов и тратит место впустую, и самый важный недостаток заключается в том, что он не может решить проблему циклических ссылок.

Поскольку алгоритм счетчика ссылок с трудом решает проблему циклических ссылок, основные виртуальные машины Java не используют алгоритм счетчика ссылок для управления памятью.

Давайте посмотрим на фрагмент циклического эталонного кода:

public class ReferenceDemo {
    public Object instance = null;
    private static final int _1Mb = 1024 * 1024;
    private byte[] bigSize = new byte[10 * _1Mb]; // 申请内存
    public static void main(String[] args) {
        System.out.println(String.format("开始:%d M",Runtime.getRuntime().freeMemory() / (1024 * 1024)));
        ReferenceDemo referenceDemo = new ReferenceDemo();
        ReferenceDemo referenceDemo2 = new ReferenceDemo();
        referenceDemo.instance = referenceDemo2;
        referenceDemo2.instance = referenceDemo;
        System.out.println(String.format("运行:%d M",Runtime.getRuntime().freeMemory() / (1024 * 1024)));
        referenceDemo = null;
        referenceDemo2 = null;
        System.gc(); // 手动触发垃圾回收
        System.out.println(String.format("结束:%d M",Runtime.getRuntime().freeMemory() / (1024 * 1024)));
    }
}

Результат запуска:

Старт: 117 м

Бег: 96 м

Конец: 119 М

Из результатов видно, что виртуальная машина не перерабатывает их из-за взаимных ссылок, а также показывает, что виртуальная машина реализована без использования счетчиков ссылок.

Алгоритмы анализа достижимости

В основных реализациях основных языков, таких как Java, C# и даже древний Lisp, для определения того, является ли объект живым, используются алгоритмы анализа достижимости.

Основная идея этого алгоритма состоит в том, чтобы использовать ряд объектов «GC Roots» в качестве отправной точки и начинать поиск с этих объектов, а путь поиска называется «цепочкой ссылок».

Когда объект не имеет какой-либо цепочки ссылок, связанной с корнями GC, это доказывает, что объект может быть переработан. Как показано ниже:

可达性分析算法

В Java доступен в виде списка объектов GC Roots:

  • Справочные объекты в стеке виртуальной машины Java.
  • Объект, на который ссылается JNI (то есть собственный метод в целом) в стеке собственных методов.
  • Ссылочный объект статической константы класса в области методов.
  • Ссылочный объект для константы в области методов.

Отношения между жизнью и смертью объектов и ссылок

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

До JDK1.2 определение ссылки было очень традиционным: если значение, хранящееся в данных ссылочного типа, представляло начальный адрес другого фрагмента памяти, говорили, что этот фрагмент памяти представляет собой ссылку.

Такое определение является чистым, но также и очень узким, и в этом случае объект либо упоминается, либо не упоминается, и ничего нельзя сделать с объектами между ними.

После JDK1.2 ссылка была расширена и разделена на:

  • Сильная ссылка

  • Мягкая ссылка

  • Слабая ссылка

  • Фантомная ссылка

Это также ответ на первый вопрос в начале статьи.Предметы не являются жизненно важными.Когда места достаточно, эти предметы можно сохранить.Если места недостаточно, эти предметы можно выбросить. Реализация многих функций кэширования также соответствует этому сценарию.

Сильные ссылки, мягкие ссылки, слабые ссылки и виртуальные ссылки — сильные стороны этих четырех видов ссылок расположены в порядке убывания.

Сильная цитата:Такие ссылки, как «Object obj = new Object()», которые встречаются в коде повсеместно, пока существует сильная ссылка, сборщик мусора никогда не вернет объект, на который указывает ссылка.

Мягкие ссылки:Это относительно сильная ссылка, которая ослабляет некоторые ссылки, что может привести к исключению объектов из некоторой сборки мусора.Только когда JVM считает, что памяти недостаточно, она попытается восстановить объект, на который указывает мягкая ссылка. JVM гарантирует, что объект, на который указывает программная ссылка, очищается, прежде чем выдавать ошибку OutOfMemoryError.

Слабая ссылка:Несущественный объект, но его сила слабее, чем у мягкой ссылки, объект, связанный со слабой ссылкой, может существовать только до следующей сборки мусора.

Фиктивная ссылка:Также известная как фантомная ссылка или фантомная ссылка, это самая слабая ссылочная связь. Невозможно получить экземпляр объекта через виртуальную ссылку. Цель установки виртуальной ссылки для объекта состоит только в том, чтобы получить ее, когда объект повторно используется коллектор.Системное оповещение.

Метка смерти и спасение

Недостижимый объект в алгоритме достижимости не "должен умереть". Чтобы действительно объявить объект мертвым, он должен пройти как минимум два процесса маркировки.

Если у объекта нет цепочки ссылок, подключенной к GC Roots после анализа достижимости, он будет помечен в первый раз и подвергнут скринингу, условием скрининга является необходимость выполнения объектом метода finalize().

Два условия выполнения метода finalize():

1. Переопределите метод finalize().

2. Метод finalize() ранее не вызывался, так как метод finalize() объекта может быть выполнен только один раз.

Если два вышеуказанных условия соблюдены, объект будет помещен в очередь F-Queue, а затем будет выполнен низкоприоритетным потоком Finalizer, созданным виртуальной машиной.

Самоспасение субъекта

Метод finalize() — это последний шанс для объекта избежать участи смерти. переменная класса или переменные-члены объекта.

Давайте посмотрим на конкретный код реализации:

public class FinalizeDemo {
    public static FinalizeDemo Hook = null;
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("执行finalize方法");
        FinalizeDemo.Hook = this;
    }
    public static void main(String[] args) throws InterruptedException {
        Hook = new FinalizeDemo();
        // 第一次拯救
        Hook = null;
        System.gc();
        Thread.sleep(500); // 等待finalize执行
        if (Hook != null) {
            System.out.println("我还活着");
        } else {
            System.out.println("我已经死了");
        }
        // 第二次,代码完全一样
        Hook = null;
        System.gc();
        Thread.sleep(500); // 等待finalize执行
        if (Hook != null) {
            System.out.println("我还活着");
        } else {
            System.out.println("我已经死了");
        }
    }
}

Результат выполнения:

Выполнить метод finalize

я все еще жив

я уже мертв

Как видно из результатов, метод finalize() любого объекта будет вызываться системой только один раз.

Не рекомендуется использовать метод finalize() для сохранения объектов., по следующим причинам:

1. Finalize() объекта может быть выполнен только один раз.

2. Дорого в эксплуатации.

3. Неопределенность велика.

4. Порядок вызова каждого объекта не может быть гарантирован.

Ссылаться на

"Глубокое понимание виртуальной машины Java"

«Алгоритмы и реализации сборки мусора»

※ Для того, чтобы написать хорошую техническую статью, за ней стоит «трудность» чтения двух книг. Писать не просто, поддержите!!!

наконец

Подпишитесь на официальный аккаунт, отправьте ключевое слово «gc» и получите учебные материалы «Алгоритм и реализация сборки мусора».