Skip to content

Commit d5f44dc

Browse files
committed
8319650: Improve heap dump performance with class metadata caching
Reviewed-by: phh Backport-of: 03db82818b905f21cb5ad1d56a687e238b4a6e33
1 parent b9ca253 commit d5f44dc

File tree

1 file changed

+133
-24
lines changed

1 file changed

+133
-24
lines changed

src/hotspot/share/services/heapDumper.cpp

Lines changed: 133 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -883,8 +883,10 @@ class ParDumpWriter : public AbstractDumpWriter {
883883

884884
Monitor* ParDumpWriter::_lock = nullptr;
885885

886-
// Support class with a collection of functions used when dumping the heap
886+
class DumperClassCacheTable;
887+
class DumperClassCacheTableEntry;
887888

889+
// Support class with a collection of functions used when dumping the heap
888890
class DumperSupport : AllStatic {
889891
public:
890892

@@ -899,7 +901,7 @@ class DumperSupport : AllStatic {
899901
static u4 sig2size(Symbol* sig);
900902

901903
// returns the size of the instance of the given class
902-
static u4 instance_size(Klass* k);
904+
static u4 instance_size(InstanceKlass* ik, DumperClassCacheTableEntry* class_cache_entry = nullptr);
903905

904906
// dump a jfloat
905907
static void dump_float(AbstractDumpWriter* writer, jfloat f);
@@ -912,13 +914,13 @@ class DumperSupport : AllStatic {
912914
// dumps static fields of the given class
913915
static void dump_static_fields(AbstractDumpWriter* writer, Klass* k);
914916
// dump the raw values of the instance fields of the given object
915-
static void dump_instance_fields(AbstractDumpWriter* writer, oop o);
917+
static void dump_instance_fields(AbstractDumpWriter* writer, oop o, DumperClassCacheTableEntry* class_cache_entry);
916918
// get the count of the instance fields for a given class
917919
static u2 get_instance_fields_count(InstanceKlass* ik);
918920
// dumps the definition of the instance fields for a given class
919921
static void dump_instance_field_descriptors(AbstractDumpWriter* writer, Klass* k);
920922
// creates HPROF_GC_INSTANCE_DUMP record for the given object
921-
static void dump_instance(AbstractDumpWriter* writer, oop o);
923+
static void dump_instance(AbstractDumpWriter* writer, oop o, DumperClassCacheTable* class_cache);
922924
// creates HPROF_GC_CLASS_DUMP record for the given instance class
923925
static void dump_instance_class(AbstractDumpWriter* writer, Klass* k);
924926
// creates HPROF_GC_CLASS_DUMP record for a given array class
@@ -948,6 +950,106 @@ class DumperSupport : AllStatic {
948950
}
949951
};
950952

953+
// Hash table of klasses to the klass metadata. This should greatly improve the
954+
// hash dumping performance. This hash table is supposed to be used by a single
955+
// thread only.
956+
//
957+
class DumperClassCacheTableEntry : public CHeapObj<mtServiceability> {
958+
friend class DumperClassCacheTable;
959+
private:
960+
GrowableArray<char> _sigs_start;
961+
GrowableArray<int> _offsets;
962+
u4 _instance_size;
963+
int _entries;
964+
965+
public:
966+
DumperClassCacheTableEntry() : _instance_size(0), _entries(0) {};
967+
968+
int field_count() { return _entries; }
969+
char sig_start(int field_idx) { return _sigs_start.at(field_idx); }
970+
int offset(int field_idx) { return _offsets.at(field_idx); }
971+
u4 instance_size() { return _instance_size; }
972+
};
973+
974+
class DumperClassCacheTable {
975+
private:
976+
// ResourceHashtable SIZE is specified at compile time so we
977+
// use 1031 which is the first prime after 1024.
978+
static constexpr size_t TABLE_SIZE = 1031;
979+
980+
// Maintain the cache for N classes. This limits memory footprint
981+
// impact, regardless of how many classes we have in the dump.
982+
// This also improves look up performance by keeping the statically
983+
// sized table from overloading.
984+
static constexpr int CACHE_TOP = 256;
985+
986+
typedef ResourceHashtable<InstanceKlass*, DumperClassCacheTableEntry*,
987+
TABLE_SIZE, AnyObj::C_HEAP, mtServiceability> PtrTable;
988+
PtrTable* _ptrs;
989+
990+
// Single-slot cache to handle the major case of objects of the same
991+
// class back-to-back, e.g. from T[].
992+
InstanceKlass* _last_ik;
993+
DumperClassCacheTableEntry* _last_entry;
994+
995+
void unlink_all(PtrTable* table) {
996+
class CleanupEntry: StackObj {
997+
public:
998+
bool do_entry(InstanceKlass*& key, DumperClassCacheTableEntry*& entry) {
999+
delete entry;
1000+
return true;
1001+
}
1002+
} cleanup;
1003+
table->unlink(&cleanup);
1004+
}
1005+
1006+
public:
1007+
DumperClassCacheTableEntry* lookup_or_create(InstanceKlass* ik) {
1008+
if (_last_ik == ik) {
1009+
return _last_entry;
1010+
}
1011+
1012+
DumperClassCacheTableEntry* entry;
1013+
DumperClassCacheTableEntry** from_cache = _ptrs->get(ik);
1014+
if (from_cache == nullptr) {
1015+
entry = new DumperClassCacheTableEntry();
1016+
for (HierarchicalFieldStream<JavaFieldStream> fld(ik); !fld.done(); fld.next()) {
1017+
if (!fld.access_flags().is_static()) {
1018+
Symbol* sig = fld.signature();
1019+
entry->_sigs_start.push(sig->char_at(0));
1020+
entry->_offsets.push(fld.offset());
1021+
entry->_entries++;
1022+
entry->_instance_size += DumperSupport::sig2size(sig);
1023+
}
1024+
}
1025+
1026+
if (_ptrs->number_of_entries() >= CACHE_TOP) {
1027+
// We do not track the individual hit rates for table entries.
1028+
// Purge the entire table, and let the cache catch up with new
1029+
// distribution.
1030+
unlink_all(_ptrs);
1031+
}
1032+
1033+
_ptrs->put(ik, entry);
1034+
} else {
1035+
entry = *from_cache;
1036+
}
1037+
1038+
// Remember for single-slot cache.
1039+
_last_ik = ik;
1040+
_last_entry = entry;
1041+
1042+
return entry;
1043+
}
1044+
1045+
DumperClassCacheTable() : _ptrs(new (mtServiceability) PtrTable), _last_ik(nullptr), _last_entry(nullptr) {}
1046+
1047+
~DumperClassCacheTable() {
1048+
unlink_all(_ptrs);
1049+
delete _ptrs;
1050+
}
1051+
};
1052+
9511053
// write a header of the given type
9521054
void DumperSupport:: write_header(AbstractDumpWriter* writer, hprofTag tag, u4 len) {
9531055
writer->write_u1(tag);
@@ -1092,16 +1194,18 @@ void DumperSupport::dump_field_value(AbstractDumpWriter* writer, char type, oop
10921194
}
10931195

10941196
// returns the size of the instance of the given class
1095-
u4 DumperSupport::instance_size(Klass* k) {
1096-
InstanceKlass* ik = InstanceKlass::cast(k);
1097-
u4 size = 0;
1098-
1099-
for (HierarchicalFieldStream<JavaFieldStream> fld(ik); !fld.done(); fld.next()) {
1100-
if (!fld.access_flags().is_static()) {
1101-
size += sig2size(fld.signature());
1197+
u4 DumperSupport::instance_size(InstanceKlass* ik, DumperClassCacheTableEntry* class_cache_entry) {
1198+
if (class_cache_entry != nullptr) {
1199+
return class_cache_entry->instance_size();
1200+
} else {
1201+
u4 size = 0;
1202+
for (HierarchicalFieldStream<JavaFieldStream> fld(ik); !fld.done(); fld.next()) {
1203+
if (!fld.access_flags().is_static()) {
1204+
size += sig2size(fld.signature());
1205+
}
11021206
}
1207+
return size;
11031208
}
1104-
return size;
11051209
}
11061210

11071211
u4 DumperSupport::get_static_fields_size(InstanceKlass* ik, u2& field_count) {
@@ -1173,14 +1277,10 @@ void DumperSupport::dump_static_fields(AbstractDumpWriter* writer, Klass* k) {
11731277
}
11741278

11751279
// dump the raw values of the instance fields of the given object
1176-
void DumperSupport::dump_instance_fields(AbstractDumpWriter* writer, oop o) {
1177-
InstanceKlass* ik = InstanceKlass::cast(o->klass());
1178-
1179-
for (HierarchicalFieldStream<JavaFieldStream> fld(ik); !fld.done(); fld.next()) {
1180-
if (!fld.access_flags().is_static()) {
1181-
Symbol* sig = fld.signature();
1182-
dump_field_value(writer, sig->char_at(0), o, fld.offset());
1183-
}
1280+
void DumperSupport::dump_instance_fields(AbstractDumpWriter* writer, oop o, DumperClassCacheTableEntry* class_cache_entry) {
1281+
assert(class_cache_entry != nullptr, "Pre-condition: must be provided");
1282+
for (int idx = 0; idx < class_cache_entry->field_count(); idx++) {
1283+
dump_field_value(writer, class_cache_entry->sig_start(idx), o, class_cache_entry->offset(idx));
11841284
}
11851285
}
11861286

@@ -1211,9 +1311,12 @@ void DumperSupport::dump_instance_field_descriptors(AbstractDumpWriter* writer,
12111311
}
12121312

12131313
// creates HPROF_GC_INSTANCE_DUMP record for the given object
1214-
void DumperSupport::dump_instance(AbstractDumpWriter* writer, oop o) {
1314+
void DumperSupport::dump_instance(AbstractDumpWriter* writer, oop o, DumperClassCacheTable* class_cache) {
12151315
InstanceKlass* ik = InstanceKlass::cast(o->klass());
1216-
u4 is = instance_size(ik);
1316+
1317+
DumperClassCacheTableEntry* cache_entry = class_cache->lookup_or_create(ik);
1318+
1319+
u4 is = instance_size(ik, cache_entry);
12171320
u4 size = 1 + sizeof(address) + 4 + sizeof(address) + 4 + is;
12181321

12191322
writer->start_sub_record(HPROF_GC_INSTANCE_DUMP, size);
@@ -1227,7 +1330,7 @@ void DumperSupport::dump_instance(AbstractDumpWriter* writer, oop o) {
12271330
writer->write_u4(is);
12281331

12291332
// field values
1230-
dump_instance_fields(writer, o);
1333+
dump_instance_fields(writer, o, cache_entry);
12311334

12321335
writer->end_sub_record();
12331336
}
@@ -1690,6 +1793,9 @@ class HeapObjectDumper : public ObjectClosure {
16901793

16911794
AbstractDumpWriter* writer() { return _writer; }
16921795
bool is_large(oop o);
1796+
1797+
DumperClassCacheTable _class_cache;
1798+
16931799
public:
16941800
HeapObjectDumper(AbstractDumpWriter* writer, HeapDumpLargeObjectList* list = nullptr) {
16951801
_writer = writer;
@@ -1722,7 +1828,7 @@ void HeapObjectDumper::do_object(oop o) {
17221828

17231829
if (o->is_instance()) {
17241830
// create a HPROF_GC_INSTANCE record for each object
1725-
DumperSupport::dump_instance(writer(), o);
1831+
DumperSupport::dump_instance(writer(), o, &_class_cache);
17261832
} else if (o->is_objArray()) {
17271833
// create a HPROF_GC_OBJ_ARRAY_DUMP record for each object array
17281834
DumperSupport::dump_object_array(writer(), objArrayOop(o));
@@ -2278,6 +2384,7 @@ void VM_HeapDumper::work(uint worker_id) {
22782384
// The HPROF_GC_CLASS_DUMP and HPROF_GC_INSTANCE_DUMP are the vast bulk
22792385
// of the heap dump.
22802386
if (_num_dumper_threads <= 1) {
2387+
ResourceMark rm;
22812388
HeapObjectDumper obj_dumper(writer());
22822389
Universe::heap()->object_iterate(&obj_dumper);
22832390
} else {
@@ -2294,6 +2401,7 @@ void VM_HeapDumper::work(uint worker_id) {
22942401
{
22952402
ParDumpWriter pw(writer());
22962403
{
2404+
ResourceMark rm;
22972405
HeapObjectDumper obj_dumper(&pw, _large_object_list);
22982406
_poi->object_iterate(&obj_dumper, worker_id);
22992407
}
@@ -2314,6 +2422,7 @@ void VM_HeapDumper::work(uint worker_id) {
23142422

23152423
assert(get_worker_type(worker_id) == VMDumperType, "Heap dumper must be VMDumper");
23162424
// Use writer() rather than ParDumpWriter to avoid memory consumption.
2425+
ResourceMark rm;
23172426
HeapObjectDumper obj_dumper(writer());
23182427
dump_large_objects(&obj_dumper);
23192428
// Writes the HPROF_HEAP_DUMP_END record.

0 commit comments

Comments
 (0)