changeset 52976:7bc8b456e5ac

8210388: Use hash table to store archived subgraph_info records Reviewed-by: jiangli
author iklam
date Mon, 08 Oct 2018 16:29:10 -0700
parents 2a85adf3c330
children 71495d579a65
files src/hotspot/share/classfile/compactHashtable.cpp src/hotspot/share/classfile/compactHashtable.hpp src/hotspot/share/classfile/stringTable.cpp src/hotspot/share/classfile/stringTable.hpp src/hotspot/share/classfile/symbolTable.cpp src/hotspot/share/classfile/symbolTable.hpp src/hotspot/share/memory/heapShared.cpp src/hotspot/share/memory/heapShared.hpp src/hotspot/share/memory/metaspaceShared.cpp
diffstat 9 files changed, 219 insertions(+), 245 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/classfile/compactHashtable.cpp	Mon Oct 08 14:14:52 2018 -0700
+++ b/src/hotspot/share/classfile/compactHashtable.cpp	Mon Oct 08 16:29:10 2018 -0700
@@ -174,7 +174,7 @@
 // The CompactHashtable implementation
 //
 
-void SimpleCompactHashtable::serialize(SerializeClosure* soc) {
+void SimpleCompactHashtable::serialize_header(SerializeClosure* soc) {
   soc->do_ptr((void**)&_base_address);
   soc->do_u4(&_entry_count);
   soc->do_u4(&_bucket_count);
--- a/src/hotspot/share/classfile/compactHashtable.hpp	Mon Oct 08 14:14:52 2018 -0700
+++ b/src/hotspot/share/classfile/compactHashtable.hpp	Mon Oct 08 16:29:10 2018 -0700
@@ -123,6 +123,15 @@
 
 public:
   void dump(SimpleCompactHashtable *cht, const char* table_name);
+
+  static int default_num_buckets(size_t num_entries) {
+    return default_num_buckets((int)num_entries);
+  }
+  static int default_num_buckets(int num_entries) {
+    int num_buckets = num_entries / SharedSymbolTableBucketSize;
+    // calculation of num_buckets can result in zero buckets, we need at least one
+    return (num_buckets < 1) ? 1 : num_buckets;
+  }
 };
 #endif // INCLUDE_CDS
 
@@ -213,8 +222,8 @@
     _entries = entries;
   }
 
-  // For reading from/writing to the CDS archive
-  void serialize(SerializeClosure* soc) NOT_CDS_RETURN;
+  // Read/Write the table's header from/to the CDS archive
+  void serialize_header(SerializeClosure* soc) NOT_CDS_RETURN;
 
   inline bool empty() {
     return (_entry_count == 0);
--- a/src/hotspot/share/classfile/stringTable.cpp	Mon Oct 08 14:14:52 2018 -0700
+++ b/src/hotspot/share/classfile/stringTable.cpp	Mon Oct 08 16:29:10 2018 -0700
@@ -819,18 +819,9 @@
   return new_s;
 }
 
-class CompactStringTableWriter: public CompactHashtableWriter {
-public:
-  CompactStringTableWriter(int num_entries, CompactHashtableStats* stats) :
-    CompactHashtableWriter(num_entries, stats) {}
-  void add(unsigned int hash, oop string) {
-    CompactHashtableWriter::add(hash, CompressedOops::encode(string));
-  }
-};
-
 struct CopyToArchive : StackObj {
-  CompactStringTableWriter* _writer;
-  CopyToArchive(CompactStringTableWriter* writer) : _writer(writer) {}
+  CompactHashtableWriter* _writer;
+  CopyToArchive(CompactHashtableWriter* writer) : _writer(writer) {}
   bool operator()(WeakHandle<vm_string_table_data>* val) {
     oop s = val->peek();
     if (s == NULL) {
@@ -838,6 +829,7 @@
     }
     unsigned int hash = java_lang_String::hash_code(s);
     if (hash == 0) {
+      // We do not archive Strings with a 0 hashcode because ......
       return true;
     }
 
@@ -849,12 +841,12 @@
 
     val->replace(new_s);
     // add to the compact table
-    _writer->add(hash, new_s);
+    _writer->add(hash, CompressedOops::encode(new_s));
     return true;
   }
 };
 
