Skip to content

Commit 0694cc1

Browse files
committed
8352075: Perf regression accessing fields
Reviewed-by: shade, iklam Backport-of: e18277b
1 parent a3abaad commit 0694cc1

18 files changed

+904
-70
lines changed

src/hotspot/share/classfile/classFileParser.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3738,6 +3738,7 @@ void ClassFileParser::apply_parsed_class_metadata(
37383738
_cp->set_pool_holder(this_klass);
37393739
this_klass->set_constants(_cp);
37403740
this_klass->set_fieldinfo_stream(_fieldinfo_stream);
3741+
this_klass->set_fieldinfo_search_table(_fieldinfo_search_table);
37413742
this_klass->set_fields_status(_fields_status);
37423743
this_klass->set_methods(_methods);
37433744
this_klass->set_inner_classes(_inner_classes);
@@ -3747,6 +3748,8 @@ void ClassFileParser::apply_parsed_class_metadata(
37473748
this_klass->set_permitted_subclasses(_permitted_subclasses);
37483749
this_klass->set_record_components(_record_components);
37493750

3751+
DEBUG_ONLY(FieldInfoStream::validate_search_table(_cp, _fieldinfo_stream, _fieldinfo_search_table));
3752+
37503753
// Delay the setting of _local_interfaces and _transitive_interfaces until after
37513754
// initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
37523755
// be shared with _transitive_interfaces and _transitive_interfaces may be shared with
@@ -5054,6 +5057,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
50545057
// note that is not safe to use the fields in the parser from this point on
50555058
assert(nullptr == _cp, "invariant");
50565059
assert(nullptr == _fieldinfo_stream, "invariant");
5060+
assert(nullptr == _fieldinfo_search_table, "invariant");
50575061
assert(nullptr == _fields_status, "invariant");
50585062
assert(nullptr == _methods, "invariant");
50595063
assert(nullptr == _inner_classes, "invariant");
@@ -5274,6 +5278,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
52745278
_super_klass(),
52755279
_cp(nullptr),
52765280
_fieldinfo_stream(nullptr),
5281+
_fieldinfo_search_table(nullptr),
52775282
_fields_status(nullptr),
52785283
_methods(nullptr),
52795284
_inner_classes(nullptr),
@@ -5350,6 +5355,7 @@ void ClassFileParser::clear_class_metadata() {
53505355
// deallocated if classfile parsing returns an error.
53515356
_cp = nullptr;
53525357
_fieldinfo_stream = nullptr;
5358+
_fieldinfo_search_table = nullptr;
53535359
_fields_status = nullptr;
53545360
_methods = nullptr;
53555361
_inner_classes = nullptr;
@@ -5372,6 +5378,7 @@ ClassFileParser::~ClassFileParser() {
53725378
if (_fieldinfo_stream != nullptr) {
53735379
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_stream);
53745380
}
5381+
MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_search_table);
53755382

53765383
if (_fields_status != nullptr) {
53775384
MetadataFactory::free_array<FieldStatus>(_loader_data, _fields_status);
@@ -5772,6 +5779,7 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st
57725779
_fieldinfo_stream =
57735780
FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count,
57745781
injected_fields_count, loader_data(), CHECK);
5782+
_fieldinfo_search_table = FieldInfoStream::create_search_table(_cp, _fieldinfo_stream, _loader_data, CHECK);
57755783
_fields_status =
57765784
MetadataFactory::new_array<FieldStatus>(_loader_data, _temp_field_info->length(),
57775785
FieldStatus(0), CHECK);

src/hotspot/share/classfile/classFileParser.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -123,6 +123,7 @@ class ClassFileParser {
123123
const InstanceKlass* _super_klass;
124124
ConstantPool* _cp;
125125
Array<u1>* _fieldinfo_stream;
126+
Array<u1>* _fieldinfo_search_table;
126127
Array<FieldStatus>* _fields_status;
127128
Array<Method*>* _methods;
128129
Array<u2>* _inner_classes;

src/hotspot/share/classfile/fieldLayoutBuilder.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ void FieldLayout::reconstruct_layout(const InstanceKlass* ik, bool& has_instance
301301
BasicType last_type;
302302
int last_offset = -1;
303303
while (ik != nullptr) {
304-
for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
304+
for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
305305
BasicType type = Signature::basic_type(fs.signature());
306306
// distinction between static and non-static fields is missing
307307
if (fs.access_flags().is_static()) continue;
@@ -461,7 +461,7 @@ void FieldLayout::print(outputStream* output, bool is_static, const InstanceKlas
461461
bool found = false;
462462
const InstanceKlass* ik = super;
463463
while (!found && ik != nullptr) {
464-
for (AllFieldStream fs(ik->fieldinfo_stream(), ik->constants()); !fs.done(); fs.next()) {
464+
for (AllFieldStream fs(ik); !fs.done(); fs.next()) {
465465
if (fs.offset() == b->offset()) {
466466
output->print_cr(" @%d \"%s\" %s %d/%d %s",
467467
b->offset(),

src/hotspot/share/classfile/javaClasses.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,13 @@ void java_lang_Class::fixup_mirror(Klass* k, TRAPS) {
967967
Array<u1>* new_fis = FieldInfoStream::create_FieldInfoStream(fields, java_fields, injected_fields, k->class_loader_data(), CHECK);
968968
ik->set_fieldinfo_stream(new_fis);
969969
MetadataFactory::free_array<u1>(k->class_loader_data(), old_stream);
970+
971+
Array<u1>* old_table = ik->fieldinfo_search_table();
972+
Array<u1>* search_table = FieldInfoStream::create_search_table(ik->constants(), new_fis, k->class_loader_data(), CHECK);
973+
ik->set_fieldinfo_search_table(search_table);
974+
MetadataFactory::free_array<u1>(k->class_loader_data(), old_table);
975+
976+
DEBUG_ONLY(FieldInfoStream::validate_search_table(ik->constants(), new_fis, search_table));
970977
}
971978
}
972979

src/hotspot/share/oops/fieldInfo.cpp

Lines changed: 214 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
*
2323
*/
2424

25+
#include "memory/resourceArea.hpp"
26+
#include "cds/cdsConfig.hpp"
2527
#include "oops/fieldInfo.inline.hpp"
2628
#include "runtime/atomic.hpp"
29+
#include "utilities/packedTable.hpp"
2730

2831
void FieldInfo::print(outputStream* os, ConstantPool* cp) {
2932
os->print_cr("index=%d name_index=%d name=%s signature_index=%d signature=%s offset=%d "
@@ -37,8 +40,10 @@ void FieldInfo::print(outputStream* os, ConstantPool* cp) {
3740
field_flags().as_uint(),
3841
initializer_index(),
3942
generic_signature_index(),
40-
_field_flags.is_injected() ? lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8(),
41-
contended_group());
43+
_field_flags.is_generic() ? (_field_flags.is_injected() ?
44+
lookup_symbol(generic_signature_index())->as_utf8() : cp->symbol_at(generic_signature_index())->as_utf8()
45+
) : "",
46+
is_contended() ? contended_group() : 0);
4247
}
4348

4449
void FieldInfo::print_from_growable_array(outputStream* os, GrowableArray<FieldInfo>* array, ConstantPool* cp) {
@@ -62,13 +67,17 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
6267
StreamSizer s;
6368
StreamFieldSizer sizer(&s);
6469

70+
assert(fields->length() == java_fields + injected_fields, "must be");
71+
6572
sizer.consumer()->accept_uint(java_fields);
6673
sizer.consumer()->accept_uint(injected_fields);
6774
for (int i = 0; i < fields->length(); i++) {
6875
FieldInfo* fi = fields->adr_at(i);
6976
sizer.map_field_info(*fi);
7077
}
71-
int storage_size = sizer.consumer()->position() + 1;
78+
// Originally there was an extra byte with 0 terminating the reading;
79+
// now we check limits instead.
80+
int storage_size = sizer.consumer()->position();
7281
Array<u1>* const fis = MetadataFactory::new_array<u1>(loader_data, storage_size, CHECK_NULL);
7382

7483
using StreamWriter = UNSIGNED5::Writer<Array<u1>*, int, ArrayHelper<Array<u1>*, int>>;
@@ -79,15 +88,14 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
7988
writer.consumer()->accept_uint(java_fields);
8089
writer.consumer()->accept_uint(injected_fields);
8190
for (int i = 0; i < fields->length(); i++) {
82-
FieldInfo* fi = fields->adr_at(i);
83-
writer.map_field_info(*fi);
91+
writer.map_field_info(fields->at(i));
8492
}
8593

8694
#ifdef ASSERT
8795
FieldInfoReader r(fis);
88-
int jfc = r.next_uint();
96+
int jfc, ifc;
97+
r.read_field_counts(&jfc, &ifc);
8998
assert(jfc == java_fields, "Must be");
90-
int ifc = r.next_uint();
9199
assert(ifc == injected_fields, "Must be");
92100
for (int i = 0; i < jfc + ifc; i++) {
93101
FieldInfo fi;
@@ -113,30 +121,221 @@ Array<u1>* FieldInfoStream::create_FieldInfoStream(GrowableArray<FieldInfo>* fie
113121
return fis;
114122
}
115123

124+
int FieldInfoStream::compare_name_and_sig(const Symbol* n1, const Symbol* s1, const Symbol* n2, const Symbol* s2) {
125+
int cmp = n1->fast_compare(n2);
126+
return cmp != 0 ? cmp : s1->fast_compare(s2);
127+
}
128+
129+
130+
// We use both name and signature during the comparison; while JLS require unique
131+
// names for fields, JVMS requires only unique name + signature combination.
132+
struct field_pos {
133+
Symbol* _name;
134+
Symbol* _signature;
135+
int _index;
136+
int _position;
137+
};
138+
139+
class FieldInfoSupplier: public PackedTableBuilder::Supplier {
140+
const field_pos* _positions;
141+
size_t _elements;
142+
143+
public:
144+
FieldInfoSupplier(const field_pos* positions, size_t elements): _positions(positions), _elements(elements) {}
145+
146+
bool next(uint32_t* key, uint32_t* value) override {
147+
if (_elements == 0) {
148+
return false;
149+
}
150+
*key = _positions->_position;
151+
*value = _positions->_index;
152+
++_positions;
153+
--_elements;
154+
return true;
155+
}
156+
};
157+
158+
Array<u1>* FieldInfoStream::create_search_table(ConstantPool* cp, const Array<u1>* fis, ClassLoaderData* loader_data, TRAPS) {
159+
if (CDSConfig::is_dumping_dynamic_archive()) {
160+
// We cannot use search table; in case of dynamic archives it should be sorted by "requested" addresses,
161+
// but Symbol* addresses are coming from _constants, which has "buffered" addresses.
162+
// For background, see new comments inside allocate_node_impl in symbolTable.cpp
163+
return nullptr;
164+
}
165+
166+
FieldInfoReader r(fis);
167+
int java_fields;
168+
int injected_fields;
169+
r.read_field_counts(&java_fields, &injected_fields);
170+
assert(java_fields >= 0, "must be");
171+
if (java_fields == 0 || fis->length() == 0 || static_cast<uint>(java_fields) < BinarySearchThreshold) {
172+
return nullptr;
173+
}
174+
175+
ResourceMark rm;
176+
field_pos* positions = NEW_RESOURCE_ARRAY(field_pos, java_fields);
177+
for (int i = 0; i < java_fields; ++i) {
178+
assert(r.has_next(), "number of fields must match");
179+
180+
positions[i]._position = r.position();
181+
FieldInfo fi;
182+
r.read_field_info(fi);
183+
184+
positions[i]._name = fi.name(cp);
185+
positions[i]._signature = fi.signature(cp);
186+
positions[i]._index = i;
187+
}
188+
auto compare_pair = [](const void* v1, const void* v2) {
189+
const field_pos* p1 = reinterpret_cast<const field_pos*>(v1);
190+
const field_pos* p2 = reinterpret_cast<const field_pos*>(v2);
191+
return compare_name_and_sig(p1->_name, p1->_signature, p2->_name, p2->_signature);
192+
};
193+
qsort(positions, java_fields, sizeof(field_pos), compare_pair);
194+
195+
PackedTableBuilder builder(fis->length() - 1, java_fields - 1);
196+
Array<u1>* table = MetadataFactory::new_array<u1>(loader_data, java_fields * builder.element_bytes(), CHECK_NULL);
197+
FieldInfoSupplier supplier(positions, java_fields);
198+
builder.fill(table->data(), static_cast<size_t>(table->length()), supplier);
199+
return table;
200+
}
201+
116202
GrowableArray<FieldInfo>* FieldInfoStream::create_FieldInfoArray(const Array<u1>* fis, int* java_fields_count, int* injected_fields_count) {
117-
int length = FieldInfoStream::num_total_fields(fis);
118-
GrowableArray<FieldInfo>* array = new GrowableArray<FieldInfo>(length);
119203
FieldInfoReader r(fis);
120-
*java_fields_count = r.next_uint();
121-
*injected_fields_count = r.next_uint();
204+
r.read_field_counts(java_fields_count, injected_fields_count);
205+
int length = *java_fields_count + *injected_fields_count;
206+
207+
GrowableArray<FieldInfo>* array = new GrowableArray<FieldInfo>(length);
122208
while (r.has_next()) {
123209
FieldInfo fi;
124210
r.read_field_info(fi);
125211
array->append(fi);
126212
}
127213
assert(array->length() == length, "Must be");
128-
assert(array->length() == *java_fields_count + *injected_fields_count, "Must be");
129214
return array;
130215
}
131216

132217
void FieldInfoStream::print_from_fieldinfo_stream(Array<u1>* fis, outputStream* os, ConstantPool* cp) {
133-
int length = FieldInfoStream::num_total_fields(fis);
134218
FieldInfoReader r(fis);
135-
int java_field_count = r.next_uint();
136-
int injected_fields_count = r.next_uint();
219+
int java_fields_count;
220+
int injected_fields_count;
221+
r.read_field_counts(&java_fields_count, &injected_fields_count);
137222
while (r.has_next()) {
138223
FieldInfo fi;
139224
r.read_field_info(fi);
140225
fi.print(os, cp);
141226
}
142227
}
228+
229+
class FieldInfoComparator: public PackedTableLookup::Comparator {
230+
const FieldInfoReader* _reader;
231+
ConstantPool* _cp;
232+
const Symbol* _name;
233+
const Symbol* _signature;
234+
235+
public:
236+
FieldInfoComparator(const FieldInfoReader* reader, ConstantPool* cp, const Symbol* name, const Symbol* signature):
237+
_reader(reader), _cp(cp), _name(name), _signature(signature) {}
238+
239+
int compare_to(uint32_t position) override {
240+
FieldInfoReader r2(*_reader);
241+
r2.set_position_and_next_index(position, -1);
242+
u2 name_index, sig_index;
243+
r2.read_name_and_signature(&name_index, &sig_index);
244+
Symbol* mid_name = _cp->symbol_at(name_index);
245+
Symbol* mid_sig = _cp->symbol_at(sig_index);
246+
247+
return FieldInfoStream::compare_name_and_sig(_name, _signature, mid_name, mid_sig);
248+
}
249+
250+
#ifdef ASSERT
251+
void reset(uint32_t position) override {
252+
FieldInfoReader r2(*_reader);
253+
r2.set_position_and_next_index(position, -1);
254+
u2 name_index, signature_index;
255+
r2.read_name_and_signature(&name_index, &signature_index);
256+
_name = _cp->symbol_at(name_index);
257+
_signature = _cp->symbol_at(signature_index);
258+
}
259+
#endif // ASSERT
260+
};
261+
262+
#ifdef ASSERT
263+
void FieldInfoStream::validate_search_table(ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table) {
264+
if (search_table == nullptr) {
265+
return;
266+
}
267+
FieldInfoReader reader(fis);
268+
int java_fields, injected_fields;
269+
reader.read_field_counts(&java_fields, &injected_fields);
270+
assert(java_fields > 0, "must be");
271+
272+
PackedTableLookup lookup(fis->length() - 1, java_fields - 1, search_table);
273+
assert(lookup.element_bytes() * java_fields == static_cast<unsigned int>(search_table->length()), "size does not match");
274+
275+
FieldInfoComparator comparator(&reader, cp, nullptr, nullptr);
276+
// Check 1: assert that elements have the correct order based on the comparison function
277+
lookup.validate_order(comparator);
278+
279+
// Check 2: Iterate through the original stream (not just search_table) and try if lookup works as expected
280+
reader.set_position_and_next_index(0, 0);
281+
reader.read_field_counts(&java_fields, &injected_fields);
282+
while (reader.has_next()) {
283+
int field_start = reader.position();
284+
FieldInfo fi;
285+
reader.read_field_info(fi);
286+
if (fi.field_flags().is_injected()) {
287+
// checking only java fields that precede injected ones
288+
break;
289+
}
290+
291+
FieldInfoReader r2(fis);
292+
int index = r2.search_table_lookup(search_table, fi.name(cp), fi.signature(cp), cp, java_fields);
293+
assert(index == static_cast<int>(fi.index()), "wrong index: %d != %u", index, fi.index());
294+
assert(index == r2.next_index(), "index should match");
295+
assert(field_start == r2.position(), "must find the same position");
296+
}
297+
}
298+
#endif // ASSERT
299+
300+
void FieldInfoStream::print_search_table(outputStream* st, ConstantPool* cp, const Array<u1>* fis, const Array<u1>* search_table) {
301+
if (search_table == nullptr) {
302+
return;
303+
}
304+
FieldInfoReader reader(fis);
305+
int java_fields, injected_fields;
306+
reader.read_field_counts(&java_fields, &injected_fields);
307+
assert(java_fields > 0, "must be");
308+
PackedTableLookup lookup(fis->length() - 1, java_fields - 1, search_table);
309+
auto printer = [&] (size_t offset, uint32_t position, uint32_t index) {
310+
reader.set_position_and_next_index(position, -1);
311+
u2 name_index, sig_index;
312+
reader.read_name_and_signature(&name_index, &sig_index);
313+
Symbol* name = cp->symbol_at(name_index);
314+
Symbol* sig = cp->symbol_at(sig_index);
315+
st->print(" [%zu] #%d,#%d = ", offset, name_index, sig_index);
316+
name->print_symbol_on(st);
317+
st->print(":");
318+
sig->print_symbol_on(st);
319+
st->print(" @ %p,%p", name, sig);
320+
st->cr();
321+
};
322+
323+
lookup.iterate(printer);
324+
}
325+
326+
int FieldInfoReader::search_table_lookup(const Array<u1>* search_table, const Symbol* name, const Symbol* signature, ConstantPool* cp, int java_fields) {
327+
assert(java_fields >= 0, "must be");
328+
if (java_fields == 0) {
329+
return -1;
330+
}
331+
FieldInfoComparator comp(this, cp, name, signature);
332+
PackedTableLookup lookup(_r.limit() - 1, java_fields - 1, search_table);
333+
uint32_t position;
334+
static_assert(sizeof(uint32_t) == sizeof(_next_index), "field size assert");
335+
if (lookup.search(comp, &position, reinterpret_cast<uint32_t*>(&_next_index))) {
336+
_r.set_position(static_cast<int>(position));
337+
return _next_index;
338+
} else {
339+
return -1;
340+
}
341+
}

0 commit comments

Comments
 (0)