1. Чем Java отличается от C++?
В С++ необходимо самому заботится об освобождении памяти. В Java этим занимается GC.
2. Что такое менеджер памяти?
Менеджер памяти — часть компьютерной программы (как прикладной, так и операционной системы), обрабатывающая запросы на выделение и освобождение оперативной памяти или (для некоторых архитектур ЭВМ) запросы на включение заданной области памяти в адресное пространство процессора.
Основное назначение менеджера памяти в первом смысле — реализация динамической памяти. Например, в языке C динамическое выделение памяти производится через функцию malloc.
3. Какой механизм используется в Java для управления памятью?
Распределение памяти в java реализует технология Java HotSpot от Oracle. Она обеспечивает динамическое выделение памяти для объектов и сборщиков мусора.
Память процесса делится на non-heap (до JDK 8 stack) и heap (куча):
В предыдущих выпусках Java Hotspot VM метаданные класса выделялись в Permanent Generation. Начиная с JDK 8, permanent generation было удалено, а метаданные класса размещены в собственной памяти. Объем собственной памяти, которую можно использовать для метаданных класса, по умолчанию не ограничен.
Stack (структура non-heap до JDK 8)
Permanent Generation — содержит необходимые для управления программой метаданные классов, в том числе метаданные о созданных объектах, методах и т.п.
Code Cache — используемая JVM память при включенной JIT-компиляции (в этой области памяти кешируется скомпилированный платформенно-зависимый код)
Heap - куча (тут и работает GC)
New (Yang) Generation - хранит короткоживущие объекты.
Eden Space — сюда аллоцируются среднестатистические объекты*. Если нет места запускается малая сборка мусора (minor GC).
Survivor Space — точнее их два, S1 и S2, и они меняются ролями. Хранятся перемещенные из Eden Space объекты, признанные живыми во время сборки мусора (без разницы малой или полной). Объекты, пережившие несколько сборок мусора, перемещаются в следующую сборку Tenured Generation.
Old (Tenured) Generation - хранит долгоживущие объекты. Когда данная область памяти заполняется, выполняется полная сборка мусора (full GC).
Алгоритм GC исходит из того предположения, что большинство java-объектов живут недолго. Быстро становятся мусором. От них необходимо довольно оперативно избавляться. Что и происходит в New Generation. Там сбор мусора гораздо чаще, чем в Old Generation, где хранятся долгоживущие объекты. После создания объект попадает в New Generation и имеет шанс попасть в Old Generation по прошествии нескольких циклов GC.
*объекты-акселераты, размер которых настолько велик, что создавать их в Eden, а потом таскать за собой по Survivor’ам слишком накладно, размещаются сразу в Tenured Generation.
4. Опишите процесс работы сборщика мусора.
При каждом создании объекта, происходит выделение памяти в eden и объект помещается туда. Как только в eden снижается место ниже установленного предела инициализируется сборка в eden. Происходит поиск живых объектов, до которых есть доступные из корневых объектов программы ссылки. Все живые объекты переносятся в область Survivor, остальные уничтожаются сборщиком. Так проходит несколько циклов до того, как не заполнится область survivor. Происходит чистка в этой области. Со временем часть объектов из survivor переходят в старое поколение до того как оно не заполнится и произойдет полная сборка.
5. Какие алгоритмы сборщика вы знаете?
· Serial GC
· Parallel GC
· Concurrent Mark Sweep (CMS)
· Garbage-First (G1)
· Z Garbage Collector (ZGC)
Serial
Когда нет места в Eden, запускается GC, живые объекты коприруются в S1. Вся область Eden очищается. S1 и S2 меняются местами. При последующих циклах в S1 будут записаны живые объекты как из Eden, так и из S2. После нескольких циклов обмена S1 и S2 или заполнения области S2, обекты, которые живут достаточно долго перемещаются в Old Generation.
Следует сказать, что не всегда объекты при создании аллоцируюся в Eden. Если объект слишком велик, он сразу идет в Old Generation.
Когда после очередной сборки мусора места нехватает уже в New Generation, то запускается сбор мусора в Old Generation (наряду со сборкой в New Generation). В Old Generation объекты уплотняются (алгоритм Mark-Sweep-Compact).
Если после полной сборки мусора места нехватает, то вылетает Java.lang.OutOfMemoryError.
Но во время работы VM может запрашивать увеличение памяти и Heap может увеличиваться.
Как правило, Old Generation занимает 2/3 объема Heap.
Эффективоность алгоритма сборки мусора считается по параметру STW (Stop The World) - время, когда все процессы кроме GC останавливаются. Serial в этом смысле не слишком эффективен, т.к. делает свою работу не торопясь, в одном потоке.
Parallel
То же, что и Serial, но использует для работы несколько потоков. Таким образом STW чуть меньше.
Concurrent Mark Sweep
Принцип работы с New Generation такой же, как и в случае алгоритмов Serial и Parallel, отличия в том, что данный алгоритм разделяет младшую (New Generation) и старшую (Old Generation) сборку мусора во времени. Причем сбор мусора в Old Generation происходит в отдельном потоке, независимо от младшей сборки. При этом сначала приложение останавливается, сборщик помечает все живые объекты доступные из GC Root (корневых точек) напрямую, затем приложение вновь начинает работу, а сбощик проверяет объекты доступные по ссылкам из этих самых помеченных, и также помечает их как живые. Эта особенность создает так называемые плавающие объекты, которые помечены как живые, но таковыми по факту не являющимися. Но они будут удалены в следующих циклах. Т.е. пропускная способность растет, STW уменьшается, но требутся больше места для хранения плавающих объектов.
В этом алгоритме уплотнения нет. Т.е. область Old Generation дефрагментированна.
Garbage-First
G1 сильно отличается от своих предшественников. Он делит область Heap не физически, а скорее логически на те же области: Eden, Survivor, Old Generation. Причем дефрагментированно. Физически область Heap делится на регионы одинакового размера, каждый из которых может быть Eden, Survivor или Old Generation + область для больших объектов (громадный регион).
Над очисткой регионов Eden работает сразу несколько потоков, объекты переносятся в регионы Survivor или регионы старшего поколения (Tenured). Это знакомый по предыдущим алгоритмам очистки подход. На время очистки работа приложения останавливается. Отличие в том, что очистка производится не по всем регионам Eden, а только по некоторым, которые более всего в ней нуждаются, таким образом регулируется время очистки. Отсюда название алгоритма - в первую очередь мусор.
А с полной сборкой (точнее, здесь она называется смешанной (mixed)) все немного хитроумнее, чем в рассмотренных ранее сборщиках. В G1 существует процесс, называемый циклом пометки (marking cycle), который работает параллельно с основным приложением и составляет список живых объектов. За исключением последнего пункта, этот процесс выглядит уже знакомо для нас:
· Initial mark. Пометка корней (с остановкой основного приложения) с использованием информации, полученной из малых сборок.
· Concurrent marking. Пометка всех живых объектов в куче в нескольких потоках, параллельно с работой основного приложения.
· Remark. Дополнительный поиск не учтенных ранее живых объектов (с остановкой основного приложения).
· Cleanup. Очистка вспомогательных структур учета ссылок на объекты и поиск пустых регионов, которые уже можно использовать для размещения новых объектов. Первая часть этого шага выполняется при остановленном основном приложении. После окончания цикла пометки G1 переключается на выполнение смешанных сборок. Это значит, что при каждой сборке к набору регионов младшего поколения, подлежащих очистке, добавляется некоторое количество регионов старшего поколения. Количество таких сборок и количество очищаемых регионов старшего поколения выбирается исходя из имеющейся у сборщика статистики о предыдущих сборках таким образом, чтобы не выходить за требуемое время сборки. Как только сборщик очистил достаточно памяти, он переключается обратно в режим малых сборок.
Очередной цикл пометки и, как следствие, очередные смешанные сборки будут запущены тогда, когда заполненность кучи превысит определенный порог.
Опираясь на уже упомянутую статистику о предыдущих сборках, G1 может менять количество регионов, закрепленных за определенным поколением, для оптимизации будущих сборок.
Громадные регионы. С точки зрения JVM объекты которые превышают размер половины региона являются громадными. Особенности:
· никогда не перемещается между регионами
· может удаляться в рамках цикла пометки или полной сборки мусора
· в регионе, занятом громадным объектом, может находится только он сам.
Громадные объекты в силу небольшого размера регионов могут порождать проблемы с точки зрения STW.
G1 выигрывает по времени STW, но расплатой является меньшая пропускная способность (около 90%, ср., например у Parallel ок. 99%) т.е. большие затраты ресурсов процессора.
6. Чем отличаются сборщики мусора?
Serial (последовательный) — самый простой вариант для приложений, которым не требуется большой размер кучи для работы (Oracle указывает условную границу 100 МБ), которые не очень чувствительны к коротким остановкам и им для работы доступно только одно ядро процессора. Редко когда используется, но на слабых компьютерах может быть выбран виртуальной машиной в качестве сборщика по умолчанию.
+ Непритязательность по части ресурсов компьютера. Т.к. всю работу он выполняет последовательно в одном потоке, никаких заметных оверхедов и негативных побочных эффектов у него нет.
- Долгие паузы на сборку мусора при заметных объемах данных.
- Все настройки Serial GC крутятся вокруг размеров различных регионов кучи, т.е. для тонкой настройки требуется вручную что-то изучать, настраивать, экспериментировать и т.д.
Parallel (параллельный) — наследует подходы к сборке от последовательного сборщика, но добавляет параллелизм в некоторые операции, а также возможности по автоматической подстройке под требуемые параметры производительности. В целом, Parallel GC — это простой, понятный и эффективный сборщик, подходящий для большинства приложений. У него нет скрытых накладных расходов, мы всегда можем поменять его настройки и ясно увидеть результат этих изменений.
+ в сравнении с Serial GC есть возможность автоматической подстройки под требуемые параметры производительности и меньшие паузы на время сборок. При наличии нескольких процессорных ядер выигрыш в скорости будет практически во всех приложениях.
- Определенная фрагментация памяти, но вряд ли она будет существенной для большинства приложений, т.к. сборщиком используется относительно небольшое количество потоков.
Concurrent Mark Sweep (CMS) — нацелен на снижение максимальных задержек путем выполнения части работ по сборке мусора параллельно с основными потоками приложения. Более требователен к ресурсам процессора. Подходит для работы в приложениях с относительно большими объемами долгоживущих данных, для приложений, имеющих доступ к нескольким ядрам процессора и чувствительных к паузам STW.
+ по сравнению с рассмотренными ранее Serial/Parallel GC ориентирован на минимизацию времени простоя, что является критическим фактором для многих приложений. Но для выполнения этой задачи приходится жертвовать ресурсами процессора и зачастую общей пропускной способностью.
- не уплотняет объекты в старшем поколении, что приводит к фрагментации области Tenured. Этот факт в совокупности с наличием плавающего мусора приводит к необходимости выделять приложению (конкретно — старшему поколению) больше памяти, чем потребовалось бы для других сборщиков (Oracle советует на 20% больше).
- долгие паузы при потенциально возможных сбоях конкурентного режима могут стать неприятным сюрпризом. Хотя они не частые, и при наличии достаточного объема памяти сборщику CMS удается их полностью избегать.
Garbage-First (G1) — создан для замены CMS, но не является явным продолжением линейки Serial/Parallel/CMS. Мспользуется в серверных приложениях, работающих на многопроцессорных серверах и оперирующих большими объемами данных (размер кучи от 4 ГБ и выше), для которых важно сохранять время отклика небольшим и предсказуемым, пусть даже за счет уменьшения пропускной способности.
+ G1 более точно предсказывает размеры пауз, чем CMS, и лучше распределяет сборки во времени, чтобы не допустить длительных остановок приложения, особенно при больших размерах кучи.
+ В отличие от CMS, например, он не фрагментирует память.
- Тратит ресурсы процессора, которые использует для выполнения достаточно большой части своей работы параллельно с основной программой. В результате страдает пропускная способность приложения. Целевое значением пропускной способности по умолчанию для G1 равно 90%, для Parallel GC составляет 99%. Это не значит, что пропускная способность с G1 всегда будет почти на 10% меньше, но данную особенность следует учитывать.
«Монолитный» означает то, что всё поколение должно быть очищено за проход.
«В большинстве случаев одновременный» — mostly concurrent, одновременный имеется в виду фоновый, «работающий вместе с потоками приложения».
Z Garbage Collector (ZGC) - это масштабируемый сборщик мусора с малой задержкой. ZGC выполняет всю дорогостоящую работу одновременно, не останавливая выполнение потоков приложения.
ZGC предназначен для приложений, которые требуют малой задержки (паузы менее 10 мс) и/или используют очень большую кучу (мульти-терабайты). Включается с помощью опции -XX:+UseZGC.
ZGC доступен в качестве экспериментальной функции, начиная с JDK 11.
7. Расскажите про утилиты для анализа памяти?
встроенные в JDK утилиты: jps, jmap, jstat, jconsole, visualvm до java 9.
внешние: VisualVM c java 9 и утилита от NetBeans - YourKit Java Profiler.
jconsole, visualvm и yourkit profiler предоставляют удобный визуальный интерфейс.
jps - выводит pid-ы процессов, которые использует VM
jmap - выводит информацию о состоянии памяти виртуальной машины
jmap -heap 14152 или jhsdb jmap --heap --pid 14152
jstat - аналогично jmap
jstack - показывает какие процессы запущены в виртуальной машине (нет инфы о памяти)
...
jconsole
VisualVM (с плагином VisualGC)
YourKit Profiler - универсальный расширенный профилировщик, используется не только для анализа памяти, но и ,например, для поиска узких мест в коде (повторения и тп)
8. Что такое ссылки?
Ссылки в Java — это указатели на объекты. Другими словами, ссылка — это переменная, содержащая адрес ячейки памяти, в которой хранится объект. Кроме того, ссылка может быть инициализирована как null — нулевая ссылка, не указывающая ни на какой объект в памяти (именно это значение является значением по умолчанию). Внутри класса в нестатическом контексте также может быть использована ссылка this, указывающая на текущий объект, и ссылка super, указывающая на текущий объект суперкласса.
Абстрактный класс java.lang.ref.Reference предоставляет базу для ссылочных классов. В нём определены следующие методы:
· get — метод, возвращающий сильную ссылку на объект, на который указывает ссылка.
· clear — очищает ссылку.
· isEnqueued и enqueue, отвечающие за взаимодействие ссылки и объекта ReferenceQueue.
9. Какие типы ссылок Вы знаете?
В Java существует четыре типа ссылок, различающихся по способу сбора мусора и предоставляющие пользователю возможность более гибко работать с памятью:
· Сильные ссылки (Strong References) - стандартные ссылки, создаются каждый раз, когда аллоцируем место в памяти через оператор new. Если на объект есть хоть одна жесткая ссылка, то данный объект не будет утилизирован при сборке мусора.
Counter counter = new Counter(); // strong reference
· Слабые ссылки (WeakReferences) - создаются с помощью вызова new WeakReference<T>(T obj, ReferenceQueue<T> queue) или new WeakReference<T>(T obj). Если на объект есть только слабая ссылка, то будет выполнена попытка утилизации данного объекта при сборке мусора.
Counter counter = new Counter(); // strong reference - line 1
WeakReference<Counter> weakCounter = new WeakReference<Counter>(counter); //weak reference
counter = null; // now Counter object is eligible for garbage collection
· Мягкие ссылки (Soft References) - создаются с помощью вызова new SoftReference<T>(T obj, ReferenceQueue<T> queue) или new SoftReference(T obj). Если на объект есть только мягкая ссылка, то будет выполнена попытка утилизации данного объекта при сборке мусора в случае, если приложению не хватает памяти.
Counter prime = new Counter(); // prime holds a strong reference - line 2
SoftReference<Counter> soft = new SoftReference<Counter>(prime) ; //soft reference variable has SoftReference to Counter Object created at line 2
prime = null; // now Counter object is eligible for garbage collection but only be collected when JVM absolutely needs memory
· Фантомные ссылки (PhantomReferences) - создаются с помощью вызова new PhantomReference<T>(T obj, ReferenceQueue<T> queue). Если на объект есть только фантомная ссылка, то будет выполнена попытка утилизации данного объекта при сборке мусора. Сам объект при этом не будет удален из памяти до тех пор, пока на него существует фантомная ссылка или данная фантомная ссылка не очищена с помощью вызова метода clear(). Так же стоит заметить, что метод get() фантомной ссылки всегда возвращает null.
DigitalCounter digit = new DigitalCounter(); // digit reference variable has strong reference - line 3
PhantomReference<DigitalCounter> phantom = new PhantomReference<DigitalCounter>(digit); // phantom reference to object created at line 3
digit = null;
Фантомные ссылки (PhantomReference) - при создании фантомных ссылок обязательно нужно указывать в конструкторе объект очереди ReferenceQueue, это связано с целью применения данного вида ссылок. Метод get у такой ссылки всегда возвращает null, спросите, а почему? Давайте разберемся. Основное применение фантомной ссылки - это создание альтернативного механизма освобождения ресурсов у объекта. Объект будет достижим, пока все фантомные ссылки на него не будут очищены, либо сами не станут недостижимы. В целях обеспечения невозможности создать жесткую ссылку на объект после его финализации, метод get всегда возвращает вам null. Когда на объект остаются только фантомные ссылки, его уровень достижимости начинает спускаться до фантомного, он финализируется (выполняется finilize()). После очистки ссылки (clear()) вручную объект станет полностью недостижим, фантомная ссылка добавится в очередь, указанную при создании, и сразу, либо спустя время, референт будет собран сборщиком мусора.
Как может помочь такой вид ссылок? Представьте, что у вас есть 2 объекта, основной и дополнительный. Вы не можете уничтожить основной раньше дополнительного в соответствии с политикой очистки ресурсов. Итого, мы оставляем две фантомные ссылки на каждый из объектов и ассоциируем их с очередью. Уровень достижимости таких объектов опускается до фантомного (см. выше), объект финализируется и единственное, что о нем напоминает в вашем коде, это фантомная ссылка. Мы вызываем clear у дополнительного объекта и начинаем опрашивать очередь ссылок, а когда метод poll очереди вернет нам ссылку на доп. объект, мы получили гарантии того, что объект финализирован и недостижим, теперь мы можем сделать clear для основного объекта. Это более гибко, чем рассчитывать на метод finalize, который выглядит как уродливое наследие.
10. Чем они отличаются?
Сначала общее правило: политика зачистки для некоего объекта и очистки ссылок на него определяется самыми жёсткими из всех ссылок, что на него указывают.
Различие между всеми типами ссылок только одно — поведение GC с объектами, на которые они ссылаются:
- StrongReference нужны для указания на объекты, которые должны обязательно оставаться в памяти всё то время, что эти ссылки на него существуют. Объект будет уничтожен только в том случае, если мы явно назначим ссылке null, либо потеряем доступ к ссылке через класс родитель (например класс родитель обнулится и объект также обнулиться. Обнулится, т.е. его будет обрабатывать GC и высвобождать память из этого объекта)
- WeakReference — если GC видит что объект доступен только через цепочку weak-ссылок, то он удалит его из памяти.
Пример использования: WeakHashMap - это реализация которая хранит ключ, используя weak-ссылку. Когда сильная ссылка на фактический объект удалена и затем GC удаляет ключ из памяти, то удаляется вся запись из Map: при добавлении новой пары <ключ, значение>, создается WeakReference для ключа и в конструктор передается ReferenceQueue. Когда GC удаляет ключ с памяти, то ReferenceQueue возвращает соответствующий WeakReference для этого ключа. После этого соответствующий Entry удаляется с Map.
WeakHashMap не предназначена для использования в качестве кэша. WeakReference создается для ключа, а не для значения. И данные будут удалены только после того, как в программе не останется strong-ссылок на ключ, а не на значение. В большинстве случаев это не то, чего вы хотите достичь кэшированием. Данные с WeakHashMap будут удалены не сразу после того как GC обнаружит, что ключ доступен только через weak-ссылки. Фактически очистка произойдет при следующем обращении к WeakHashMap. В первую очередь WeakHashMap предназначен для использования с ключами, у которых метод equals проверяет идентичность объектов (использует оператор ==). Как только доступ к ключу потерян, его уже нельзя создать заново.
- SoftReference — если GC видит что объект доступен только через цепочку soft-ссылок, то он удалит его из памяти только в случае необходимости. По сути SoftReference - это механизм кэширования объектов в памяти, но в критической ситуации, когда закончится доступная память, GC удалит не использующиеся объекты из памяти и тем самым попробует спасти JVM от завершения работы.
Общий контракт звучит следующим образом: GC гарантировано удалит из кучи все объекты, доступные только по soft-ссылке, перед тем как бросит OutOfMemoryError.
Например, если нужно насоздавать ещё объектов с сильными ссылками, а уже негде, лучше освободить кэш и замедлить работу, чем уронить процесс напрочь.
Основная разница между SoftReference и WeakReference, в том, что SoftReference являются более подходящим для кэшей, а WeakReference для хранения метаданных.
- PhantomReference — если GC видит что объект доступен только через цепочку phantom-ссылок, то он его удалит из памяти, когда это ему "понравится" (зависит от реализации GC, возможно после нескольких запусков)
11. Расскажите про String pool и Int pool.
Класс String, возможно, наиболее часто используемый класс в Java. Если новый объект создавать в динамической памяти (memory heap) каждый раз, когда мы используем String, то мы потратим впустую много памяти. Пул строк (String pool) решает эту проблему, сохраняя только один объект для каждого значения строки.
Хотя мы создали несколько переменных String со значениями Duke и Juggy, но в динамической памяти (куче) создаётся и храниться только два объекта. Для доказательства посмотрите следующий пример кода. (Напомним, что в Java оператор "==" используется для сравнения двух объектов и определения того один и тот же это объект или нет.)
String juggy = "Juggy";
String anotherJuggy = "Juggy";
System.out.println(juggy == anotherJuggy);
Этот код вернет true, потому что две переменные String указывают на один и тот же объект в пуле строк. Их значения одинаковые.
Теперь посмотрите на этот код — он выглядит похожим на предыдущий пример, но здесь есть отличие.
String duke = new String("duke");
String anotherDuke = new String("duke");
System.out.println(duke == anotherDuke);
На основе предыдущего примера можно подумать, что этот код вернёт true, но это не так. Добавление оператора new приводит к созданию нового объекта String в памяти. Таким образом, JVM создаст два разных объекта.
У обертки Intrger имеется кеш значений от -128 до 127. В классе-обёртке Integer есть внутренний класс IntegerCache. Он объявлен как private static. В этом внутреннем классе кешированные объекты находятся в массиве cache[]. Кеширование выполняется при первом использовании класса-обёртки. После первого использования, вместо создания нового экземпляра (кроме использования конструктора), используются кешированные объекты.
Демонстрация этого кеша приведена в коде ниже:
Integer a = 120;
Integer b = 120;
Integer c = 130;
Integer d = 130;
System.out.println(a == b); //true
System.out.println(c == d); //false
Кэширование касается не только класса-оболочки Integer. Имеются аналогичные реализации кеширования для других классов-оболочек целочисленных типов: ByteCache, ShortCache, LongCache, CharacterCache. Кешированные объекты не используются при создании объекта-обёртки с помощью конструктора.
12. Расскажите о методе String.intern().
Для хранениястрок в пуле используется способ, называемый "интернирование строк" (String interning).
/**
* Возвращает каноническое представление для строкового объекта.
*
* Пул строк (первоначально пустой) управляется классом {@code String}.
*
* Когда вызывается метод intern, если пул уже содержит строку,
* равную этому объекту {@code String}, определяемому через
* метод {@link #equals(Object)}, тогда возвращается строка из пула.
* Иначе, этот объект {@code String} добавляется к
* пулу и возвращается ссылка на этот объект {@code String}.
*
* Из этого следует, что для любых двух строк {@code s} и {@code t},
* {@code s.intern() == t.intern()} будет {@code true}
* тогда и только тогда, когда {@code s.equals(t)} равно {@code true}.
*
* Все литеральные строки и строковые константы интернируются.
* Строковые литералы определяются в разделе 3.10.5 The Java™ Language Specification.
*
* @returns строка, которая имеет то же самое содержание как эта строка,
* но, гарантируется, что она будет из пула уникальных строк.
*
* @jls 3.10.5 String Literals
*/ public native String intern();
Метод intern() используется для хранения строк в пуле строк. Во-первых, он проверяет, существует ли уже созданная строка в пуле. Если нет, то создает новую строку в пуле. Логика пула строк основана на паттерне Flyweight.
Теперь, обратите внимание, что происходит, когда мы используем new для создания двух строк:
String duke = new String("duke");
String duke2 = new String("duke");
System.out.println(duke == duke2); // Здесь результат будет false
System.out.println(duke.intern() == duke2.intern()); // Здесь результат будет true
В отличие от предыдущего примера с ключевым словом new, в данном случае сравнение вернёт true. Это потому, что использование метода intern() гарантирует, что строка будет в пуле.
13. Расскажите, что такое профайлер.
Профилирование - это сбор и анализ характеристик работы приложения.
Проще говоря, запускаем приложение, "подключаемся" к нему, в реальном времени смотрим как оно работает, делаем выводы. Далее предпринимаем меры.
Профайлер – программа, которая осуществляет эти действия. (jmap, jconsole)
14. Расскажите, как использовать VisualVM.
15. Расскажите, чем отличается sampling от profiling? (Это типы аудита. Режим работы в профайлере)
Profiling это единоразовый снимок состояния в памяти на момент запуска профайлера.
Sampling это периодическое обновление состояния в памяти, что позволяет наблюдать динамику использования памяти работающим приложением.
16. Расскажите о методе finalize().
Метод находится в классе Object, поэтому наследуется всеми классами.
Вызывается сборщиком мусора для объекта, когда сборщик мусора определяет, что больше нет ссылок на объект. Подкласс переопределяет метод finalize, чтобы избавиться от системных ресурсов или выполнить другую очистку.
Общий контракт finalize заключается в том, что он вызывается, если и когда виртуальная машина Java определила, что больше нет никаких средств, с помощью которых этот объект может быть доступен любому потоку, который еще не умер, кроме как в результате предпринятого действия. финализацией какого-либо другого объекта или класса, который готов к финализации. Метод finalize может выполнять любые действия, в том числе снова делать этот объект доступным для других потоков; обычная цель finalize, однако, состоит в том, чтобы выполнить действия по очистке перед тем, как объект будет безвозвратно отброшен. Например, метод finalize для объекта, представляющего соединение ввода-вывода, может выполнять явные транзакции ввода-вывода, чтобы разорвать соединение до того, как объект будет окончательно удален.
Метод finalize класса Object не выполняет никаких специальных действий; он просто возвращается нормально. Подклассы Object могут переопределить это определение.
Язык программирования Java не гарантирует, какой поток вызовет метод finalize для любого заданного объекта. Однако гарантируется, что поток, вызывающий finalize, не будет удерживать никаких видимых пользователю блокировок синхронизации при вызове finalize. Если методом finalize выдается неперехваченное исключение, оно игнорируется, и завершение этого объекта завершается.
После вызова метода finalize для объекта никакие дальнейшие действия не предпринимаются до тех пор, пока виртуальная машина Java снова не определит, что больше нет средств, с помощью которых этот объект может быть доступен для любого потока, который еще не умер, включая возможные действия. другими объектами или классами, которые готовы к завершению, после чего объект может быть отброшен.
Метод finalize никогда не вызывается виртуальной машиной Java более одного раза для любого заданного объекта.
Любое исключение, созданное методом finalize, приводит к остановке финализации этого объекта, но в противном случае игнорируется.
Механизм финализации по своей сути проблематичен. Завершение может привести к проблемам с производительностью, взаимоблокировкам и зависаниям. Ошибки в финализаторах могут привести к утечке ресурсов; нет возможности отменить финализацию, если в ней больше нет необходимости; и не указан порядок среди вызовов финализации методов разных объектов. Кроме того, нет никаких гарантий относительно сроков завершения. Метод finalize может быть вызван для финализируемого объекта только после неопределенной задержки, если вообще будет вызван. Классы, экземпляры которых содержат ресурсы, не относящиеся к куче, должны предоставлять метод для включения явного освобождения этих ресурсов, а также должны реализовывать AutoCloseable, если это необходимо. Cleaner и PhantomReference предоставляют более гибкие и эффективные способы высвобождения ресурсов, когда объект становится недоступным.
Джошуа Блох Эффективное программирование 2.8.
Очистители в этом отношении оказываются немного лучше финализаторов, так как авторы классов могут управлять их потоками очистки, но очистители попрежнему работают в фоновом режиме, под управлением сборщика мусора, так что никакой гарантии своевременной очистки не может быть.
Спецификация языка программирования Java не только не дает гарантии своевременного вызова финализаторов или очистителей, но и не дает гарантии, что они вообще будут вызваны. Вполне возможно (и даже вероятно), что программа завершится, так и не вызвав их для некоторых объектов, ставших недоступными. Как следствие вы никогда не должны ставить обновление сохраняемого (persistent) состояния в зависимость от финализатора или очистителя.
Не поддавайтесь соблазнам методов System, gc и System. runFinalization. Они могут увеличить вероятность выполнения финализаторов и очистителей, но не гарантируют его. Единственные методы, которые, как заявлялось, гарантируют удаление, — это System. runFinalizersOnExit и его близнец Runtime. runFinalizersOnExit. Эти методы фатально ошибочны и много лет как признаны устаревшими и не рекомендованными к употреблению.
Еще одна проблема, связанная с финализаторами, состоит в том, что неперехваченное исключение в ходе финализации игнорируется, а финализация этого объекта прекращается [25, 12.6]. Необработанное исключение может оставить объект в поврежденном состоянии. И если другой поток попытается воспользоваться таким “испорченным” объектом, результат может быть непредсказуем. Обычно необработанное исключение завершает поток и выдает распечатку стека, однако в случае финализатора этого не происходит — не выдается даже предупреждение. Очистители этой проблемы не имеют, поскольку библиотека, использующая очиститель, управляет его потоком.
Так что же вам делать вместо написания финализатора или очистителя для класса, объекты которого инкапсулируют ресурсы, требующие освобождения, например файлы или потоки? Просто сделайте ваш класс реализующим АutoCloseable и потребуйте от его клиентов вызова метода close для каждого экземпляра, когда он больше не нужен (обычно с помощью try-c-ресурсами для гарантии освобождения даже при исключениях (раздел 2.9)).
Так для чего же годятся финализаторы и очистители (если они вообще для чего-то нужны)? У них есть два корректных предназначения. Одно из них — служить в качестве подстраховки на случай, если владелец ресурса не вызовет его метод close.
Второе обоснованное применение очистителей касается объектов с платформозависимыми узлами (native peers). Такой узел — это платформозависимый (не являющийся объектом Java) объект, к которому обычный объект обращается через машинные команды. Поскольку такой узел не является обычным объектом, сборщик мусора о нем не знает, и, соответственно, при утилизации обычного объекта утилизировать платформозависимый объект он не может. Очиститель или финализатор является подходящим механизмом для решения этой задачи при условии, что узел не содержит критических ресурсов. Если же снижение производительности неприемлемо или узел содержит ресурсы, которые необходимо освободить немедленно, класс должен содержать метод close, описанный ранее.
17. Расскажите о методе clone(). Что такое Deep clone and Shallow clone?
Цель этого метода – клонировать объект – т.е. создать его клон/копию/дубликат.
Дело в том, что Java-машина не знает, какие объекты можно клонировать, а какие нет. Файлы, например, клонировать нельзя. Как и поток System.in.
Поэтому вопрос о полноценном клонировании был отдан на откуп разработчикам классов. Тут все было сделано по аналогии с методом equals. Даже есть свой аналог hashCode – это интерфейс Cloneable.
Интерфейс Cloneable – это так называемый интерфейс-маркер, который не содержит никаких методов. Он используется, чтобы маркировать (помечать) некоторые классы.
Если разработчик класса считает, что объекты класса можно клонировать, он помечает класс этим интерфейсом (наследует класс от Cloneable).
Если разработчика не устраивает стандартная реализация метода clone, он должен написать свою, которая будет создавать дубликат объекта правильным образом.
При вызове метода clone(), Java проверяет, был ли у объекта интерфейс Cloneable. Если да — клонирует объект методом clone(), если нет — выкидывает исключение CloneNotSupportedException.
Если ты хочешь воспользоваться «клонированием по умолчанию», которое реализовано в классе Object, тебе нужно:
а) Добавить интерфейс Cloneable своему классу
б) Переопределить метод clone и вызвать в нем базовую реализацию:
class Point implements Cloneable
{
int x;
int y;
public Object clone()
{
return super.clone();
}
}
Или ты можешь написать реализацию метода clone полностью сам:
class Point
{
int x;
int y;
public Object clone()
{
Point point = newPoint();
point.x = this.x;
point.y = this.y;
return point;
}
}
18. Расскажите о Stack и Heap.