Here I collect a list of information. It's all about Java/Kotlin/Android development and containes themes in which I was bad!
Многие принебрегают этой темой и я не исключение. Но проведя немного времени в интернете, я собрал все пазлы во едино, и записал информацию так, как мне было ее легко понять. Начнем с азов, а именно как устроено распределение в памяти JVM(Java Virtual Machine).
Stack - Это место в памяти, где храняться все наши переменные, методы, Threads, Static etc. По сути основное место хранения данных для Runtime. Заполняеться по большей части на этапе компиляции.
Heap(Куча) - Это именно та часть памяти JVM которая приносит большинству людей боль и страдания на собеседовании, а так же в момент разработки. Heap - Это область где размещаються созданные нами обьекты, и в зависимости от разных реализаций JVM и GC вы можете увидить разные исполне того самого Heap
В большенстве случаев структура Heap выглади так:
- Young Generation:
- Edem
- From
- To
- Old Generation
Young generation - Место куда помещаються обьекты при первом создании/выделении/аллокации. Здесь они живут до того момента, пока:
- Не будут уничтоженые при ближайшей чистке, ввиду неиспользования;
- Не переместяться в From или Old Generation, при условии что обьект в использовании достаточно долго.
Old Generation - Место в "куче" где храняться обьекты на которых долгое время есть зависимость и они долгое время используются.
GC Roots/GC Root Set - Набор переменных, методов, тредов и т.д. от которых GC может получить информацию о том, нужны ли те или иные обьекты еще(используються ли они).
Лично я еще немного углубился в эту тему, чтобы разобрать как были устроены алгоритмы работы GC в разных версиях Java, и как они со временем изменялись. Но по моему мнению, информации выше достаточно, чтобы перейти к Android Oriented GC. Мы имеем базовое понимание из чего состоит распределение памяти в JVM, а значет можем двигаться дальше.
За время разработки Android OS было два варианта реализации Dalvik/ART(Adnroid Runtime)
В качестве алгоритма работы GC был выбрана реализация CMS(Concurrent Mark-and-Sweep) Работает он по следующей схеме:
- Find Root Set
- Mark Reachable Objects(1) Concurrent, и это накладывает некие нюансы, в виде появления новых обьектов на этом этапе, и необходимости заново проходить первые пункты.
- Mark Reachable Objects(2) Снова ищет и замораживает (Stop-the-world) приложение, ощущенние будто выполняется тяжелая операция на данном этапе.
- Collect
Основные минусы Dalvik:
- Обьекты с коротким ЖЦ живут дольше положенного, связанно с последующими минусами;
- Делает дефрагментацию только если приложение находиться в фоне. Из-за этого затрудняеться выделение памяти под новые обьекты во время работы приложения;
- Срабатывает только в трех случаях:
- Выделение памяти под новый обьект не удалось
- Размер Heap достиг его предельного максимума, для приложения
- Был непосредственный вызов
- Нет разделения на Generation
Lollipop & Marshmellow Алгоритм все тот же, CMS Но с большим рядом улудшений!
- Смена алгоритма выделения памяти под новый обьект с dlmalloc на RosAlloc. Это позволило создавать обьекты непосредственно на тредах, где было фактическое создание.
- Группирует маленькие выделения на кусочки, а большое выделение выравнивает по страницам. Последнее достигается за счет того, что ART не имеет лимита по памяти на один процесс.
- Компоновка (дефрагментация) происходит как на БГ так и на ФГ.За счет этого вызов GC сократился в разы, так как он всегда держит хип в дефрагментированном состоянии!
- "Микролок" - делает алгоритм работы Mark-and-Sweep паралельным и тем самым уменшает скороть выполнения с 10 до 3 мс.
Oreo Тут произошел прям очень сильный прорыв! Алгоритм CMS, сменил абсолютно новый, для андроида - CHCC(Concurrent Heap Compaction Collector) Что это дало нам и что же за плюшки получила ART VM:
- Дефрагментация происходит теперь всегда, при каждом срабатывании GC. Таким образом сохраняется до 30% всей необходимой памяти!
- Система бакетов. Теперь каждому отдельному треду присваиваеться некий бакет, в котором он может аллоцировать себе обьекты. Тут нам еще пригодился новый алгоритм выделения RosAlloc. Разработан был алгоритм thread-local bump-allocator, благодаря которому выделение теперь занимает в 18раз меньше времени чем на Dalvik и на 70% быстрее, по сравнению с прошлой генерацией ART. Для аллокации теперь достаточно знать указатель на бакет, после чего считываеться размер от указателя до конца бакета и сравниваеться с размером обьекта, под который нужно выделить память. После чего обновляеться указатель с адрессом нового обьекта!
- Была убрана Generation.
Q
- Вернули Generation, что еще больше ускорило работу.