@@ -883,8 +883,10 @@ class ParDumpWriter : public AbstractDumpWriter {
883
883
884
884
Monitor* ParDumpWriter::_lock = nullptr ;
885
885
886
- // Support class with a collection of functions used when dumping the heap
886
+ class DumperClassCacheTable ;
887
+ class DumperClassCacheTableEntry ;
887
888
889
+ // Support class with a collection of functions used when dumping the heap
888
890
class DumperSupport : AllStatic {
889
891
public:
890
892
@@ -899,7 +901,7 @@ class DumperSupport : AllStatic {
899
901
static u4 sig2size (Symbol* sig);
900
902
901
903
// 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 );
903
905
904
906
// dump a jfloat
905
907
static void dump_float (AbstractDumpWriter* writer, jfloat f);
@@ -912,13 +914,13 @@ class DumperSupport : AllStatic {
912
914
// dumps static fields of the given class
913
915
static void dump_static_fields (AbstractDumpWriter* writer, Klass* k);
914
916
// 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 );
916
918
// get the count of the instance fields for a given class
917
919
static u2 get_instance_fields_count (InstanceKlass* ik);
918
920
// dumps the definition of the instance fields for a given class
919
921
static void dump_instance_field_descriptors (AbstractDumpWriter* writer, Klass* k);
920
922
// 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 );
922
924
// creates HPROF_GC_CLASS_DUMP record for the given instance class
923
925
static void dump_instance_class (AbstractDumpWriter* writer, Klass* k);
924
926
// creates HPROF_GC_CLASS_DUMP record for a given array class
@@ -948,6 +950,106 @@ class DumperSupport : AllStatic {
948
950
}
949
951
};
950
952
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
+
951
1053
// write a header of the given type
952
1054
void DumperSupport:: write_header(AbstractDumpWriter* writer, hprofTag tag, u4 len) {
953
1055
writer->write_u1 (tag);
@@ -1092,16 +1194,18 @@ void DumperSupport::dump_field_value(AbstractDumpWriter* writer, char type, oop
1092
1194
}
1093
1195
1094
1196
// 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
+ }
1102
1206
}
1207
+ return size;
1103
1208
}
1104
- return size;
1105
1209
}
1106
1210
1107
1211
u4 DumperSupport::get_static_fields_size (InstanceKlass* ik, u2& field_count) {
@@ -1173,14 +1277,10 @@ void DumperSupport::dump_static_fields(AbstractDumpWriter* writer, Klass* k) {
1173
1277
}
1174
1278
1175
1279
// 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));
1184
1284
}
1185
1285
}
1186
1286
@@ -1211,9 +1311,12 @@ void DumperSupport::dump_instance_field_descriptors(AbstractDumpWriter* writer,
1211
1311
}
1212
1312
1213
1313
// 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 ) {
1215
1315
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);
1217
1320
u4 size = 1 + sizeof (address) + 4 + sizeof (address) + 4 + is;
1218
1321
1219
1322
writer->start_sub_record (HPROF_GC_INSTANCE_DUMP, size);
@@ -1227,7 +1330,7 @@ void DumperSupport::dump_instance(AbstractDumpWriter* writer, oop o) {
1227
1330
writer->write_u4 (is);
1228
1331
1229
1332
// field values
1230
- dump_instance_fields (writer, o);
1333
+ dump_instance_fields (writer, o, cache_entry );
1231
1334
1232
1335
writer->end_sub_record ();
1233
1336
}
@@ -1690,6 +1793,9 @@ class HeapObjectDumper : public ObjectClosure {
1690
1793
1691
1794
AbstractDumpWriter* writer () { return _writer; }
1692
1795
bool is_large (oop o);
1796
+
1797
+ DumperClassCacheTable _class_cache;
1798
+
1693
1799
public:
1694
1800
HeapObjectDumper (AbstractDumpWriter* writer, HeapDumpLargeObjectList* list = nullptr ) {
1695
1801
_writer = writer;
@@ -1722,7 +1828,7 @@ void HeapObjectDumper::do_object(oop o) {
1722
1828
1723
1829
if (o->is_instance ()) {
1724
1830
// create a HPROF_GC_INSTANCE record for each object
1725
- DumperSupport::dump_instance (writer (), o);
1831
+ DumperSupport::dump_instance (writer (), o, &_class_cache );
1726
1832
} else if (o->is_objArray ()) {
1727
1833
// create a HPROF_GC_OBJ_ARRAY_DUMP record for each object array
1728
1834
DumperSupport::dump_object_array (writer (), objArrayOop (o));
@@ -2278,6 +2384,7 @@ void VM_HeapDumper::work(uint worker_id) {
2278
2384
// The HPROF_GC_CLASS_DUMP and HPROF_GC_INSTANCE_DUMP are the vast bulk
2279
2385
// of the heap dump.
2280
2386
if (_num_dumper_threads <= 1 ) {
2387
+ ResourceMark rm;
2281
2388
HeapObjectDumper obj_dumper (writer ());
2282
2389
Universe::heap ()->object_iterate (&obj_dumper);
2283
2390
} else {
@@ -2294,6 +2401,7 @@ void VM_HeapDumper::work(uint worker_id) {
2294
2401
{
2295
2402
ParDumpWriter pw (writer ());
2296
2403
{
2404
+ ResourceMark rm;
2297
2405
HeapObjectDumper obj_dumper (&pw, _large_object_list);
2298
2406
_poi->object_iterate (&obj_dumper, worker_id);
2299
2407
}
@@ -2314,6 +2422,7 @@ void VM_HeapDumper::work(uint worker_id) {
2314
2422
2315
2423
assert (get_worker_type (worker_id) == VMDumperType, " Heap dumper must be VMDumper" );
2316
2424
// Use writer() rather than ParDumpWriter to avoid memory consumption.
2425
+ ResourceMark rm;
2317
2426
HeapObjectDumper obj_dumper (writer ());
2318
2427
dump_large_objects (&obj_dumper);
2319
2428
// Writes the HPROF_HEAP_DUMP_END record.
0 commit comments