/* * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ #include "precompiled.hpp" #include "jvm.h" #include "classfile/classFileParser.hpp" #include "classfile/fieldLayoutBuilder.hpp" #include "memory/resourceArea.hpp" #include "oops/array.hpp" #include "oops/fieldStreams.inline.hpp" #include "oops/instanceMirrorKlass.hpp" #include "oops/klass.inline.hpp" #include "oops/valueKlass.inline.hpp" #include "runtime/fieldDescriptor.inline.hpp" LayoutRawBlock::LayoutRawBlock(Kind kind, int size) : _next_block(NULL), _prev_block(NULL), _value_klass(NULL), _kind(kind), _offset(-1), _alignment(1), _size(size), _field_index(-1), _is_reference(false) { assert(kind == EMPTY || kind == RESERVED || kind == PADDING || kind == INHERITED, "Otherwise, should use the constructor with a field index argument"); assert(size > 0, "Sanity check"); } LayoutRawBlock::LayoutRawBlock(int index, Kind kind, int size, int alignment, bool is_reference) : _next_block(NULL), _prev_block(NULL), _value_klass(NULL), _kind(kind), _offset(-1), _alignment(alignment), _size(size), _field_index(index), _is_reference(is_reference) { assert(kind == REGULAR || kind == FLATTENED || kind == INHERITED, "Other kind do not have a field index"); assert(size > 0, "Sanity check"); assert(alignment > 0, "Sanity check"); } bool LayoutRawBlock::fit(int size, int alignment) { int adjustment = 0; if ((_offset % alignment) != 0) { adjustment = alignment - (_offset % alignment); } return _size >= size + adjustment; } FieldGroup::FieldGroup(int contended_group) : _next(NULL), _primitive_fields(NULL), _oop_fields(NULL), _flattened_fields(NULL), _contended_group(contended_group), // -1 means no contended group, 0 means default contended group _oop_count(0) {} void FieldGroup::add_primitive_field(AllFieldStream fs, BasicType type) { int size = type2aelembytes(type); LayoutRawBlock* block = new LayoutRawBlock(fs.index(), LayoutRawBlock::REGULAR, size, size /* alignment == size for primitive types */, false); if (_primitive_fields == NULL) { _primitive_fields = new(ResourceObj::RESOURCE_AREA, mtInternal) GrowableArray(INITIAL_LIST_SIZE); } _primitive_fields->append(block); } void FieldGroup::add_oop_field(AllFieldStream fs) { int size = type2aelembytes(T_OBJECT); LayoutRawBlock* block = new LayoutRawBlock(fs.index(), LayoutRawBlock::REGULAR, size, size /* alignment == size for oops */, true); if (_oop_fields == NULL) { _oop_fields = new(ResourceObj::RESOURCE_AREA, mtInternal) GrowableArray(INITIAL_LIST_SIZE); } _oop_fields->append(block); _oop_count++; } void FieldGroup::add_flattened_field(AllFieldStream fs, ValueKlass* vk) { // _flattened_fields list might be merged with the _primitive_fields list in the future LayoutRawBlock* block = new LayoutRawBlock(fs.index(), LayoutRawBlock::FLATTENED, vk->get_exact_size_in_bytes(), vk->get_alignment(), false); block->set_value_klass(vk); if (_flattened_fields == NULL) { _flattened_fields = new(ResourceObj::RESOURCE_AREA, mtInternal) GrowableArray(INITIAL_LIST_SIZE); } _flattened_fields->append(block); } void FieldGroup::sort_by_size() { if (_primitive_fields != NULL) { _primitive_fields->sort(LayoutRawBlock::compare_size_inverted); } if (_flattened_fields != NULL) { _flattened_fields->sort(LayoutRawBlock::compare_size_inverted); } } FieldLayout::FieldLayout(Array* fields, ConstantPool* cp) : _fields(fields), _cp(cp), _blocks(NULL), _start(_blocks), _last(_blocks) {} void FieldLayout::initialize_static_layout() { _blocks = new LayoutRawBlock(LayoutRawBlock::EMPTY, INT_MAX); _blocks->set_offset(0); _last = _blocks; _start = _blocks; // Note: at this stage, InstanceMirrorKlass::offset_of_static_fields() could be zero, because // during bootstrapping, the size of the java.lang.Class is still not known when layout // of static field is computed. Field offsets are fixed later when the size is known // (see java_lang_Class::fixup_mirror()) if (InstanceMirrorKlass::offset_of_static_fields() > 0) { insert(first_empty_block(), new LayoutRawBlock(LayoutRawBlock::RESERVED, InstanceMirrorKlass::offset_of_static_fields())); _blocks->set_offset(0); } } void FieldLayout::initialize_instance_layout(const InstanceKlass* super_klass) { if (super_klass == NULL) { _blocks = new LayoutRawBlock(LayoutRawBlock::EMPTY, INT_MAX); _blocks->set_offset(0); _last = _blocks; _start = _blocks; insert(first_empty_block(), new LayoutRawBlock(LayoutRawBlock::RESERVED, instanceOopDesc::base_offset_in_bytes())); } else { bool has_fields = reconstruct_layout(super_klass); fill_holes(super_klass); if ((UseEmptySlotsInSupers && !super_klass->has_contended_annotations()) || !has_fields) { _start = _blocks; // Setting _start to _blocks instead of _last would allow subclasses // to allocate fields in empty slots of their super classes } else { _start = _last; } } } LayoutRawBlock* FieldLayout::first_field_block() { LayoutRawBlock* block = _blocks; while (block != NULL && block->kind() != LayoutRawBlock::INHERITED && block->kind() != LayoutRawBlock::REGULAR && block->kind() != LayoutRawBlock::FLATTENED) { block = block->next_block(); } return block; } // Insert a set of fields into a layout. // For each field, search for an empty slot able to fit the field // (satisfying both size and alignment requirements), if none is found, // add the field at the end of the layout. // Fields cannot be inserted before the block specified in the "start" argument void FieldLayout::add(GrowableArray* list, LayoutRawBlock* start) { if (list == NULL) return; if (start == NULL) start = this->_start; bool last_search_success = false; int last_size = 0; int last_alignment = 0; for (int i = 0; i < list->length(); i ++) { LayoutRawBlock* b = list->at(i); LayoutRawBlock* cursor = NULL; LayoutRawBlock* candidate = NULL; // if start is the last block, just append the field if (start == last_block()) { candidate = last_block(); } // Before iterating over the layout to find an empty slot fitting the field's requirements, // check if the previous field had the same requirements and if the search for a fitting slot // was successful. If the requirements were the same but the search failed, a new search will // fail the same way, so just append the field at the of the layout. else if (b->size() == last_size && b->alignment() == last_alignment && !last_search_success) { candidate = last_block(); } else { // Iterate over the layout to find an empty slot fitting the field's requirements last_size = b->size(); last_alignment = b->alignment(); cursor = last_block()->prev_block(); assert(cursor != NULL, "Sanity check"); last_search_success = true; while (cursor != start) { if (cursor->kind() == LayoutRawBlock::EMPTY && cursor->fit(b->size(), b->alignment())) { if (candidate == NULL || cursor->size() < candidate->size()) { candidate = cursor; } } cursor = cursor->prev_block(); } if (candidate == NULL) { candidate = last_block(); last_search_success = false; } assert(candidate != NULL, "Candidate must not be null"); assert(candidate->kind() == LayoutRawBlock::EMPTY, "Candidate must be an empty block"); assert(candidate->fit(b->size(), b->alignment()), "Candidate must be able to store the block"); } insert_field_block(candidate, b); } } // Used for classes with hard coded field offsets, insert a field at the specified offset */ void FieldLayout::add_field_at_offset(LayoutRawBlock* block, int offset, LayoutRawBlock* start) { assert(block != NULL, "Sanity check"); block->set_offset(offset); if (start == NULL) { start = this->_start; } LayoutRawBlock* slot = start; while (slot != NULL) { if ((slot->offset() <= block->offset() && (slot->offset() + slot->size()) > block->offset()) || slot == _last){ assert(slot->kind() == LayoutRawBlock::EMPTY, "Matching slot must be an empty slot"); assert(slot->size() >= block->offset() + block->size() ,"Matching slot must be big enough"); if (slot->offset() < block->offset()) { int adjustment = block->offset() - slot->offset(); LayoutRawBlock* adj = new LayoutRawBlock(LayoutRawBlock::EMPTY, adjustment); insert(slot, adj); } insert(slot, block); if (slot->size() == 0) { remove(slot); } FieldInfo::from_field_array(_fields, block->field_index())->set_offset(block->offset()); return; } slot = slot->next_block(); } fatal("Should have found a matching slot above, corrupted layout or invalid offset"); } // The allocation logic uses a best fit strategy: the set of fields is allocated // in the first empty slot big enough to contain the whole set ((including padding // to fit alignment constraints). void FieldLayout::add_contiguously(GrowableArray* list, LayoutRawBlock* start) { if (list == NULL) return; if (start == NULL) { start = _start; } // This code assumes that if the first block is well aligned, the following // blocks would naturally be well aligned (no need for adjustment) int size = 0; for (int i = 0; i < list->length(); i++) { size += list->at(i)->size(); } LayoutRawBlock* candidate = NULL; if (start == last_block()) { candidate = last_block(); } else { LayoutRawBlock* first = list->at(0); candidate = last_block()->prev_block(); while (candidate->kind() != LayoutRawBlock::EMPTY || !candidate->fit(size, first->alignment())) { if (candidate == start) { candidate = last_block(); break; } candidate = candidate->prev_block(); } assert(candidate != NULL, "Candidate must not be null"); assert(candidate->kind() == LayoutRawBlock::EMPTY, "Candidate must be an empty block"); assert(candidate->fit(size, first->alignment()), "Candidate must be able to store the whole contiguous block"); } for (int i = 0; i < list->length(); i++) { LayoutRawBlock* b = list->at(i); insert_field_block(candidate, b); assert((candidate->offset() % b->alignment() == 0), "Contiguous blocks must be naturally well aligned"); } } LayoutRawBlock* FieldLayout::insert_field_block(LayoutRawBlock* slot, LayoutRawBlock* block) { assert(slot->kind() == LayoutRawBlock::EMPTY, "Blocks can only be inserted in empty blocks"); if (slot->offset() % block->alignment() != 0) { int adjustment = block->alignment() - (slot->offset() % block->alignment()); LayoutRawBlock* adj = new LayoutRawBlock(LayoutRawBlock::EMPTY, adjustment); insert(slot, adj); } insert(slot, block); if (slot->size() == 0) { remove(slot); } FieldInfo::from_field_array(_fields, block->field_index())->set_offset(block->offset()); return block; } bool FieldLayout::reconstruct_layout(const InstanceKlass* ik) { bool has_instance_fields = false; GrowableArray* all_fields = new GrowableArray(32); while (ik != NULL) { for (AllFieldStream fs(ik->fields(), ik->constants()); !fs.done(); fs.next()) { BasicType type = Signature::basic_type(fs.signature()); // distinction between static and non-static fields is missing if (fs.access_flags().is_static()) continue; has_instance_fields = true; LayoutRawBlock* block; if (type == T_VALUETYPE) { ValueKlass* vk = ValueKlass::cast(ik->get_value_field_klass(fs.index())); block = new LayoutRawBlock(fs.index(), LayoutRawBlock::INHERITED, vk->get_exact_size_in_bytes(), vk->get_alignment(), false); } else { int size = type2aelembytes(type); // INHERITED blocks are marked as non-reference because oop_maps are handled by their holder class block = new LayoutRawBlock(fs.index(), LayoutRawBlock::INHERITED, size, size, false); } block->set_offset(fs.offset()); all_fields->append(block); } ik = ik->super() == NULL ? NULL : InstanceKlass::cast(ik->super()); } all_fields->sort(LayoutRawBlock::compare_offset); _blocks = new LayoutRawBlock(LayoutRawBlock::RESERVED, instanceOopDesc::base_offset_in_bytes()); _blocks->set_offset(0); _last = _blocks; for(int i = 0; i < all_fields->length(); i++) { LayoutRawBlock* b = all_fields->at(i); _last->set_next_block(b); b->set_prev_block(_last); _last = b; } _start = _blocks; return has_instance_fields; } // Called during the reconstruction of a layout, after fields from super // classes have been inserted. It fills unused slots between inserted fields // with EMPTY blocks, so the regular field insertion methods would work. // This method handles classes with @Contended annotations differently // by inserting PADDING blocks instead of EMPTY block to prevent subclasses' // fields to interfere with contended fields/classes. void FieldLayout::fill_holes(const InstanceKlass* super_klass) { assert(_blocks != NULL, "Sanity check"); assert(_blocks->offset() == 0, "first block must be at offset zero"); LayoutRawBlock::Kind filling_type = super_klass->has_contended_annotations() ? LayoutRawBlock::PADDING: LayoutRawBlock::EMPTY; LayoutRawBlock* b = _blocks; while (b->next_block() != NULL) { if (b->next_block()->offset() > (b->offset() + b->size())) { int size = b->next_block()->offset() - (b->offset() + b->size()); LayoutRawBlock* empty = new LayoutRawBlock(filling_type, size); empty->set_offset(b->offset() + b->size()); empty->set_next_block(b->next_block()); b->next_block()->set_prev_block(empty); b->set_next_block(empty); empty->set_prev_block(b); } b = b->next_block(); } assert(b->next_block() == NULL, "Invariant at this point"); assert(b->kind() != LayoutRawBlock::EMPTY, "Sanity check"); // If the super class has @Contended annotation, a padding block is // inserted at the end to ensure that fields from the subclasses won't share // the cache line of the last field of the contended class if (super_klass->has_contended_annotations()) { LayoutRawBlock* p = new LayoutRawBlock(LayoutRawBlock::PADDING, ContendedPaddingWidth); p->set_offset(b->offset() + b->size()); b->set_next_block(p); p->set_prev_block(b); b = p; } if (!UseEmptySlotsInSupers) { // Add an empty slots to align fields of the subclass on a heapOopSize boundary // in order to emulate the behavior of the previous algorithm int align = (b->offset() + b->size()) % heapOopSize; if (align != 0) { int sz = heapOopSize - align; LayoutRawBlock* p = new LayoutRawBlock(LayoutRawBlock::EMPTY, sz); p->set_offset(b->offset() + b->size()); b->set_next_block(p); p->set_prev_block(b); b = p; } } LayoutRawBlock* last = new LayoutRawBlock(LayoutRawBlock::EMPTY, INT_MAX); last->set_offset(b->offset() + b->size()); assert(last->offset() > 0, "Sanity check"); b->set_next_block(last); last->set_prev_block(b); _last = last; } LayoutRawBlock* FieldLayout::insert(LayoutRawBlock* slot, LayoutRawBlock* block) { assert(slot->kind() == LayoutRawBlock::EMPTY, "Blocks can only be inserted in empty blocks"); assert(slot->offset() % block->alignment() == 0, "Incompatible alignment"); block->set_offset(slot->offset()); slot->set_offset(slot->offset() + block->size()); assert((slot->size() - block->size()) < slot->size(), "underflow checking"); assert(slot->size() - block->size() >= 0, "no negative size allowed"); slot->set_size(slot->size() - block->size()); block->set_prev_block(slot->prev_block()); block->set_next_block(slot); slot->set_prev_block(block); if (block->prev_block() != NULL) { block->prev_block()->set_next_block(block); } if (_blocks == slot) { _blocks = block; } return block; } void FieldLayout::remove(LayoutRawBlock* block) { assert(block != NULL, "Sanity check"); assert(block != _last, "Sanity check"); if (_blocks == block) { _blocks = block->next_block(); if (_blocks != NULL) { _blocks->set_prev_block(NULL); } } else { assert(block->prev_block() != NULL, "_prev should be set for non-head blocks"); block->prev_block()->set_next_block(block->next_block()); block->next_block()->set_prev_block(block->prev_block()); } if (block == _start) { _start = block->prev_block(); } } void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlass* super) { ResourceMark rm; LayoutRawBlock* b = _blocks; while(b != _last) { switch(b->kind()) { case LayoutRawBlock::REGULAR: { FieldInfo* fi = FieldInfo::from_field_array(_fields, b->field_index()); output->print_cr(" @%d \"%s\" %s %d/%d %s", b->offset(), fi->name(_cp)->as_C_string(), fi->signature(_cp)->as_C_string(), b->size(), b->alignment(), "REGULAR"); break; } case LayoutRawBlock::FLATTENED: { FieldInfo* fi = FieldInfo::from_field_array(_fields, b->field_index()); output->print_cr(" @%d \"%s\" %s %d/%d %s", b->offset(), fi->name(_cp)->as_C_string(), fi->signature(_cp)->as_C_string(), b->size(), b->alignment(), "FLATTENED"); break; } case LayoutRawBlock::RESERVED: { output->print_cr(" @%d %d/- %s", b->offset(), b->size(), "RESERVED"); break; } case LayoutRawBlock::INHERITED: { assert(!is_static, "Static fields are not inherited in layouts"); assert(super != NULL, "super klass must be provided to retrieve inherited fields info"); bool found = false; const InstanceKlass* ik = super; while (!found && ik != NULL) { for (AllFieldStream fs(ik->fields(), ik->constants()); !fs.done(); fs.next()) { if (fs.offset() == b->offset()) { output->print_cr(" @%d \"%s\" %s %d/%d %s", b->offset(), fs.name()->as_C_string(), fs.signature()->as_C_string(), b->size(), b->size(), // so far, alignment constraint == size, will change with Valhalla "INHERITED"); found = true; break; } } ik = ik->java_super(); } break; } case LayoutRawBlock::EMPTY: output->print_cr(" @%d %d/1 %s", b->offset(), b->size(), "EMPTY"); break; case LayoutRawBlock::PADDING: output->print_cr(" @%d %d/1 %s", b->offset(), b->size(), "PADDING"); break; } b = b->next_block(); } } FieldLayoutBuilder::FieldLayoutBuilder(const Symbol* classname, const InstanceKlass* super_klass, ConstantPool* constant_pool, Array* fields, bool is_contended, bool is_value_type, ClassLoaderData* class_loader_data, Handle protection_domain, FieldLayoutInfo* info) : _classname(classname), _super_klass(super_klass), _constant_pool(constant_pool), _fields(fields), _info(info), _root_group(NULL), _contended_groups(GrowableArray(8)), _static_fields(NULL), _layout(NULL), _static_layout(NULL), _class_loader_data(class_loader_data), _protection_domain(protection_domain), _nonstatic_oopmap_count(0), _alignment(-1), _first_field_offset(-1), _exact_size_in_bytes(-1), _has_nonstatic_fields(false), _is_contended(is_contended), _is_value_type(is_value_type), _has_flattening_information(is_value_type), _has_nonatomic_values(false), _atomic_field_count(0) {} FieldGroup* FieldLayoutBuilder::get_or_create_contended_group(int g) { assert(g > 0, "must only be called for named contended groups"); FieldGroup* fg = NULL; for (int i = 0; i < _contended_groups.length(); i++) { fg = _contended_groups.at(i); if (fg->contended_group() == g) return fg; } fg = new FieldGroup(g); _contended_groups.append(fg); return fg; } void FieldLayoutBuilder::prologue() { _layout = new FieldLayout(_fields, _constant_pool); const InstanceKlass* super_klass = _super_klass; _layout->initialize_instance_layout(super_klass); if (super_klass != NULL) { _has_nonstatic_fields = super_klass->has_nonstatic_fields(); } _static_layout = new FieldLayout(_fields, _constant_pool); _static_layout->initialize_static_layout(); _static_fields = new FieldGroup(); _root_group = new FieldGroup(); } // Field sorting for regular (non-inline) classes: // - fields are sorted in static and non-static fields // - non-static fields are also sorted according to their contention group // (support of the @Contended annotation) // - @Contended annotation is ignored for static fields // - field flattening decisions are taken in this method void FieldLayoutBuilder::regular_field_sorting() { for (AllFieldStream fs(_fields, _constant_pool); !fs.done(); fs.next()) { FieldGroup* group = NULL; if (fs.access_flags().is_static()) { group = _static_fields; } else { _has_nonstatic_fields = true; _atomic_field_count++; // we might decrement this if (fs.is_contended()) { int g = fs.contended_group(); if (g == 0) { group = new FieldGroup(true); _contended_groups.append(group); } else { group = get_or_create_contended_group(g); } } else { group = _root_group; } } assert(group != NULL, "invariant"); BasicType type = Signature::basic_type(fs.signature()); switch(type) { case T_BYTE: case T_CHAR: case T_DOUBLE: case T_FLOAT: case T_INT: case T_LONG: case T_SHORT: case T_BOOLEAN: group->add_primitive_field(fs, type); break; case T_OBJECT: case T_ARRAY: if (group != _static_fields) _nonstatic_oopmap_count++; group->add_oop_field(fs); break; case T_VALUETYPE: if (group == _static_fields) { // static fields are never flattened group->add_oop_field(fs); } else { _has_flattening_information = true; // Flattening decision to be taken here // This code assumes all verification have been performed before // (field is a flattenable field, field's type has been loaded // and it is an inline klass Thread* THREAD = Thread::current(); Klass* klass = SystemDictionary::resolve_flattenable_field_or_fail(&fs, Handle(THREAD, _class_loader_data->class_loader()), _protection_domain, true, THREAD); assert(klass != NULL, "Sanity check"); ValueKlass* vk = ValueKlass::cast(klass); bool too_big_to_flatten = (ValueFieldMaxFlatSize >= 0 && (vk->size_helper() * HeapWordSize) > ValueFieldMaxFlatSize); bool too_atomic_to_flatten = vk->is_declared_atomic(); bool too_volatile_to_flatten = fs.access_flags().is_volatile(); if (vk->is_naturally_atomic()) { too_atomic_to_flatten = false; //too_volatile_to_flatten = false; //FIXME // volatile fields are currently never flattened, this could change in the future } if (!(too_big_to_flatten | too_atomic_to_flatten | too_volatile_to_flatten)) { group->add_flattened_field(fs, vk); _nonstatic_oopmap_count += vk->nonstatic_oop_map_count(); fs.set_flattened(true); if (!vk->is_atomic()) { // flat and non-atomic: take note _has_nonatomic_values = true; _atomic_field_count--; // every other field is atomic but this one } } else { _nonstatic_oopmap_count++; group->add_oop_field(fs); } } break; default: fatal("Something wrong?"); } } _root_group->sort_by_size(); _static_fields->sort_by_size(); if (!_contended_groups.is_empty()) { for (int i = 0; i < _contended_groups.length(); i++) { _contended_groups.at(i)->sort_by_size(); } } } /* Field sorting for inline classes: * - because inline classes are immutable, the @Contended annotation is ignored * when computing their layout (with only read operation, there's no false * sharing issue) * - this method also records the alignment of the field with the most * constraining alignment, this value is then used as the alignment * constraint when flattening this inline type into another container * - field flattening decisions are taken in this method (those decisions are * currently only based in the size of the fields to be flattened, the size * of the resulting instance is not considered) */ void FieldLayoutBuilder::inline_class_field_sorting(TRAPS) { assert(_is_value_type, "Should only be used for inline classes"); int alignment = 1; for (AllFieldStream fs(_fields, _constant_pool); !fs.done(); fs.next()) { FieldGroup* group = NULL; int field_alignment = 1; if (fs.access_flags().is_static()) { group = _static_fields; } else { _has_nonstatic_fields = true; _atomic_field_count++; // we might decrement this group = _root_group; } assert(group != NULL, "invariant"); BasicType type = Signature::basic_type(fs.signature()); switch(type) { case T_BYTE: case T_CHAR: case T_DOUBLE: case T_FLOAT: case T_INT: case T_LONG: case T_SHORT: case T_BOOLEAN: if (group != _static_fields) { field_alignment = type2aelembytes(type); // alignment == size for primitive types } group->add_primitive_field(fs, type); break; case T_OBJECT: case T_ARRAY: if (group != _static_fields) { _nonstatic_oopmap_count++; field_alignment = type2aelembytes(type); // alignment == size for oops } group->add_oop_field(fs); break; case T_VALUETYPE: { if (group == _static_fields) { // static fields are never flattened group->add_oop_field(fs); } else { // Flattening decision to be taken here // This code assumes all verifications have been performed before // (field is a flattenable field, field's type has been loaded // and it is an inline klass Thread* THREAD = Thread::current(); Klass* klass = SystemDictionary::resolve_flattenable_field_or_fail(&fs, Handle(THREAD, _class_loader_data->class_loader()), _protection_domain, true, CHECK); assert(klass != NULL, "Sanity check"); ValueKlass* vk = ValueKlass::cast(klass); bool too_big_to_flatten = (ValueFieldMaxFlatSize >= 0 && (vk->size_helper() * HeapWordSize) > ValueFieldMaxFlatSize); bool too_atomic_to_flatten = vk->is_declared_atomic(); bool too_volatile_to_flatten = fs.access_flags().is_volatile(); if (vk->is_naturally_atomic()) { too_atomic_to_flatten = false; //too_volatile_to_flatten = false; //FIXME // volatile fields are currently never flattened, this could change in the future } if (!(too_big_to_flatten | too_atomic_to_flatten | too_volatile_to_flatten)) { group->add_flattened_field(fs, vk); _nonstatic_oopmap_count += vk->nonstatic_oop_map_count(); field_alignment = vk->get_alignment(); fs.set_flattened(true); if (!vk->is_atomic()) { // flat and non-atomic: take note _has_nonatomic_values = true; _atomic_field_count--; // every other field is atomic but this one } } else { _nonstatic_oopmap_count++; field_alignment = type2aelembytes(T_OBJECT); group->add_oop_field(fs); } } break; } default: fatal("Unexpected BasicType"); } if (!fs.access_flags().is_static() && field_alignment > alignment) alignment = field_alignment; } _alignment = alignment; if (!_has_nonstatic_fields) { // There are a number of fixes required throughout the type system and JIT Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_ClassFormatError(), "Value Types do not support zero instance size yet"); return; } } void FieldLayoutBuilder::insert_contended_padding(LayoutRawBlock* slot) { if (ContendedPaddingWidth > 0) { LayoutRawBlock* padding = new LayoutRawBlock(LayoutRawBlock::PADDING, ContendedPaddingWidth); _layout->insert(slot, padding); } } /* Computation of regular classes layout is an evolution of the previous default layout * (FieldAllocationStyle 1): * - flattened fields are allocated first (because they have potentially the * least regular shapes, and are more likely to create empty slots between them, * which can then be used to allocation primitive or oop fields). Allocation is * performed from the biggest to the smallest flattened field. * - then primitive fields (from the biggest to the smallest) * - then oop fields are allocated contiguously (to reduce the number of oopmaps * and reduce the work of the GC). */ void FieldLayoutBuilder::compute_regular_layout() { bool need_tail_padding = false; prologue(); regular_field_sorting(); if (_is_contended) { _layout->set_start(_layout->last_block()); // insertion is currently easy because the current strategy doesn't try to fill holes // in super classes layouts => the _start block is by consequence the _last_block insert_contended_padding(_layout->start()); need_tail_padding = true; } _layout->add(_root_group->flattened_fields()); _layout->add(_root_group->primitive_fields()); _layout->add(_root_group->oop_fields()); if (!_contended_groups.is_empty()) { for (int i = 0; i < _contended_groups.length(); i++) { FieldGroup* cg = _contended_groups.at(i); LayoutRawBlock* start = _layout->last_block(); insert_contended_padding(start); _layout->add(_root_group->flattened_fields()); _layout->add(cg->primitive_fields(), start); _layout->add(cg->oop_fields(), start); need_tail_padding = true; } } if (need_tail_padding) { insert_contended_padding(_layout->last_block()); } _static_layout->add(_static_fields->flattened_fields()); _static_layout->add_contiguously(_static_fields->oop_fields()); _static_layout->add(_static_fields->primitive_fields()); epilogue(); } /* Computation of inline classes has a slightly different strategy than for * regular classes. Regular classes have their oop fields allocated at the end * of the layout to increase GC performances. Unfortunately, this strategy * increases the number of empty slots inside an instance. Because the purpose * of inline classes is to be embedded into other containers, it is critical * to keep their size as small as possible. For this reason, the allocation * strategy is: * - flattened fields are allocated first (because they have potentially the * least regular shapes, and are more likely to create empty slots between them, * which can then be used to allocation primitive or oop fields). Allocation is * performed from the biggest to the smallest flattened field. * - then oop fields are allocated contiguously (to reduce the number of oopmaps * and reduce the work of the GC) * - then primitive fields (from the biggest to the smallest) */ void FieldLayoutBuilder::compute_inline_class_layout(TRAPS) { prologue(); inline_class_field_sorting(CHECK); // Inline types are not polymorphic, so they cannot inherit fields. // By consequence, at this stage, the layout must be composed of a RESERVED // block, followed by an EMPTY block. assert(_layout->start()->kind() == LayoutRawBlock::RESERVED, "Unexpected"); assert(_layout->start()->next_block()->kind() == LayoutRawBlock::EMPTY, "Unexpected"); LayoutRawBlock* first_empty = _layout->start()->next_block(); if (first_empty->offset() % _alignment != 0) { LayoutRawBlock* padding = new LayoutRawBlock(LayoutRawBlock::PADDING, _alignment - (first_empty->offset() % _alignment)); _layout->insert(first_empty, padding); _layout->set_start(padding->next_block()); } _layout->add(_root_group->flattened_fields()); _layout->add(_root_group->oop_fields()); _layout->add(_root_group->primitive_fields()); LayoutRawBlock* first_field = _layout->first_field_block(); if (first_field != NULL) { _first_field_offset = _layout->first_field_block()->offset(); _exact_size_in_bytes = _layout->last_block()->offset() - _layout->first_field_block()->offset(); } else { // special case for empty value types _first_field_offset = _layout->blocks()->size(); _exact_size_in_bytes = 0; } _exact_size_in_bytes = _layout->last_block()->offset() - _layout->first_field_block()->offset(); _static_layout->add(_static_fields->flattened_fields()); _static_layout->add_contiguously(_static_fields->oop_fields()); _static_layout->add(_static_fields->primitive_fields()); epilogue(); } // Compute layout of the java/lang/ref/Reference class according // to the hard coded offsets of its fields void FieldLayoutBuilder::compute_java_lang_ref_Reference_layout() { prologue(); regular_field_sorting(); assert(_contended_groups.is_empty(), "java.lang.Reference has no @Contended annotations"); assert(_root_group->primitive_fields() == NULL, "java.lang.Reference has no nonstatic primitive fields"); int field_count = 0; int offset = -1; for (int i = 0; i < _root_group->oop_fields()->length(); i++) { LayoutRawBlock* b = _root_group->oop_fields()->at(i); FieldInfo* fi = FieldInfo::from_field_array(_fields, b->field_index()); if (fi->name(_constant_pool)->equals("referent")) { offset = java_lang_ref_Reference::referent_offset; } else if (fi->name(_constant_pool)->equals("queue")) { offset = java_lang_ref_Reference::queue_offset; } else if (fi->name(_constant_pool)->equals("next")) { offset = java_lang_ref_Reference::next_offset; } else if (fi->name(_constant_pool)->equals("discovered")) { offset = java_lang_ref_Reference::discovered_offset; } assert(offset != -1, "Unknown field"); _layout->add_field_at_offset(b, offset); field_count++; } assert(field_count == 4, "Wrong number of fields in java.lang.ref.Reference"); _static_layout->add_contiguously(this->_static_fields->oop_fields()); _static_layout->add(this->_static_fields->primitive_fields()); epilogue(); } // Compute layout of the boxing class according // to the hard coded offsets of their fields void FieldLayoutBuilder::compute_boxing_class_layout() { prologue(); regular_field_sorting(); assert(_contended_groups.is_empty(), "Boxing classes have no @Contended annotations"); assert(_root_group->oop_fields() == NULL, "Boxing classes have no nonstatic oops fields"); int field_count = 0; int offset = -1; for (int i = 0; i < _root_group->primitive_fields()->length(); i++) { LayoutRawBlock* b = _root_group->primitive_fields()->at(i); FieldInfo* fi = FieldInfo::from_field_array(_fields, b->field_index()); assert(fi->name(_constant_pool)->equals("value"), "Boxing classes have a single nonstatic field named 'value'"); BasicType type = Signature::basic_type(fi->signature(_constant_pool)); offset = java_lang_boxing_object::value_offset_in_bytes(type); assert(offset != -1, "Unknown field"); _layout->add_field_at_offset(b, offset); field_count++; } assert(field_count == 1, "Wrong number of fields for a boxing class"); _static_layout->add_contiguously(this->_static_fields->oop_fields()); _static_layout->add(this->_static_fields->primitive_fields()); epilogue(); } void FieldLayoutBuilder::add_flattened_field_oopmap(OopMapBlocksBuilder* nonstatic_oop_maps, ValueKlass* vklass, int offset) { int diff = offset - vklass->first_field_offset(); const OopMapBlock* map = vklass->start_of_nonstatic_oop_maps(); const OopMapBlock* last_map = map + vklass->nonstatic_oop_map_count(); while (map < last_map) { nonstatic_oop_maps->add(map->offset() + diff, map->count()); map++; } } void FieldLayoutBuilder::epilogue() { // Computing oopmaps int super_oop_map_count = (_super_klass == NULL) ? 0 :_super_klass->nonstatic_oop_map_count(); int max_oop_map_count = super_oop_map_count + _nonstatic_oopmap_count; OopMapBlocksBuilder* nonstatic_oop_maps = new OopMapBlocksBuilder(max_oop_map_count); if (super_oop_map_count > 0) { nonstatic_oop_maps->initialize_inherited_blocks(_super_klass->start_of_nonstatic_oop_maps(), _super_klass->nonstatic_oop_map_count()); } if (_root_group->oop_fields() != NULL) { for (int i = 0; i < _root_group->oop_fields()->length(); i++) { LayoutRawBlock* b = _root_group->oop_fields()->at(i); nonstatic_oop_maps->add(b->offset(), 1); } } GrowableArray* ff = _root_group->flattened_fields(); if (ff != NULL) { for (int i = 0; i < ff->length(); i++) { LayoutRawBlock* f = ff->at(i); ValueKlass* vk = f->value_klass(); assert(vk != NULL, "Should have been initialized"); if (vk->contains_oops()) { add_flattened_field_oopmap(nonstatic_oop_maps, vk, f->offset()); } } } if (!_contended_groups.is_empty()) { for (int i = 0; i < _contended_groups.length(); i++) { FieldGroup* cg = _contended_groups.at(i); if (cg->oop_count() > 0) { assert(cg->oop_fields() != NULL && cg->oop_fields()->at(0) != NULL, "oop_count > 0 but no oop fields found"); nonstatic_oop_maps->add(cg->oop_fields()->at(0)->offset(), cg->oop_count()); } } } nonstatic_oop_maps->compact(); int instance_end = align_up(_layout->last_block()->offset(), wordSize); int static_fields_end = align_up(_static_layout->last_block()->offset(), wordSize); int static_fields_size = (static_fields_end - InstanceMirrorKlass::offset_of_static_fields()) / wordSize; int nonstatic_field_end = align_up(_layout->last_block()->offset(), heapOopSize); // Pass back information needed for InstanceKlass creation _info->oop_map_blocks = nonstatic_oop_maps; _info->_instance_size = align_object_size(instance_end / wordSize); _info->_static_field_size = static_fields_size; _info->_nonstatic_field_size = (nonstatic_field_end - instanceOopDesc::base_offset_in_bytes()) / heapOopSize; _info->_has_nonstatic_fields = _has_nonstatic_fields; // A value type is naturally atomic if it has just one field, and // that field is simple enough. _info->_is_naturally_atomic = (_is_value_type && (_atomic_field_count <= 1) && !_has_nonatomic_values && _contended_groups.is_empty()); // This may be too restrictive, since if all the fields fit in 64 // bits we could make the decision to align instances of this class // to 64-bit boundaries, and load and store them as single words. // And on machines which supported larger atomics we could similarly // allow larger values to be atomic, if properly aligned. if (PrintFieldLayout) { ResourceMark rm; tty->print_cr("Layout of class %s", _classname->as_C_string()); tty->print_cr("Instance fields:"); _layout->print(tty, false, _super_klass); tty->print_cr("Static fields:"); _static_layout->print(tty, true, NULL); tty->print_cr("Instance size = %d bytes", _info->_instance_size * wordSize); if (_is_value_type) { tty->print_cr("First field offset = %d", _first_field_offset); tty->print_cr("Alignment = %d bytes", _alignment); tty->print_cr("Exact size = %d bytes", _exact_size_in_bytes); } tty->print_cr("---"); } } void FieldLayoutBuilder::build_layout(TRAPS) { if (_classname == vmSymbols::java_lang_ref_Reference()) { compute_java_lang_ref_Reference_layout(); } else if (_classname == vmSymbols::java_lang_Boolean() || _classname == vmSymbols::java_lang_Character() || _classname == vmSymbols::java_lang_Float() || _classname == vmSymbols::java_lang_Double() || _classname == vmSymbols::java_lang_Byte() || _classname == vmSymbols::java_lang_Short() || _classname == vmSymbols::java_lang_Integer() || _classname == vmSymbols::java_lang_Long()) { compute_boxing_class_layout(); } else if (_is_value_type) { compute_inline_class_layout(CHECK); } else { compute_regular_layout(); } }