On Tue, 10 Sep 2024 19:11:30 GMT, Roman Kennke <rken...@openjdk.org> wrote:
>> This is the main body of the JEP 450: Compact Object Headers (Experimental). >> >> It is also a follow-up to #20640, which now also includes (and supersedes) >> #20603 and #20605, plus the Tiny Class-Pointers parts that have been >> previously missing. >> >> Main changes: >> - Introduction of the (experimental) flag UseCompactObjectHeaders. All >> changes in this PR are protected by this flag. The purpose of the flag is to >> provide a fallback, in case that users unexpectedly observe problems with >> the new implementation. The intention is that this flag will remain >> experimental and opt-in for at least one release, then make it on-by-default >> and diagnostic (?), and eventually deprecate and obsolete it. However, there >> are a few unknowns in that plan, specifically, we may want to further >> improve compact headers to 4 bytes, we are planning to enhance the Klass* >> encoding to support virtually unlimited number of Klasses, at which point we >> could also obsolete UseCompressedClassPointers. >> - The compressed Klass* can now be stored in the mark-word of objects. In >> order to be able to do this, we are add some changes to GC forwarding (see >> below) to protect the relevant (upper 22) bits of the mark-word. Significant >> parts of this PR deal with loading the compressed Klass* from the mark-word. >> This PR also changes some code paths (mostly in GCs) to be more careful when >> accessing Klass* (or mark-word or size) to be able to fetch it from the >> forwardee in case the object is forwarded. >> - Self-forwarding in GCs (which is used to deal with promotion failure) now >> uses a bit to indicate 'self-forwarding'. This is needed to preserve the >> crucial Klass* bits in the header. This also allows to get rid of >> preserved-header machinery in SerialGC and G1 (Parallel GC abuses >> preserved-marks to also find all other relevant oops). >> - Full GC forwarding now uses an encoding similar to compressed-oops. We >> have 40 bits for that, and can encode up to 8TB of heap. When exceeding 8TB, >> we turn off UseCompressedClassPointers (except in ZGC, which doesn't use the >> GC forwarding at all). >> - Instances can now have their base-offset (the offset where the field >> layouter starts to place fields) at offset 8 (instead of 12 or 16). >> - Arrays will now store their length at offset 8. >> - CDS can now write and read archives with the compressed header. However, >> it is not possible to read an archive that has been written with an opposite >> setting of UseCompactObjectHeaders. Some build machinery is added so that >> _co... > > Roman Kennke has updated the pull request incrementally with one additional > commit since the last revision: > > Fix FullGCForwarding initialization Hi, Me and @caspernorrbin are reviewing the Metaspace changes (so anything in the `memory` and `metaspace` folders). We have found minor improvements that can be made and some nits, but the code over all looks OK. We are finishing up a first round of review now, and will have a second one. Thank you for your hard work and your patience with the review process. src/hotspot/share/memory/classLoaderMetaspace.cpp line 87: > 85: klass_alignment_words, > 86: "class arena"); > 87: } As per my comment in the header file, change the code to this: ```c++ if (class_context != nullptr) { // ... Same as in PR } else { _class_space_arena = _non_class_space_arena; } src/hotspot/share/memory/classLoaderMetaspace.cpp line 115: > 113: if (wastage.is_nonempty()) { > 114: non_class_space_arena()->deallocate(wastage); > 115: } This code reads a bit strangely. I understand *what* it tries to do. It tries to give back any wasted memory from either the class space arena *or* the non class space arena to the non class space arena's freelist. I assume that we do this since any wastage is presumably too small to be used by our new 22-bit class pointers. However, this context will be lost on future readers. It should have at least a comment in the `if (wastage.is_nonempty())` clause explaining what we expect should happen and why. For example: ```c++ // Any wasted memory is presumably too small for any class. // Therefore, give it back to the non-class space arena's free list. src/hotspot/share/memory/classLoaderMetaspace.cpp line 118: > 116: #ifdef ASSERT > 117: if (result.is_nonempty()) { > 118: const bool in_class_arena = class_space_arena() != nullptr ? > class_space_arena()->contains(result) : false; Unnecessary nullptr check if you take my suggestion, or you should switch to `have_class_space_arena`. src/hotspot/share/memory/classLoaderMetaspace.cpp line 165: > 163: MetaBlock bl(ptr, word_size); > 164: // If the block would be reusable for a Klass, add to class arena, > otherwise to > 165: // then non-class arena. Nit: spelling, "the" src/hotspot/share/memory/classLoaderMetaspace.hpp line 81: > 79: metaspace::MetaspaceArena* class_space_arena() const { return > _class_space_arena; } > 80: > 81: bool have_class_space_arena() const { return _class_space_arena != > nullptr; } This is unnecessary. Instead of having this and having to remember to check for nullness each time, just change the `_class_space_arena` to point to the same arena as the `_non_class_space_arena` does when we run with `-XX:-UseCompressedClassPointers` src/hotspot/share/memory/metaspace.cpp line 656: > 654: // Adjust size of the compressed class space. > 655: > 656: const size_t res_align = reserve_alignment(); Can you change the name to `root_chunk_size`? src/hotspot/share/memory/metaspace.hpp line 112: > 110: static size_t max_allocation_word_size(); > 111: > 112: // Minimum allocation alignment, in bytes. All MetaData shall be > aligned correclty Nit: Spelling, "correctly" src/hotspot/share/memory/metaspace/metablock.hpp line 48: > 46: > 47: MetaWord* base() const { return _base; } > 48: const MetaWord* end() const { return _base + _word_size; } `assert(is_nonempty())` src/hotspot/share/memory/metaspace/metablock.hpp line 51: > 49: size_t word_size() const { return _word_size; } > 50: bool is_empty() const { return _base == nullptr; } > 51: bool is_nonempty() const { return _base != nullptr; } Can `_base == nullptr` but `_word_size != 0`? src/hotspot/share/memory/metaspace/metablock.hpp line 52: > 50: bool is_empty() const { return _base == nullptr; } > 51: bool is_nonempty() const { return _base != nullptr; } > 52: void reset() { _base = nullptr; _word_size = 0; } Is this function really necessary? According to my IDE it's only used in tests and even then the `MetaBlock` isn't used afterwards (so it has no effect). src/hotspot/share/memory/metaspace/metaspaceArena.hpp line 44: > 42: class FreeBlocks; > 43: > 44: struct ArenaStats; Nit: Sort? src/hotspot/share/memory/metaspace/metaspaceArena.hpp line 84: > 82: // between threads and needs to be synchronized in CLMS. > 83: > 84: const size_t _allocation_alignment_words; Nit: Document this? All other members are documented. ------------- PR Review: https://git.openjdk.org/jdk/pull/20677#pullrequestreview-2296528491 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754335269 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754398993 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754343513 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754459464 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754330432 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754619023 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754508321 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754142822 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754142098 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754153662 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754192464 PR Review Comment: https://git.openjdk.org/jdk/pull/20677#discussion_r1754197251