-void StringTable::copy_shared_string_table(CompactStringTableWriter* writer) {
+void StringTable::copy_shared_string_table(CompactHashtableWriter* writer) {
   assert(MetaspaceShared::is_heap_object_archiving_allowed(), "must be");
 
   CopyToArchive copy(writer);
@@ -865,18 +857,18 @@
   assert(MetaspaceShared::is_heap_object_archiving_allowed(), "must be");
 
   _shared_table.reset();
-  int num_buckets = the_table()->_items_count / SharedSymbolTableBucketSize;
-  // calculation of num_buckets can result in zero buckets, we need at least one
-  CompactStringTableWriter writer(num_buckets > 1 ? num_buckets : 1,
-                                  &MetaspaceShared::stats()->string);
+  int num_buckets = CompactHashtableWriter::default_num_buckets(
+      StringTable::the_table()->_items_count);
+  CompactHashtableWriter writer(num_buckets,
+                                &MetaspaceShared::stats()->string);
 
   // Copy the interned strings into the "string space" within the java heap
   copy_shared_string_table(&writer);
   writer.dump(&_shared_table, "string");
 }
 
-void StringTable::serialize(SerializeClosure* soc) {
-  _shared_table.serialize(soc);
+void StringTable::serialize_shared_table_header(SerializeClosure* soc) {
+  _shared_table.serialize_header(soc);
 
   if (soc->writing()) {
     // Sanity. Make sure we don't use the shared table at dump time
--- a/src/hotspot/share/classfile/stringTable.hpp	Mon Oct 08 14:14:52 2018 -0700
+++ b/src/hotspot/share/classfile/stringTable.hpp	Mon Oct 08 16:29:10 2018 -0700
@@ -33,7 +33,7 @@
 #include "oops/weakHandle.hpp"
 #include "utilities/concurrentHashTable.hpp"
 
-class CompactStringTableWriter;
+class CompactHashtableWriter;
 class SerializeClosure;
 
 class StringTable;
@@ -163,14 +163,14 @@
   // Sharing
  private:
   oop lookup_shared(const jchar* name, int len, unsigned int hash) NOT_CDS_JAVA_HEAP_RETURN_(NULL);
-  static void copy_shared_string_table(CompactStringTableWriter* ch_table) NOT_CDS_JAVA_HEAP_RETURN;
+  static void copy_shared_string_table(CompactHashtableWriter* ch_table) NOT_CDS_JAVA_HEAP_RETURN;
  public:
   static oop create_archived_string(oop s, Thread* THREAD) NOT_CDS_JAVA_HEAP_RETURN_(NULL);
   static void set_shared_string_mapped() { _shared_string_mapped = true; }
   static bool shared_string_mapped()     { return _shared_string_mapped; }
   static void shared_oops_do(OopClosure* f) NOT_CDS_JAVA_HEAP_RETURN;
   static void write_to_archive() NOT_CDS_JAVA_HEAP_RETURN;
-  static void serialize(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
+  static void serialize_shared_table_header(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
 
   // Jcmd
   static void dump(outputStream* st, bool verbose=false);
--- a/src/hotspot/share/classfile/symbolTable.cpp	Mon Oct 08 14:14:52 2018 -0700
+++ b/src/hotspot/share/classfile/symbolTable.cpp	Mon Oct 08 16:29:10 2018 -0700
@@ -627,43 +627,31 @@
 }
 
 #if INCLUDE_CDS
-class CompactSymbolTableWriter: public CompactHashtableWriter {
-public:
-  CompactSymbolTableWriter(int num_buckets, CompactHashtableStats* stats) :
-    CompactHashtableWriter(num_buckets, stats) {}
-  void add(unsigned int hash, Symbol *symbol) {
-    uintx deltax = MetaspaceShared::object_delta(symbol);
+struct CopyToArchive : StackObj {
+  CompactHashtableWriter* _writer;
+  CopyToArchive(CompactHashtableWriter* writer) : _writer(writer) {}
+  bool operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    Symbol* sym = *value;
+    unsigned int fixed_hash = hash_shared_symbol((const char*)sym->bytes(), sym->utf8_length());
+    assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length(), false),
+           "must not rehash during dumping");
+
+    uintx deltax = MetaspaceShared::object_delta(sym);
     // When the symbols are stored into the archive, we already check that
     // they won't be more than MAX_SHARED_DELTA from the base address, or
     // else the dumping would have been aborted.
     assert(deltax <= MAX_SHARED_DELTA, "must not be");
     u4 delta = u4(deltax);
 
-    CompactHashtableWriter::add(hash, delta);
-  }
-};
-
-struct CopyToArchive : StackObj {
-  CompactSymbolTableWriter* _writer;
-  CopyToArchive(CompactSymbolTableWriter* writer) : _writer(writer) {}
-  bool operator()(Symbol** value) {
-    assert(value != NULL, "expected valid value");
-    assert(*value != NULL, "value should point to a symbol");
-    Symbol* sym = *value;
-    unsigned int fixed_hash = hash_shared_symbol((const char*)sym->bytes(), sym->utf8_length());
-    if (fixed_hash == 0) {
-      return true;
-    }
-    assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length(), false),
-           "must not rehash during dumping");
-
     // add to the compact table
-    _writer->add(fixed_hash, sym);
+    _writer->add(fixed_hash, delta);
     return true;
   }
 };
 
-void SymbolTable::copy_shared_symbol_table(CompactSymbolTableWriter* writer) {
+void SymbolTable::copy_shared_symbol_table(CompactHashtableWriter* writer) {
   CopyToArchive copy(writer);
   SymbolTable::the_table()->_local_table->do_scan(Thread::current(), copy);
 }
@@ -671,10 +659,10 @@
 void SymbolTable::write_to_archive() {
   _shared_table.reset();
 
-  int num_buckets = (int)(SymbolTable::the_table()->_items_count / SharedSymbolTableBucketSize);
-  // calculation of num_buckets can result in zero buckets, we need at least one
-  CompactSymbolTableWriter writer(num_buckets > 1 ? num_buckets : 1,
-                                  &MetaspaceShared::stats()->symbol);
+  int num_buckets = CompactHashtableWriter::default_num_buckets(
+      SymbolTable::the_table()->_items_count);
+  CompactHashtableWriter writer(num_buckets,
+                                &MetaspaceShared::stats()->symbol);
   copy_shared_symbol_table(&writer);
   writer.dump(&_shared_table, "symbol");
 
@@ -686,8 +674,8 @@
   assert(sym == _shared_table.lookup(name, hash, len), "sanity");
 }
 
-void SymbolTable::serialize(SerializeClosure* soc) {
-  _shared_table.serialize(soc);
+void SymbolTable::serialize_shared_table_header(SerializeClosure* soc) {
+  _shared_table.serialize_header(soc);
 
   if (soc->writing()) {
     // Sanity. Make sure we don't use the shared table at dump time
--- a/src/hotspot/share/classfile/symbolTable.hpp	Mon Oct 08 14:14:52 2018 -0700
+++ b/src/hotspot/share/classfile/symbolTable.hpp	Mon Oct 08 16:29:10 2018 -0700
@@ -84,7 +84,7 @@
   operator Symbol*()                             { return _temp; }
 };
 
-class CompactSymbolTableWriter;
+class CompactHashtableWriter;
 class SerializeClosure;
 
 class SymbolTableConfig;
@@ -240,10 +240,10 @@
 
   // Sharing
 private:
-  static void copy_shared_symbol_table(CompactSymbolTableWriter* ch_table);
+  static void copy_shared_symbol_table(CompactHashtableWriter* ch_table);
 public:
   static void write_to_archive() NOT_CDS_RETURN;
-  static void serialize(SerializeClosure* soc) NOT_CDS_RETURN;
+  static void serialize_shared_table_header(SerializeClosure* soc) NOT_CDS_RETURN;
   static void metaspace_pointers_do(MetaspaceClosure* it);
 
   // Jcmd
--- a/src/hotspot/share/memory/heapShared.cpp	Mon Oct 08 14:14:52 2018 -0700
+++ b/src/hotspot/share/memory/heapShared.cpp	Mon Oct 08 16:29:10 2018 -0700
@@ -41,49 +41,26 @@
 #include "utilities/bitMap.inline.hpp"
 
 #if INCLUDE_CDS_JAVA_HEAP
-KlassSubGraphInfo* HeapShared::_subgraph_info_list = NULL;
-int HeapShared::_num_archived_subgraph_info_records = 0;
-Array<ArchivedKlassSubGraphInfoRecord>* HeapShared::_archived_subgraph_info_records = NULL;
-
-KlassSubGraphInfo* HeapShared::find_subgraph_info(Klass* k) {
-  KlassSubGraphInfo* info = _subgraph_info_list;
-  while (info != NULL) {
-    if (info->klass() == k) {
-      return info;
-    }
-    info = info->next();
-  }
-  return NULL;
-}
+address   HeapShared::_narrow_oop_base;
+int       HeapShared::_narrow_oop_shift;
+HeapShared::DumpTimeKlassSubGraphInfoTable* HeapShared::_dump_time_subgraph_info_table = NULL;
+HeapShared::RunTimeKlassSubGraphInfoTable   HeapShared::_run_time_subgraph_info_table;
 
 // Get the subgraph_info for Klass k. A new subgraph_info is created if
 // there is no existing one for k. The subgraph_info records the relocated
 // Klass* of the original k.
 KlassSubGraphInfo* HeapShared::get_subgraph_info(Klass* k) {
+  assert(DumpSharedSpaces, "dump time only");
   Klass* relocated_k = MetaspaceShared::get_relocated_klass(k);
-  KlassSubGraphInfo* info = find_subgraph_info(relocated_k);
-  if (info != NULL) {
-    return info;
+  KlassSubGraphInfo* info = _dump_time_subgraph_info_table->get(relocated_k);
+  if (info == NULL) {
+    _dump_time_subgraph_info_table->put(relocated_k, KlassSubGraphInfo(relocated_k));
+    info = _dump_time_subgraph_info_table->get(relocated_k);
+    ++ _dump_time_subgraph_info_table->_count;
   }
-
-  info = new KlassSubGraphInfo(relocated_k, _subgraph_info_list);
-  _subgraph_info_list = info;
   return info;
 }
 
-address   HeapShared::_narrow_oop_base;
-int       HeapShared::_narrow_oop_shift;
-
-int HeapShared::num_of_subgraph_infos() {
-  int num = 0;
-  KlassSubGraphInfo* info = _subgraph_info_list;
-  while (info != NULL) {
-    num ++;
-    info = info->next();
-  }
-  return num;
-}
-
 // Add an entry field to the current KlassSubGraphInfo.
 void KlassSubGraphInfo::add_subgraph_entry_field(int static_field_offset, oop v) {
   assert(DumpSharedSpaces, "dump time only");
@@ -156,7 +133,6 @@
 // Initialize an archived subgraph_info_record from the given KlassSubGraphInfo.
 void ArchivedKlassSubGraphInfoRecord::init(KlassSubGraphInfo* info) {
   _k = info->klass();
-  _next = NULL;
   _entry_field_records = NULL;
   _subgraph_object_klasses = NULL;
 
@@ -191,6 +167,26 @@
   }
 }
 
+struct CopyKlassSubGraphInfoToArchive : StackObj {
+  CompactHashtableWriter* _writer;
+  CopyKlassSubGraphInfoToArchive(CompactHashtableWriter* writer) : _writer(writer) {}
+
+  bool do_entry(Klass* klass, KlassSubGraphInfo& info) {
+    if (info.subgraph_object_klasses() != NULL || info.subgraph_entry_fields() != NULL) {
+      ArchivedKlassSubGraphInfoRecord* record =
+        (ArchivedKlassSubGraphInfoRecord*)MetaspaceShared::read_only_space_alloc(sizeof(ArchivedKlassSubGraphInfoRecord));
+      record->init(&info);
+
+      unsigned int hash = primitive_hash<Klass*>(klass);
+      uintx deltax = MetaspaceShared::object_delta(record);
+      guarantee(deltax <= MAX_SHARED_DELTA, "must not be");
+      u4 delta = u4(deltax);
+      _writer->add(hash, delta);
+    }
+    return true; // keep on iterating
+  }
+};
+
 // Build the records of archived subgraph infos, which include:
 // - Entry points to all subgraphs from the containing class mirror. The entry
 //   points are static fields in the mirror. For each entry point, the field
@@ -198,144 +194,96 @@
 //   back to the corresponding field at runtime.
 // - A list of klasses that need to be loaded/initialized before archived
 //   java object sub-graph can be accessed at runtime.
-//
-// The records are saved in the archive file and reloaded at runtime.
-//
-// Layout of the archived subgraph info records:
-//
-// records_size | num_records | records*
-// ArchivedKlassSubGraphInfoRecord | entry_fields | subgraph_object_klasses
-size_t HeapShared::build_archived_subgraph_info_records(int num_records) {
-  // remember the start address
-  char* start_p = MetaspaceShared::read_only_space_top();
+void HeapShared::write_subgraph_info_table() {
+  // Allocate the contents of the hashtable(s) inside the RO region of the CDS archive.
+  DumpTimeKlassSubGraphInfoTable* d_table = _dump_time_subgraph_info_table;
+  CompactHashtableStats stats;
 
-  // now populate the archived subgraph infos, which will be saved in the
-  // archive file
-  _archived_subgraph_info_records =
-    MetaspaceShared::new_ro_array<ArchivedKlassSubGraphInfoRecord>(num_records);
-  KlassSubGraphInfo* info = _subgraph_info_list;
-  int i = 0;
-  while (info != NULL) {
-    assert(i < _archived_subgraph_info_records->length(), "sanity");
-    ArchivedKlassSubGraphInfoRecord* record =
-      _archived_subgraph_info_records->adr_at(i);
-    record->init(info);
-    info = info->next();
-    i ++;
-  }
+  _run_time_subgraph_info_table.reset();
 
-  // _subgraph_info_list is no longer needed
-  delete _subgraph_info_list;
-  _subgraph_info_list = NULL;
+  int num_buckets = CompactHashtableWriter::default_num_buckets(d_table->_count);
+  CompactHashtableWriter writer(num_buckets, &stats);
+  CopyKlassSubGraphInfoToArchive copy(&writer);
+  _dump_time_subgraph_info_table->iterate(&copy);
 
-  char* end_p = MetaspaceShared::read_only_space_top();
-  size_t records_size = end_p - start_p;
-  return records_size;
+  writer.dump(&_run_time_subgraph_info_table, "subgraphs");
 }
 
-// Write the subgraph info records in the shared _ro region
-void HeapShared::write_archived_subgraph_infos() {
-  assert(DumpSharedSpaces, "dump time only");
-
-  Array<intptr_t>* records_header = MetaspaceShared::new_ro_array<intptr_t>(3);
-
-  _num_archived_subgraph_info_records = num_of_subgraph_infos();
-  size_t records_size = build_archived_subgraph_info_records(
-                             _num_archived_subgraph_info_records);
-
-  // Now write the header information:
-  // records_size, num_records, _archived_subgraph_info_records
-  assert(records_header != NULL, "sanity");
-  intptr_t* p = (intptr_t*)(records_header->data());
-  *p = (intptr_t)records_size;
-  p ++;
-  *p = (intptr_t)_num_archived_subgraph_info_records;
-  p ++;
-  *p = (intptr_t)_archived_subgraph_info_records;
-}
-
-char* HeapShared::read_archived_subgraph_infos(char* buffer) {
-  Array<intptr_t>* records_header = (Array<intptr_t>*)buffer;
-  intptr_t* p = (intptr_t*)(records_header->data());
-  size_t records_size = (size_t)(*p);
-  p ++;
-  _num_archived_subgraph_info_records = *p;
-  p ++;
-  _archived_subgraph_info_records =
-    (Array<ArchivedKlassSubGraphInfoRecord>*)(*p);
-
-  buffer = (char*)_archived_subgraph_info_records + records_size;
-  return buffer;
+void HeapShared::serialize_subgraph_info_table_header(SerializeClosure* soc) {
+  _run_time_subgraph_info_table.serialize_header(soc);
 }
 
 void HeapShared::initialize_from_archived_subgraph(Klass* k) {
   if (!MetaspaceShared::open_archive_heap_region_mapped()) {
     return; // nothing to do
   }
+  assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces");
 
-  if (_num_archived_subgraph_info_records == 0) {
-    return; // no subgraph info records
-  }
+  unsigned int hash = primitive_hash<Klass*>(k);
+  ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0);
 
   // Initialize from archived data. Currently this is done only
   // during VM initialization time. No lock is needed.
-  Thread* THREAD = Thread::current();
-  for (int i = 0; i < _archived_subgraph_info_records->length(); i++) {
-    ArchivedKlassSubGraphInfoRecord* record = _archived_subgraph_info_records->adr_at(i);
-    if (record->klass() == k) {
-      int i;
-      // Found the archived subgraph info record for the requesting klass.
-      // Load/link/initialize the klasses of the objects in the subgraph.
-      // NULL class loader is used.
-      Array<Klass*>* klasses = record->subgraph_object_klasses();
-      if (klasses != NULL) {
-        for (i = 0; i < klasses->length(); i++) {
-          Klass* obj_k = klasses->at(i);
-          Klass* resolved_k = SystemDictionary::resolve_or_null(
-                                                (obj_k)->name(), THREAD);
-          if (resolved_k != obj_k) {
-            return;
-          }
-          if ((obj_k)->is_instance_klass()) {
-            InstanceKlass* ik = InstanceKlass::cast(obj_k);
-            ik->initialize(THREAD);
-          } else if ((obj_k)->is_objArray_klass()) {
-            ObjArrayKlass* oak = ObjArrayKlass::cast(obj_k);
-            oak->initialize(THREAD);
-          }
+  if (record != NULL) {
+    Thread* THREAD = Thread::current();
+    if (log_is_enabled(Info, cds, heap)) {
+      ResourceMark rm;
+      log_info(cds, heap)("initialize_from_archived_subgraph " PTR_FORMAT " %s", p2i(k),
+                          k->external_name());
+    }
+
+    int i;
+    // Load/link/initialize the klasses of the objects in the subgraph.
+    // NULL class loader is used.
+    Array<Klass*>* klasses = record->subgraph_object_klasses();
+    if (klasses != NULL) {
+      for (i = 0; i < klasses->length(); i++) {
+        Klass* obj_k = klasses->at(i);
+        Klass* resolved_k = SystemDictionary::resolve_or_null(
+                                              (obj_k)->name(), THREAD);
+        if (resolved_k != obj_k) {
+          return;
+        }
+        if ((obj_k)->is_instance_klass()) {
+          InstanceKlass* ik = InstanceKlass::cast(obj_k);
+          ik->initialize(THREAD);
+        } else if ((obj_k)->is_objArray_klass()) {
+          ObjArrayKlass* oak = ObjArrayKlass::cast(obj_k);
+          oak->initialize(THREAD);
         }
       }
+    }
 
-      if (HAS_PENDING_EXCEPTION) {
-        CLEAR_PENDING_EXCEPTION;
-        // None of the field value will be set if there was an exception.
-        // The java code will not see any of the archived objects in the
-        // subgraphs referenced from k in this case.
-        return;
+    if (HAS_PENDING_EXCEPTION) {
+      CLEAR_PENDING_EXCEPTION;
+      // None of the field value will be set if there was an exception.
+      // The java code will not see any of the archived objects in the
+      // subgraphs referenced from k in this case.
+      return;
+    }
+
+    // Load the subgraph entry fields from the record and store them back to
+    // the corresponding fields within the mirror.
+    oop m = k->java_mirror();
+    Array<juint>* entry_field_records = record->entry_field_records();
+    if (entry_field_records != NULL) {
+      int efr_len = entry_field_records->length();
+      assert(efr_len % 2 == 0, "sanity");
+      for (i = 0; i < efr_len;) {
+        int field_offset = entry_field_records->at(i);
+        // The object refereced by the field becomes 'known' by GC from this
+        // point. All objects in the subgraph reachable from the object are
+        // also 'known' by GC.
+        oop v = MetaspaceShared::materialize_archived_object(
+            entry_field_records->at(i+1));
+        m->obj_field_put(field_offset, v);
+        i += 2;
+
+        log_debug(cds, heap)("  " PTR_FORMAT " init field @ %2d = " PTR_FORMAT, p2i(k), field_offset, p2i(v));
       }
 
-      // Load the subgraph entry fields from the record and store them back to
-      // the corresponding fields within the mirror.
-      oop m = k->java_mirror();
-      Array<juint>* entry_field_records = record->entry_field_records();
-      if (entry_field_records != NULL) {
-        int efr_len = entry_field_records->length();
-        assert(efr_len % 2 == 0, "sanity");
-        for (i = 0; i < efr_len;) {
-          int field_offset = entry_field_records->at(i);
-          // The object refereced by the field becomes 'known' by GC from this
-          // point. All objects in the subgraph reachable from the object are
-          // also 'known' by GC.
-          oop v = MetaspaceShared::materialize_archived_object(
-            entry_field_records->at(i+1));
-          m->obj_field_put(field_offset, v);
-          i += 2;
-        }
-      }
-
-      // Done. Java code can see the archived sub-graphs referenced from k's
-      // mirror after this point.
-      return;
+    // Done. Java code can see the archived sub-graphs referenced from k's
+    // mirror after this point.
     }
   }
 }
@@ -702,6 +650,8 @@
 };
 
 void HeapShared::init_archivable_static_fields(Thread* THREAD) {
+  _dump_time_subgraph_info_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeKlassSubGraphInfoTable();
+
   for (int i = 0; i < num_archivable_static_fields; i++) {
     ArchivableStaticFieldInfo* info = &archivable_static_fields[i];
     TempNewSymbol klass_name =  SymbolTable::new_symbol(info->klass_name, THREAD);
--- a/src/hotspot/share/memory/heapShared.hpp	Mon Oct 08 14:14:52 2018 -0700
+++ b/src/hotspot/share/memory/heapShared.hpp	Mon Oct 08 16:29:10 2018 -0700
@@ -25,6 +25,7 @@
 #ifndef SHARE_VM_MEMORY_HEAPSHARED_HPP
 #define SHARE_VM_MEMORY_HEAPSHARED_HPP
 
+#include "classfile/compactHashtable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "memory/allocation.hpp"
 #include "memory/universe.hpp"
@@ -42,7 +43,6 @@
 // within the sub-graphs.
 class KlassSubGraphInfo: public CHeapObj<mtClass> {
  private:
-  KlassSubGraphInfo* _next;
   // The class that contains the static field(s) as the entry point(s)
   // of archived object sub-graph(s).
   Klass* _k;
@@ -54,8 +54,8 @@
   GrowableArray<juint>*  _subgraph_entry_fields;
 
  public:
-  KlassSubGraphInfo(Klass* k, KlassSubGraphInfo* next) :
-    _next(next), _k(k),  _subgraph_object_klasses(NULL),
+  KlassSubGraphInfo(Klass* k) :
+    _k(k),  _subgraph_object_klasses(NULL),
     _subgraph_entry_fields(NULL) {}
   ~KlassSubGraphInfo() {
     if (_subgraph_object_klasses != NULL) {
@@ -66,7 +66,6 @@
     }
   };
 
-  KlassSubGraphInfo* next() { return _next; }
   Klass* klass()            { return _k; }
   GrowableArray<Klass*>* subgraph_object_klasses() {
     return _subgraph_object_klasses;
@@ -87,7 +86,6 @@
 // at runtime.
 class ArchivedKlassSubGraphInfoRecord {
  private:
-  ArchivedKlassSubGraphInfoRecord* _next;
   Klass* _k;
 
   // contains pairs of field offset and value for each subgraph entry field
@@ -98,11 +96,9 @@
   Array<Klass*>* _subgraph_object_klasses;
  public:
   ArchivedKlassSubGraphInfoRecord() :
-    _next(NULL), _k(NULL), _entry_field_records(NULL), _subgraph_object_klasses(NULL) {}
+    _k(NULL), _entry_field_records(NULL), _subgraph_object_klasses(NULL) {}
   void init(KlassSubGraphInfo* info);
   Klass* klass() { return _k; }
-  ArchivedKlassSubGraphInfoRecord* next() { return _next; }
-  void set_next(ArchivedKlassSubGraphInfoRecord* next) { _next = next; }
   Array<juint>*  entry_field_records() { return _entry_field_records; }
   Array<Klass*>* subgraph_object_klasses() { return _subgraph_object_klasses; }
 };
@@ -112,14 +108,42 @@
   friend class VerifySharedOopClosure;
  private:
 #if INCLUDE_CDS_JAVA_HEAP
-  // This is a list of subgraph infos built at dump time while
-  // archiving object subgraphs.
-  static KlassSubGraphInfo* _subgraph_info_list;
 
-  // Contains a list of ArchivedKlassSubGraphInfoRecords that is stored
-  // in the archive file and reloaded at runtime.
-  static int _num_archived_subgraph_info_records;
-  static Array<ArchivedKlassSubGraphInfoRecord>* _archived_subgraph_info_records;
+  static bool klass_equals(Klass* const& p1, Klass* const& p2) {
+    return primitive_equals<Klass*>(p1, p2);
+  }
+
+  static unsigned klass_hash(Klass* const& klass) {
+    return primitive_hash<address>((address)klass);
+  }
+
+  class DumpTimeKlassSubGraphInfoTable
+    : public ResourceHashtable<Klass*, KlassSubGraphInfo,
+                               HeapShared::klass_hash,
+                               HeapShared::klass_equals,
+                               137, // prime number
+                               ResourceObj::C_HEAP> {
+  public:
+    int _count;
+  };
+
+  inline static ArchivedKlassSubGraphInfoRecord* read_record_from_compact_hashtable(address base_address, u4 offset) {
+    return (ArchivedKlassSubGraphInfoRecord*)(base_address + offset);
+  }
+
+  inline static bool record_equals_compact_hashtable_entry(ArchivedKlassSubGraphInfoRecord* value, const Klass* key, int len_unused) {
+    return (value->klass() == key);
+  }
+
+  typedef CompactHashtable<
+    const Klass*,
+    ArchivedKlassSubGraphInfoRecord*,
+    read_record_from_compact_hashtable,
+    record_equals_compact_hashtable_entry
+    > RunTimeKlassSubGraphInfoTable;
+
+  static DumpTimeKlassSubGraphInfoTable* _dump_time_subgraph_info_table;
+  static RunTimeKlassSubGraphInfoTable _run_time_subgraph_info_table;
 
   // Archive object sub-graph starting from the given static field
   // in Klass k's mirror.
@@ -131,11 +155,10 @@
 
   static void verify_reachable_objects_from(oop obj, bool is_archived) PRODUCT_RETURN;
 
-  static KlassSubGraphInfo* find_subgraph_info(Klass *k);
   static KlassSubGraphInfo* get_subgraph_info(Klass *k);
   static int num_of_subgraph_infos();
 
-  static size_t build_archived_subgraph_info_records(int num_records);
+  static void build_archived_subgraph_info_records(int num_records);
 
   // Used by decode_from_archive
   static address _narrow_oop_base;
@@ -203,6 +226,8 @@
 
   static void init_archivable_static_fields(Thread* THREAD) NOT_CDS_JAVA_HEAP_RETURN;
   static void archive_static_fields(Thread* THREAD) NOT_CDS_JAVA_HEAP_RETURN;
+  static void write_subgraph_info_table() NOT_CDS_JAVA_HEAP_RETURN;
+  static void serialize_subgraph_info_table_header(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
 
 #if INCLUDE_CDS_JAVA_HEAP
   static ResourceBitMap calculate_oopmap(MemRegion region);
--- a/src/hotspot/share/memory/metaspaceShared.cpp	Mon Oct 08 14:14:52 2018 -0700
+++ b/src/hotspot/share/memory/metaspaceShared.cpp	Mon Oct 08 16:29:10 2018 -0700
@@ -420,10 +420,10 @@
   vmSymbols::serialize(soc);
   soc->do_tag(--tag);
 
-  // Dump/restore the symbol and string tables
-  SymbolTable::serialize(soc);
-  StringTable::serialize(soc);
-  soc->do_tag(--tag);
+  // Dump/restore the symbol/string/subgraph_info tables
+  SymbolTable::serialize_shared_table_header(soc);
+  StringTable::serialize_shared_table_header(soc);
+  HeapShared::serialize_subgraph_info_table_header(soc);
 
   JavaClasses::serialize_offsets(soc);
   InstanceMirrorKlass::serialize_offsets(soc);
@@ -1098,6 +1098,21 @@
     return _alloc_stats;
   }
 
+  // Use this when you allocate space with MetaspaceShare::read_only_space_alloc()
+  // outside of ArchiveCompactor::allocate(). These are usually for misc tables
+  // that are allocated in the RO space.
+  class OtherROAllocMark {
+    char* _oldtop;
+  public:
+    OtherROAllocMark() {
+      _oldtop = _ro_region.top();
+    }
+    ~OtherROAllocMark() {
+      char* newtop = _ro_region.top();
+      ArchiveCompactor::alloc_stats()->record_other_type(int(newtop - _oldtop), true);
+    }
+  };
+
   static void allocate(MetaspaceClosure::Ref* ref, bool read_only) {
     address obj = ref->obj();
     int bytes = ref->size() * BytesPerWord;
@@ -1308,7 +1323,7 @@
 }
 
 char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
-  char* oldtop = _ro_region.top();
+  ArchiveCompactor::OtherROAllocMark mark;
   // Reorder the system dictionary. Moving the symbols affects
   // how the hash table indices are calculated.
   SystemDictionary::reorder_dictionary_for_sharing();
@@ -1329,11 +1344,6 @@
   char* table_top = _ro_region.allocate(table_bytes, sizeof(intptr_t));
   SystemDictionary::copy_table(table_top, _ro_region.top());
 
-  // Write the archived object sub-graph infos. For each klass with sub-graphs,
-  // the info includes the static fields (sub-graph entry points) and Klasses
-  // of objects included in the sub-graph.
-  HeapShared::write_archived_subgraph_infos();
-
   // Write the other data to the output array.
   WriteClosure wc(&_ro_region);
   MetaspaceShared::serialize(&wc);
@@ -1341,8 +1351,6 @@
   // Write the bitmaps for patching the archive heap regions
   dump_archive_heap_oopmaps();
 
-  char* newtop = _ro_region.top();
-  ArchiveCompactor::alloc_stats()->record_other_type(int(newtop - oldtop), true);
   return buckets_top;
 }
 
@@ -1822,6 +1830,11 @@
   }
 
   G1HeapVerifier::verify_archive_regions();
+
+  {
+    ArchiveCompactor::OtherROAllocMark mark;
+    HeapShared::write_subgraph_info_table();
+  }
 }
 
 void VM_PopulateDumpSharedSpace::dump_archive_heap_oopmaps() {
@@ -1889,7 +1902,7 @@
 
 unsigned MetaspaceShared::obj_hash(oop const& p) {
   assert(!p->mark()->has_bias_pattern(),
-         "this object should never have been locked");  // so identity_hash won't safepoin
+         "this object should never have been locked");  // so identity_hash won't safepoint
   unsigned hash = (unsigned)p->identity_hash();
   return hash;
 }
@@ -2145,9 +2158,6 @@
   buffer += sizeof(intptr_t);
   buffer += len;
 
-  // The table of archived java heap object sub-graph infos
-  buffer = HeapShared::read_archived_subgraph_infos(buffer);
-
   // Verify various attributes of the archive, plus initialize the
   // shared string/symbol tables
   intptr_t* array = (intptr_t*)buffer;