- Loading...
This page describes the object copying algorithm to be implemented in JDK-8234693 Consolidate CDS static and dynamic archive dumping code.
(Latest webrev is here. The links below currently point to the webrev, but will be updated to point to the jdk/jdk repo after JDK-8234693 is pushed; ).
When you dump a static (-Xshare:dump) or dynamic (-XX:ArchiveClassesAtExit) CDS archive, the VM copies eligible class metadata into a buffer, and writes this buffer to a JSA file.
The main copying algorithm is implemented in the ArchiveBuilder class. Functionalities specific to static or dynamic dumping are implemented in the StaticArchiveBuilder and DynamicArchiveBuilder subclasses.
The copying algorithm basically starts scanning the class metadata using MetaspaceClosure, starting from the set of roots provided by StaticArchiveBuilder::iterate_roots() or DynamicArchiveBuilder::iterate_roots(). The copying is done in the following steps:
When dumping the static archive, we want the contents to be deterministic (see JDK-8241071). Basically, if you run "java -Xshare:dump" twice, you should get two classes.jsa files that are bit-by-bit identical.
Note that ArchiveBuilder::iterate_roots() scans some hashtables of symbols and classes. Some of these hashtables uses hashkeys that may be derived from (a) random numbers (e.g., Symbol::identity_hash()), or (b) addresses that may vary from run to run (e.g., SystemDictionaryShared::dumptime_classes_do()). Therefore, the iteration order is not stable.
To get a stable iteration order, we use ArchiveBuilder::gather_klasses_and_symbols(), which iterate the class metadata with ArchiveBuilder::iterate_roots() to discover all Symbols and Klasses that are eligible for archiving. It then sorts the Symbols and Klasses using a stable sorting order into the arrays ArchiveBuilder::symbols() and ArchiveBuilder::klasses().
See comments in ArchiveBuilder::gather_klasses_and_symbols() for more detail.
This step is implemented by ArchiveBuilder::gather_source_objs(), which iterates the metadata with ArchiveBuilder::iterate_sorted_roots(). All objects that are eligible for copying are entered (by reference) into ArchiveBuilder::_rw_src_objs or ArchiveBuilder::_rw_src_objs, depending on whether they are read-write or read-only.
In this step, we also remember the locations of all the pointers in the source objects using ArchiveBuilder::_ptrmap. This is used later in embedded pointer relocation (see below).
For simplicity, let's assume we have only read-write objects to copy. The source objects look like this.
address value field type field name // object "foo" @ 0x100 @0x100: 0x0 intx Foo::intxValue1 @0x108: 0x208 Bar* Foo::barPtrA @0x110: 0x1 intx Foo::intxValue1 @0x118: 0x200 Bar* Foo::barPtrB // objects ineligible for copying ... // object "bar1" @ 0x200 @0x200: 0x11 intx Bar::someField // object "bar2" @ 0x208 @0x208: 0x22 intx Bar::someField
Let's assume that ArchiveBuilder::iterate_roots() points to a single object (foo at 0x100). When we start iterating with foo, we will discover bar2 first (when we scan foo->barPtrA), and then bar1 (when we scan foo->barPtrB). After scanning bar2 and bar1, we stop because we have found no more new objects to scan.
At this point, ArchiveBuilder::_rw_src_objs will contain the following information:
_rw_src_objs->at(0)->obj() == 0x100 foo _rw_src_objs->at(0)->size_in_bytes() == 32 sizeof(Foo) _rw_src_objs->at(1)->obj() == 0x208 bar2 _rw_src_objs->at(1)->size_in_bytes() == 8 sizeof(Bar) _rw_src_objs->at(2)->obj() == 0x200 bar1 _rw_src_objs->at(2)->size_in_bytes() == 8 sizeof(Bar)
Also, _rw_src_objs->_ptrmap essentially remembers that:
There is a pointer at 0x108 (&foo->barPtrA) There is a pointer at 0x118 (&foo->barPtrB)