Pull to refresh

Comments 20

Мне кажется, что показывая график из статьи 2013 года стоило бы особо подчеркнуть, что данные могут быть неактуальны. Особенно для графика пауз GC.
На графике в общем-то указана версия Java на которой производились тесты, но я все таки добавил еще одно упоминание о том, что статья старая
Проясните пожалуйста вопрос — как происходит адресация. В С++ например ссылка на данные прямая — тупо хранится адрес и по нему программа ходит за данными. А в джаве, данные объекта перемещаются в памяти, и значит нужно каждый раз вычислять этот адрес при каждом обращении к объекту.
Не уверен, что понял вопрос. Что значит данные объекта перемещаются в памяти? У нас есть объект в памяти, на него есть ссылка, это по факту адрес объекта.
Если вам интересно как происходит процесс сжатия/расжатия, вот, для примера, код JVM (из файла compressedOops.inline.hpp) который делает декодирование адреса.
  inline oop decode_not_null(narrowOop v) {
    assert(!is_null(v), "narrow oop value can never be zero");
    address base = Universe::narrow_oop_base();
    int    shift = Universe::narrow_oop_shift();
    oop result = (oop)(void*)((uintptr_t)base + ((uintptr_t)v << shift));
    assert(check_obj_alignment(result), "address not aligned: " INTPTR_FORMAT, p2i((void*) result));
    return result;
  }

Сборщик мусора может перемещать объекты. Там есть несколько областей памяти, которые соответствуют короткоживущим и долгоживущим объектам. Если данные переместились, то адрес сменился. То есть логически поразмыслив понятно, что — адрес или надо вычислять каждый раз или менять все ссылки на этот блок после каждой сборки мусора. Вот хотел бы понять, как это происходит
Да, я кажется понял, вы спрашиваете «Что происходит со ссылками когда GC перемещает объект?». Кажется вот здесь описан механизм перемещения объектов в JVM GC:
Specifically, the GC walks the graph of reachable objects within the «from» space, starting from each of the GC roots. Each time it finds a reference to a node (in an instance field, static field, stack frame, etc), it checks the object that the reference points to to see if it has been marked as visited.

* If it is not yet marked, the GC does the following:

It marks the object in the from-space.
It copies the object into the to-space.
It stores the address of the object in to space in the from-space object. (This is like a forwarding address.)
It recursively visits each reference field of the to-space copy of the object.
The result of this the reference to the to-space object.

* If the object has been marked already, the GC looks up the forwarding address, and returns that.
Вот и вопрос — как адресуются данные при таком раскладе. Что происходит, когда надо достать object.field? В С++ просто лезет по адресу в память и получает данные. А как в джаве, если адрес плавает?

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

Там по-моему немножко не так делается, из-под потоков, которые нужно остановить, "выдергиваются" страницы памяти (размапливаются) и gc делает в них все что ему требуется, двигает объекты, правит адреса, и так далее, а потоки останавливаются сами по page fault exception, потом jvm мапит все обратно и потоки продолжают работать как ни в чем не бывало.

A это вроде как только в режиме ядра можно делать — ring0. И страницы по 4кб занимают. Что на каждый объект по 4кб резервируется?

Вот тут подробное описание. Страница для page fault создается одна на поток, а не на объект. Хандлеры на page fault можно регистрировать и в user space, все современные ядра ос позволяют это.

тема интересная, но надо разбираться.
у меня тоже бродили мысли, что устройство виртуальной памяти очень подходит для сбора мусора, если мусорщику дать возможность манипулировать такими вещами, как dirty bit, page fault, tlb и тп
Вот как раз тут у меня куча вопросов — неужели вся куча перемалывается? Я могу создать 100500 объектов, которые друг на друга ссылаются.
Все адреса хранятся в куче. Если просто пройтись по памяти и заменить байты на новые, то можно зацепить простые данные. Отсюда следует
, что надо как-то разделять простые данные и адреса объектов. Вот интересно, как это сделано и как происходит адресация данных.

PS Все больше ощущаю, что джава программисты не понимают вопросов, которые исходят от программистов С/С++. В понимании С виртуальная память представляет собой простой большой линейный массив и в нем нет регионов, которые декларируются в джаве. А любое разделение сопровождается доп расходами и необходимостью хранить адреса разделов и как-то постоянно складывать базу и смещение чтобы вычислить реальный адрес
вот как gc детектит, ссылка это или примитив:
stackoverflow.com/a/12097214/1961500

И вот, как устроен OopMap:
stackoverflow.com/a/26049445/1961500

Т.е. по сути для интерпретируемого кода, проверяет выравнивание объекта и установленный бит признака объекта, а для нативного кода, сгенеренного jit — при компиляции по инструкциям сохраняет смещения ссылок в стеке рядом с скомпиленным методом.
В джаве нет работы напрямую с адресами в памяти. Вопросы с адресом field, измененным в результате работы GarbageCollector, JVM инкапсулирует.
В самом языке нет, но в конечном счете все работает через ассемблер. Вот и интересно какова стоимость хозяйства по сравнению с прямым доступом к памяти.
Sign up to leave a comment.

Articles