Главная страница
Навигация по странице:

  • Initialization

  • Что такое JIT

  • Виды ссылок в Java

  • WeakReference , SoftReference

  • Для чего нужен сборщик мусора

  • Как работает сборщик мусора

  • Какие разновидности сборщиков мусора реализованы в виртуальной

  • Serial (последовательный)

  • -XX:+UseSerialGC .•Parallel (параллельный)

  • -XX:+UseParallelGC .•Concurrent Mark Sweep (CMS)

  • -XX:+UseConcMarkSweepGC .•Garbage-First (G1)

  • -XX:+UseG1GC . Опишите алгоритм работы какого-нибудь сборщика мусора

  • Young generation

  • Mark (пометка)

  • Что такое finalize() Зачем он нужен

  • ОглавлениеCore


    Скачать 1.53 Mb.
    НазваниеОглавлениеCore
    Дата17.05.2023
    Размер1.53 Mb.
    Формат файлаpdf
    Имя файлаpolnaya_metodichka (1).pdf
    ТипДокументы
    #1138113
    страница5 из 25
    1   2   3   4   5   6   7   8   9   ...   25
    Процесс загрузки класса состоит из трех частей:

    Loading – на этой фазе происходит поиск и физическая загрузка файла класса в определенном источнике (в зависимости от загрузчика). Этот процесс определяет базовое представление класса в памяти. На этом этапе такие понятия как
    «методы», «поля» и т. д. пока неизвестны.

    Linking – процесс, который может быть разбит на 3 части:
    Bytecode verification – проверка байт-кода на соответствие требованиям,
    определенным в спецификации JVM;
    Class preparation – создание и инициализация необходимых структур,
    используемых для представления полей, методов, реализованных интерфейсов и т.п., определенных в загружаемом классе;
    Resolving – загрузка набора классов, на которые ссылается загружаемый класс.

    Initialization – вызов статических блоков инициализации и присваивание полям класса значений по умолчанию.
    Динамическая загрузка классов в Java имеет ряд особенностей:

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

    проверка корректности загружаемого кода (type safeness). Все действия,
    связанные с контролем использования типов, производятся только во время
    загрузки класса, позволяя избежать дополнительной нагрузки во время выполнения кода.

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

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

    явный: вызов ClassLoader.loadClass() или Class.forName() (по умолчанию используется загрузчик, создавший текущий класс, но есть возможность и явного указания загрузчика);

    неявный: когда для дальнейшей работы приложения требуется ранее не использованный класс, JVM инициирует его загрузку.
    Что такое JIT?
    JIT-компиляция (англ. Just-in-time compilation, компиляция «на лету»), динамическая компиляция (англ. dynamic translation) – технология увеличения производительности программных систем, использующих байт-код, путем компиляции байт-кода в машинный код или в другой формат непосредственно во время работы программы.
    Виды ссылок в Java
    В Java существует 4 типа ссылок. Особенности каждого типа ссылок связаны с работой
    Garbage Collector.
     сильные (strong reference);
     мягкие (SoftReference);
     слабые (WeakReference);
     фантомные (PhantomReference).
    Отличия между слабыми, мягкими, фантомными и обычными ссылками в
    Java
    «Слабые» ссылки и «мягкие» ссылки (WeakReference, SoftReference) были добавлены в
    Java API давно. Ссылочные классы особенно важны в контексте сборки мусора. Сборщик мусора сам освобождает память, занимаемую объектами, но решение об освобождении памяти он принимает, исходя из типа имеющихся на объект ссылок.
    Главное отличие SoftReference от WeakReference в том, как сборщик с ними будет работать.
    Он может удалить объект в любой момент, если на него указывают только weak-ссылки, с другой стороны объекты с soft-ссылкой будут собраны только когда JVM очень нужна память.
    Благодаря таким особенностям ссылочных классов каждый из них имеет свое применение.
    SoftReference можно использовать для реализации кэшей, и когда JVM понадобится память,
    она освободит ее за счет удаления таких объектов. А WeakReference отлично подойдут для
    хранения метаданных, например, для хранения ссылки на ClassLoader. Если нет классов для загрузки, то нет смысла хранить ссылку на ClassLoader, слабая ссылка делает ClassLoader доступным для удаления как только мы назначим ее вместо сильной ссылки (Strong reference).
    Фантомные ссылки – третий тип ссылок, доступных в пакете java.lang.ref. Phantom- ссылки представлены классом java.lang.ref.PhantomReference. Объект, на который указывают только phantom-ссылки, может быть удален сборщиком в любой момент. Phantom-ссылка создается точно так же, как weak или soft.
    DigitalCounter digit = new DigitalCounter(); // digit reference variable has
    strong reference
    PhantomReference phantom = new PhantomReference(digit); // phantom reference
    digit = null;
    Как только обнулите strong-ссылки на объект DigitalCounter, сборщик мусора удалит его в любой момент, так как теперь на него ведут только phantom-ссылки.
    Классом ReferenceQueue можно воспользоваться при создании объекта класса
    WeakReference, SoftReference или PhantomReference:
    ReferenceQueue refQueue = new ReferenceQueue(); //reference will be stored in
    this queue for cleanup
    DigitalCounter digit = new DigitalCounter();
    PhantomReference phantom = new PhantomReference(digit, refQueue);
    Ссылка на объект будет добавлена в ReferenceQueue, и можно будет контролировать состояние ссылок путем опроса ReferenceQueue. Жизненный цикл Object хорошо представлен на диаграмме:
    Правильное использование ссылок поможет при сборке мусора, и в результате получим более гибкое управление памятью в Java.
    https://javarush.ru/groups/posts/1267-otlichija-mezhdu-slabihmi-mjagkimi-fantomnihmi-i- obihchnihmi-ssihlkami-v-java
    Для чего нужен сборщик мусора?
    Сборщик мусора (Garbage Collector) должен делать всего две вещи:

    находить мусор – неиспользуемые объекты (объект считается неиспользуемым,
    если ни одна из сущностей в коде, выполняемом в данный момент, не содержит ссылок на него, либо цепочка ссылок, которая могла бы связать объект с некоторой сущностью приложения, обрывается);


    освобождать память от мусора.
    Существует два подхода к обнаружению мусора:

    Reference counting;

    Tracing.
    Reference counting (подсчет ссылок). Суть этого подхода состоит в том, что каждый объект имеет счетчик. Счетчик хранит информацию о том, сколько ссылок указывает на объект.
    Когда ссылка уничтожается, счетчик уменьшается. Если значение счетчика равно нулю,
    объект можно считать мусором. Главным минусом такого подхода является сложность обеспечения точности счетчика. Также при таком подходе сложно выявлять циклические зависимости (когда два объекта указывают друг на друга, но ни один живой объект на них не ссылается), что приводит к утечкам памяти.
    Главная идея подхода Tracing (трассировка) состоит в утверждении, что живыми могут считаться только те объекты, до которых можно добраться из корневых точек (GC Root) и те объекты, которые доступны с живого объекта. Все остальное – мусор.
    Существует 4 типа корневых точек:

    локальные переменные и параметры методов;

    потоки;

    статические переменные;

    ссылки из JNI.
    Самое простое java-приложение будет иметь корневые точки:

    локальные переменные внутри метода main() и параметры метода main();

    поток, который выполняет main();

    статические переменные класса, внутри которого находится метод main().
    Таким образом, если представим все объекты и ссылки между ними как дерево, то нужно будет пройти с корневых узлов (точек) по всем ребрам. При этом узлы, до которых сможем добраться – не мусор, все остальные – мусор. При таком подходе циклические зависимости легко выявляются. HotSpot VM использует именно такой подход.
    Для очистки памяти от мусора существуют два основных метода:

    Copying collectors;

    Mark-and-sweep
    При подходе copying collectors память делится на две части «from-space» и «to-space», при этом сам принцип работы такой:

    объекты создаются в «from-space»;

    когда «from-space» заполняется, приложение приостанавливается;

    запускается сборщик мусора, находятся живые объекты в «from-space» и копируются в «to-space»;

    когда все объекты скопированы, «from-space» полностью очищается;

    «to-space» и «from-space» меняются местами.

    Главный плюс такого подхода в том, что объекты плотно забивают память. Минусы подхода:

    приложение должно быть остановлено на время, необходимое для полного прохождения цикла сборки мусора;

    в худшем случае (когда все объекты живые) «form-space» и «to-space» будут обязаны быть одинакового размера.
    Алгоритм работы mark-and-sweep можно описать так:

    объекты создаются в памяти;

    в момент, когда нужно запустить сборщик мусора, приложение приостанавливается;

    сборщик проходится по дереву объектов, помечая живые объекты;

    сборщик проходится по всей памяти, находя все не отмеченные куски памяти и сохраняя их в «free list»;

    когда новые объекты начинают создаваться, они создаются в памяти, доступной во «free list».
    Минусы этого способа:

    приложение не работает, пока происходит сборка мусора;

    время остановки напрямую зависит от размеров памяти и количества объектов;

    если не использовать «compacting», память будет использоваться неэффективно.
    Сборщики мусора HotSpot VM используют комбинированный подход Generational Garbage
    Collection, который позволяет использовать разные алгоритмы для разных этапов сборки мусора. Этот подход основывается на том, что:

    большинство создаваемых объектов быстро становятся мусором;

    существует мало связей между объектами, которые были созданы в прошлом и только что созданными объектами.
    Как работает сборщик мусора?
    Механизм сборки мусора – это процесс освобождения места в куче для возможности добавления новых объектов.
    Объекты создаются с помощью оператора new, тем самым присваивая объекту ссылку. Для окончания работы с объектом достаточно перестать на него ссылаться, например, присвоив переменной ссылку на другой объект или значение null; прекратить выполнение метода,
    чтобы его локальные переменные завершили свое существование естественным образом.
    Объекты, ссылки на которые отсутствуют, принято называть мусором (garbage), который будет удален.
    Виртуальная машина Java, применяя механизм сборки мусора, гарантирует, что любой объект, обладающий ссылками, остается в памяти – все объекты, которые недостижимы из исполняемого кода, ввиду отсутствия ссылок на них, удаляются с высвобождением отведенной для них памяти. Точнее говоря, объект не попадает в сферу действия процесса сборки мусора, если он достижим посредством цепочки ссылок, начиная с корневой (GC
    Root) ссылки, т. е. ссылки, непосредственно существующей в выполняемом коде.

    Память освобождается сборщиком мусора по его собственному «усмотрению». Программа может успешно завершить работу, не исчерпав ресурсов свободной памяти или даже не приблизившись к этой черте, и поэтому ей так и не потребуются «услуги» сборщика мусора.
    Мусор собирается системой автоматически без вмешательства пользователя или программиста, но это не значит, что этот процесс не требует внимания вовсе. Необходимость создания и удаления большого количества объектов существенным образом сказывается на производительности приложений, и если быстродействие программы является важным фактором, следует тщательно обдумывать решения, связанные с созданием объектов. Это,
    в свою очередь, уменьшит и объем мусора, подлежащего утилизации.
    Какие разновидности сборщиков мусора реализованы в виртуальной
    машине HotSpot?
    Java HotSpot VM предоставляет разработчикам на выбор четыре различных сборщика мусора:

    Serial (последовательный) – самый простой вариант для приложений с небольшим объемом данных и не требовательных к задержкам. На данный момент используется сравнительно редко, но на слабых компьютерах может быть выбран виртуальной машиной в качестве сборщика по умолчанию. Использование
    Serial GC включается опцией -XX:+UseSerialGC.

    Parallel (параллельный) – наследует подходы к сборке от последовательного сборщика, но добавляет параллелизм в некоторые операции, а также возможности по автоматической подстройке под требуемые параметры производительности. Параллельный сборщик включается опцией
    -XX:+UseParallelGC.

    Concurrent Mark Sweep (CMS) – нацелен на снижение максимальных задержек путем выполнения части работ по сборке мусора параллельно с основными потоками приложения. Подходит для работы с относительно большими объемами данных в памяти. Использование CMS GC включается опцией
    -XX:+UseConcMarkSweepGC.

    Garbage-First (G1) – создан для замены CMS, особенно в серверных приложениях, работающих на многопроцессорных серверах и оперирующих большими объемами данных. G1 включается опцией -XX:+UseG1GC.
    Опишите алгоритм работы какого-нибудь сборщика мусора,
    реализованного в виртуальной машине HotSpot
    Serial Garbage Collector (последовательный сборщик мусора) был одним из первых сборщиков мусора в HotSpot VM. Во время работы этого сборщика приложение приостанавливается и продолжает работать только после прекращение сборки мусора.
    Память приложения делится на три пространства:

    Young generation. Объекты создаются именно в этом участке памяти.

    Old generation. В этот участок памяти перемещаются объекты, которые переживают «minor garbage collection».

    Permanent generation. Тут хранятся метаданные об объектах, Class data sharing
    (CDS), пул строк (String pool). Permanent-область делится на две: только для
    чтения и для чтения-записи. Очевидно, что в этом случае область только для чтения не чистится сборщиком мусора никогда.
    Область памяти Young generation состоит из трех областей: Eden и двух меньших по размеру Survivor spacesTo space и From space. Большинство объектов создаются в области Eden, за исключением очень больших объектов, которые не могут быть размещены в ней и поэтому сразу размещаются в Old generation. В Survivor spaces перемещаются объекты, которые пережили по крайней мере одну сборку мусора, но еще не достигли порога
    «старости» (tenuring threshold), чтобы быть перемещенными в Old generation.
    Когда Young generation заполняется, то в этой области запускается процесс легкой сборки
    (minor collection), в отличие от процесса сборки, проводимого над всей кучей (full collection).
    Он происходит следующим образом: в начале работы одно из Survivor spaces – To space –
    является пустым, а другое – From space – содержит объекты, пережившие предыдущие сборки. Сборщик мусора ищет живые объекты в Eden и копирует их в To space, а затем копирует туда же и живые «молодые» (то есть не пережившие еще заданное число сборок мусора) объекты из From space. Старые объекты из From space перемещаются в Old generation. После легкой сборки From space и To space меняются ролями, область Eden становится пустой, а число объектов в Old generation увеличивается.
    Если в процессе копирования живых объектов To space переполняется, то оставшиеся живые объекты из Eden и From space, которым не хватило места в To space, будут перемещены в Old generation, независимо от того, сколько сборок мусора они пережили.
    Поскольку при использовании этого алгоритма сборщик мусора просто копирует все живые объекты из одной области памяти в другую, то такой сборщик мусора называется copying
    (копирующий). Очевидно, что для работы копирующего сборщика мусора у приложения всегда должна быть свободная область памяти, в которую будут копироваться живые объекты, и такой алгоритм может применяться для областей памяти сравнительно небольших по отношению к общему размеру памяти приложений. Young generation как раз удовлетворяет этому условию (по умолчанию на машинах клиентского типа эта область занимает около 10% кучи (значение может варьироваться в зависимости от платформы).
    Однако для сборки мусора в Old generation, занимающем большую часть всей памяти,
    используется другой алгоритм.
    В Old generation сборка мусора происходит с использованием алгоритма mark-sweep- compact, который состоит из трех фаз. В фазе Mark (пометка) сборщик мусора помечает все живые объекты, затем, в фазе Sweep (очистка) все не помеченные объекты удаляются, а в фазе Сompact (уплотнение) все живые объекты перемещаются в начало Old generation, в результате чего свободная память после очистки представляет собой непрерывную область.
    Фаза уплотнения выполняется для того, чтобы избежать фрагментации и упростить процесс выделения памяти в Old generation.
    Когда свободная память представляет собой непрерывную область, то для выделения памяти под создаваемый объект можно использовать очень быстрый (около десятка машинных инструкций) алгоритм bump-the-pointer: адрес начала свободной памяти хранится в специальном указателе, и когда поступает запрос на создание нового объекта,
    код проверяет, что для нового объекта достаточно места, и, если это так, то просто увеличивает указатель на размер объекта.
    Последовательный сборщик мусора отлично подходит для большинства приложений,
    использующих до 200 Мб кучи, работающих на машинах клиентского типа и не предъявляющих жестких требований к величине пауз, затрачиваемых на сборку мусора. В то
    же время модель «stop-the-world» может вызвать длительные паузы в работе приложения при использовании больших объемов памяти. Кроме того, последовательный алгоритм работы не позволяет оптимально использовать вычислительные ресурсы компьютера, и последовательный сборщик мусора может стать узким местом при работе приложения на многопроцессорных машинах.
    Что такое finalize()? Зачем он нужен?
    Через вызов метода finalize() JVM реализуется функциональность, аналогичная функциональности деструкторов в С++, используемых для очистки памяти перед возвращением управления операционной системе. Данный метод вызывается при уничтожении объекта сборщиком мусора (garbage collector), и, переопределяя finalize(),
    можно запрограммировать действия, необходимые для корректного удаления экземпляра класса – например, закрытие сетевых соединений, соединений с базой данных, снятие блокировок на файлы и т. д.
    После выполнения этого метода объект должен быть повторно собран сборщиком мусора (и это считается серьезной проблемой метода finalize() т. к. он мешает сборщику мусора освобождать память). Вызов этого метода не гарантируется, т. к. приложение может быть завершено до того, как будет запущена сборка мусора.
    Объект не обязательно будет доступен для сборки сразу же – метод finalize() может сохранить куда-нибудь ссылку на объект. Подобная ситуация называется
    1   2   3   4   5   6   7   8   9   ...   25


    написать администратору сайта