8184765: Dynamically resize SystemDictionary
authorgziemski
Thu, 02 Nov 2017 11:00:34 -0500
changeset 47774 69c081ca110a
parent 47773 6e3ab27f9144
child 47775 ab33aa41d7a4
8184765: Dynamically resize SystemDictionary Summary: Implemented dynamic resizing, which triggers when load factor is too high Reviewed-by: coleenp, rehn
src/hotspot/share/classfile/classLoaderData.cpp
src/hotspot/share/classfile/classLoaderData.hpp
src/hotspot/share/classfile/dictionary.cpp
src/hotspot/share/classfile/dictionary.hpp
src/hotspot/share/classfile/systemDictionary.cpp
src/hotspot/share/classfile/systemDictionary.hpp
src/hotspot/share/runtime/globals.hpp
src/hotspot/share/runtime/safepoint.cpp
src/hotspot/share/runtime/safepoint.hpp
src/hotspot/share/utilities/hashtable.cpp
src/hotspot/share/utilities/hashtable.hpp
test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java
test/hotspot/jtreg/runtime/LoadClass/TestResize.java
test/hotspot/jtreg/runtime/LoadClass/TriggerResize.java
--- a/src/hotspot/share/classfile/classLoaderData.cpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/classfile/classLoaderData.cpp	Thu Nov 02 11:00:34 2017 -0500
@@ -604,40 +604,27 @@
 
 const int _boot_loader_dictionary_size    = 1009;
 const int _default_loader_dictionary_size = 107;
-const int _prime_array_size         = 8;                       // array of primes for system dictionary size
-const int _average_depth_goal       = 3;                       // goal for lookup length
-const int _primelist[_prime_array_size] = {107, 1009, 2017, 4049, 5051, 10103, 20201, 40423};
-
-// Calculate a "good" dictionary size based
-// on predicted or current loaded classes count.
-static int calculate_dictionary_size(int classcount) {
-  int newsize = _primelist[0];
-  if (classcount > 0 && !DumpSharedSpaces) {
-    int index = 0;
-    int desiredsize = classcount/_average_depth_goal;
-    for (newsize = _primelist[index]; index < _prime_array_size -1;
-         newsize = _primelist[++index]) {
-      if (desiredsize <=  newsize) {
-        break;
-      }
-    }
-  }
-  return newsize;
-}
 
 Dictionary* ClassLoaderData::create_dictionary() {
   assert(!is_anonymous(), "anonymous class loader data do not have a dictionary");
   int size;
+  bool resizable = false;
   if (_the_null_class_loader_data == NULL) {
     size = _boot_loader_dictionary_size;
+    resizable = true;
   } else if (class_loader()->is_a(SystemDictionary::reflect_DelegatingClassLoader_klass())) {
     size = 1;  // there's only one class in relection class loader and no initiated classes
   } else if (is_system_class_loader_data()) {
-    size = calculate_dictionary_size(PredictedLoadedClassCount);
+    size = _boot_loader_dictionary_size;
+    resizable = true;
   } else {
     size = _default_loader_dictionary_size;
+    resizable = true;
   }
-  return new Dictionary(this, size);
+  if (!DynamicallyResizeSystemDictionaries || DumpSharedSpaces || UseSharedSpaces) {
+    resizable = false;
+  }
+  return new Dictionary(this, size, resizable);
 }
 
 // Unloading support
@@ -1325,6 +1312,19 @@
   }
 }
 
+int ClassLoaderDataGraph::resize_if_needed() {
+  assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!");
+  int resized = 0;
+  if (Dictionary::does_any_dictionary_needs_resizing()) {
+    FOR_ALL_DICTIONARY(cld) {
+      if (cld->dictionary()->resize_if_needed()) {
+        resized++;
+      }
+    }
+  }
+  return resized;
+}
+
 void ClassLoaderDataGraph::post_class_unload_events() {
 #if INCLUDE_TRACE
   assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!");
--- a/src/hotspot/share/classfile/classLoaderData.hpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/classfile/classLoaderData.hpp	Thu Nov 02 11:00:34 2017 -0500
@@ -143,6 +143,8 @@
     }
   }
 
+  static int resize_if_needed();
+
   static bool has_metaspace_oom()           { return _metaspace_oom; }
   static void set_metaspace_oom(bool value) { _metaspace_oom = value; }
 
--- a/src/hotspot/share/classfile/dictionary.cpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/classfile/dictionary.cpp	Thu Nov 02 11:00:34 2017 -0500
@@ -29,6 +29,7 @@
 #include "classfile/protectionDomainCache.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "classfile/systemDictionaryShared.hpp"
+#include "gc/shared/gcLocker.hpp"
 #include "logging/log.hpp"
 #include "logging/logStream.hpp"
 #include "memory/iterator.hpp"
@@ -39,6 +40,11 @@
 #include "runtime/orderAccess.inline.hpp"
 #include "utilities/hashtable.inline.hpp"
 
+// Optimization: if any dictionary needs resizing, we set this flag,
+// so that we dont't have to walk all dictionaries to check if any actually
+// needs resizing, which is costly to do at Safepoint.
+bool Dictionary::_some_dictionary_needs_resizing = false;
+
 size_t Dictionary::entry_size() {
   if (DumpSharedSpaces) {
     return SystemDictionaryShared::dictionary_entry_size();
@@ -47,15 +53,17 @@
   }
 }
 
-Dictionary::Dictionary(ClassLoaderData* loader_data, int table_size)
-  : _loader_data(loader_data), Hashtable<InstanceKlass*, mtClass>(table_size, (int)entry_size()) {
+Dictionary::Dictionary(ClassLoaderData* loader_data, int table_size, bool resizable)
+  : _loader_data(loader_data), _resizable(resizable), _needs_resizing(false),
+  Hashtable<InstanceKlass*, mtClass>(table_size, (int)entry_size()) {
 };
 
 
 Dictionary::Dictionary(ClassLoaderData* loader_data,
                        int table_size, HashtableBucket<mtClass>* t,
-                       int number_of_entries)
-  : _loader_data(loader_data), Hashtable<InstanceKlass*, mtClass>(table_size, (int)entry_size(), t, number_of_entries) {
+                       int number_of_entries, bool resizable)
+  : _loader_data(loader_data), _resizable(resizable), _needs_resizing(false),
+  Hashtable<InstanceKlass*, mtClass>(table_size, (int)entry_size(), t, number_of_entries) {
 };
 
 Dictionary::~Dictionary() {
@@ -96,6 +104,60 @@
   FREE_C_HEAP_ARRAY(char, entry);
 }
 
+const int _resize_load_trigger = 5;       // load factor that will trigger the resize
+const double _resize_factor    = 2.0;     // by how much we will resize using current number of entries
+const int _resize_max_size     = 40423;   // the max dictionary size allowed
+const int _primelist[] = {107, 1009, 2017, 4049, 5051, 10103, 20201, _resize_max_size};
+const int _prime_array_size = sizeof(_primelist)/sizeof(int);
+
+// Calculate next "good" dictionary size based on requested count
+static int calculate_dictionary_size(int requested) {
+  int newsize = _primelist[0];
+  int index = 0;
+  for (newsize = _primelist[index]; index < (_prime_array_size - 1);
+       newsize = _primelist[++index]) {
+    if (requested <= newsize) {
+      break;
+    }
+  }
+  return newsize;
+}
+
+bool Dictionary::does_any_dictionary_needs_resizing() {
+  return Dictionary::_some_dictionary_needs_resizing;
+}
+
+void Dictionary::check_if_needs_resize() {
+  if (_resizable == true) {
+    if (number_of_entries() > (_resize_load_trigger*table_size())) {
+      _needs_resizing = true;
+      Dictionary::_some_dictionary_needs_resizing = true;
+    }
+  }
+}
+
+bool Dictionary::resize_if_needed() {
+  int desired_size = 0;
+  if (_needs_resizing == true) {
+    desired_size = calculate_dictionary_size((int)(_resize_factor*number_of_entries()));
+    if (desired_size >= _resize_max_size) {
+      desired_size = _resize_max_size;
+      // We have reached the limit, turn resizing off
+      _resizable = false;
+    }
+    if ((desired_size != 0) && (desired_size != table_size())) {
+      if (!resize(desired_size)) {
+        // Something went wrong, turn resizing off
+        _resizable = false;
+      }
+    }
+  }
+
+  _needs_resizing = false;
+  Dictionary::_some_dictionary_needs_resizing = false;
+
+  return (desired_size != 0);
+}
 
 bool DictionaryEntry::contains_protection_domain(oop protection_domain) const {
 #ifdef ASSERT
@@ -264,14 +326,16 @@
 // also cast to volatile;  we do this to ensure store order is maintained
 // by the compilers.
 
-void Dictionary::add_klass(int index, unsigned int hash, Symbol* class_name,
+void Dictionary::add_klass(unsigned int hash, Symbol* class_name,
                            InstanceKlass* obj) {
   assert_locked_or_safepoint(SystemDictionary_lock);
   assert(obj != NULL, "adding NULL obj");
   assert(obj->name() == class_name, "sanity check on name");
 
   DictionaryEntry* entry = new_entry(hash, obj);
+  int index = hash_to_index(hash);
   add_entry(index, entry);
+  check_if_needs_resize();
 }
 
 
@@ -299,8 +363,11 @@
 }
 
 
-InstanceKlass* Dictionary::find(int index, unsigned int hash, Symbol* name,
+InstanceKlass* Dictionary::find(unsigned int hash, Symbol* name,
                                 Handle protection_domain) {
+  NoSafepointVerifier nsv;
+
+  int index = hash_to_index(hash);
   DictionaryEntry* entry = get_entry(index, hash, name);
   if (entry != NULL && entry->is_valid_protection_domain(protection_domain)) {
     return entry->instance_klass();
@@ -350,9 +417,10 @@
 }
 
 
-bool Dictionary::is_valid_protection_domain(int index, unsigned int hash,
+bool Dictionary::is_valid_protection_domain(unsigned int hash,
                                             Symbol* name,
                                             Handle protection_domain) {
+  int index = hash_to_index(hash);
   DictionaryEntry* entry = get_entry(index, hash, name);
   return entry->is_valid_protection_domain(protection_domain);
 }
--- a/src/hotspot/share/classfile/dictionary.hpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/classfile/dictionary.hpp	Thu Nov 02 11:00:34 2017 -0500
@@ -43,6 +43,11 @@
 class Dictionary : public Hashtable<InstanceKlass*, mtClass> {
   friend class VMStructs;
 
+  static bool _some_dictionary_needs_resizing;
+  bool _resizable;
+  bool _needs_resizing;
+  void check_if_needs_resize();
+
   ClassLoaderData* _loader_data;  // backpointer to owning loader
   ClassLoaderData* loader_data() const { return _loader_data; }
 
@@ -51,13 +56,16 @@
 protected:
   static size_t entry_size();
 public:
-  Dictionary(ClassLoaderData* loader_data, int table_size);
-  Dictionary(ClassLoaderData* loader_data, int table_size, HashtableBucket<mtClass>* t, int number_of_entries);
+  Dictionary(ClassLoaderData* loader_data, int table_size, bool resizable = false);
+  Dictionary(ClassLoaderData* loader_data, int table_size, HashtableBucket<mtClass>* t, int number_of_entries, bool resizable = false);
   ~Dictionary();
 
+  static bool does_any_dictionary_needs_resizing();
+  bool resize_if_needed();
+
   DictionaryEntry* new_entry(unsigned int hash, InstanceKlass* klass);
 
-  void add_klass(int index, unsigned int hash, Symbol* class_name, InstanceKlass* obj);
+  void add_klass(unsigned int hash, Symbol* class_name, InstanceKlass* obj);
 
   InstanceKlass* find_class(int index, unsigned int hash, Symbol* name);
 
@@ -79,8 +87,8 @@
   void do_unloading();
 
   // Protection domains
-  InstanceKlass* find(int index, unsigned int hash, Symbol* name, Handle protection_domain);
-  bool is_valid_protection_domain(int index, unsigned int hash,
+  InstanceKlass* find(unsigned int hash, Symbol* name, Handle protection_domain);
+  bool is_valid_protection_domain(unsigned int hash,
                                   Symbol* name,
                                   Handle protection_domain);
   void add_protection_domain(int index, unsigned int hash,
--- a/src/hotspot/share/classfile/systemDictionary.cpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/classfile/systemDictionary.cpp	Thu Nov 02 11:00:34 2017 -0500
@@ -371,7 +371,6 @@
   ClassLoaderData* loader_data = class_loader_data(class_loader);
   Dictionary* dictionary = loader_data->dictionary();
   unsigned int d_hash = dictionary->compute_hash(child_name);
-  int d_index = dictionary->hash_to_index(d_hash);
   unsigned int p_hash = placeholders()->compute_hash(child_name);
   int p_index = placeholders()->hash_to_index(p_hash);
   // can't throw error holding a lock
@@ -379,7 +378,7 @@
   bool throw_circularity_error = false;
   {
     MutexLocker mu(SystemDictionary_lock, THREAD);
-    Klass* childk = find_class(d_index, d_hash, child_name, dictionary);
+    Klass* childk = find_class(d_hash, child_name, dictionary);
     Klass* quicksuperk;
     // to support // loading: if child done loading, just return superclass
     // if class_name, & class_loader don't match:
@@ -487,9 +486,9 @@
 
     Symbol*  kn = klass->name();
     unsigned int d_hash = dictionary->compute_hash(kn);
-    int d_index = dictionary->hash_to_index(d_hash);
 
     MutexLocker mu(SystemDictionary_lock, THREAD);
+    int d_index = dictionary->hash_to_index(d_hash);
     dictionary->add_protection_domain(d_index, d_hash, klass,
                                       protection_domain, THREAD);
   }
@@ -555,7 +554,6 @@
   ClassLoaderData* loader_data = class_loader_data(class_loader);
   Dictionary* dictionary = loader_data->dictionary();
   unsigned int d_hash = dictionary->compute_hash(name);
-  int d_index = dictionary->hash_to_index(d_hash);
   unsigned int p_hash = placeholders()->compute_hash(name);
   int p_index = placeholders()->hash_to_index(p_hash);
 
@@ -579,7 +577,7 @@
  if (!class_loader.is_null() && is_parallelCapable(class_loader)) {
     MutexLocker mu(SystemDictionary_lock, THREAD);
     // Check if classloading completed while we were loading superclass or waiting
-    return find_class(d_index, d_hash, name, dictionary);
+    return find_class(d_hash, name, dictionary);
   }
 
   // must loop to both handle other placeholder updates
@@ -589,7 +587,7 @@
   while (super_load_in_progress) {
     MutexLocker mu(SystemDictionary_lock, THREAD);
     // Check if classloading completed while we were loading superclass or waiting
-    InstanceKlass* check = find_class(d_index, d_hash, name, dictionary);
+    InstanceKlass* check = find_class(d_hash, name, dictionary);
     if (check != NULL) {
       // Klass is already loaded, so just return it
       return check;
@@ -670,6 +668,7 @@
   class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
   ClassLoaderData *loader_data = register_loader(class_loader, CHECK_NULL);
   Dictionary* dictionary = loader_data->dictionary();
+  unsigned int d_hash = dictionary->compute_hash(name);
 
   // Do lookup to see if class already exist and the protection domain
   // has the right access
@@ -677,11 +676,10 @@
   // All subsequent calls use find_class, and set has_loaded_class so that
   // before we return a result we call out to java to check for valid protection domain
   // to allow returning the Klass* and add it to the pd_set if it is valid
-  unsigned int d_hash = dictionary->compute_hash(name);
-  int d_index = dictionary->hash_to_index(d_hash);
-  Klass* probe = dictionary->find(d_index, d_hash, name, protection_domain);
-  if (probe != NULL) return probe;
-
+  {
+    Klass* probe = dictionary->find(d_hash, name, protection_domain);
+    if (probe != NULL) return probe;
+  }
 
   // Non-bootstrap class loaders will call out to class loader and
   // define via jvm/jni_DefineClass which will acquire the
@@ -716,7 +714,7 @@
 
   {
     MutexLocker mu(SystemDictionary_lock, THREAD);
-    InstanceKlass* check = find_class(d_index, d_hash, name, dictionary);
+    InstanceKlass* check = find_class(d_hash, name, dictionary);
     if (check != NULL) {
       // Klass is already loaded, so just return it
       class_has_been_loaded = true;
@@ -800,7 +798,7 @@
                 double_lock_wait(lockObject, THREAD);
               }
               // Check if classloading completed while we were waiting
-              InstanceKlass* check = find_class(d_index, d_hash, name, dictionary);
+              InstanceKlass* check = find_class(d_hash, name, dictionary);
               if (check != NULL) {
                 // Klass is already loaded, so just return it
                 k = check;
@@ -825,7 +823,7 @@
         // i.e. now that we hold the LOAD_INSTANCE token on loading this class/CL
         // one final check if the load has already completed
         // class loaders holding the ObjectLock shouldn't find the class here
-        InstanceKlass* check = find_class(d_index, d_hash, name, dictionary);
+        InstanceKlass* check = find_class(d_hash, name, dictionary);
         if (check != NULL) {
         // Klass is already loaded, so return it after checking/adding protection domain
           k = check;
@@ -858,7 +856,7 @@
         if (k == NULL && HAS_PENDING_EXCEPTION
           && PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) {
           MutexLocker mu(SystemDictionary_lock, THREAD);
-          InstanceKlass* check = find_class(d_index, d_hash, name, dictionary);
+          InstanceKlass* check = find_class(d_hash, name, dictionary);
           if (check != NULL) {
             // Klass is already loaded, so just use it
             k = check;
@@ -873,7 +871,7 @@
       if (!HAS_PENDING_EXCEPTION && k != NULL &&
         k->class_loader() != class_loader()) {
 
-        check_constraints(d_index, d_hash, k, class_loader, false, THREAD);
+        check_constraints(d_hash, k, class_loader, false, THREAD);
 
         // Need to check for a PENDING_EXCEPTION again; check_constraints
         // can throw and doesn't use the CHECK macro.
@@ -881,7 +879,7 @@
           { // Grabbing the Compile_lock prevents systemDictionary updates
             // during compilations.
             MutexLocker mu(Compile_lock, THREAD);
-            update_dictionary(d_index, d_hash, p_index, p_hash,
+            update_dictionary(d_hash, p_index, p_hash,
               k, class_loader, THREAD);
           }
 
@@ -923,7 +921,7 @@
   if (protection_domain() == NULL) return k;
 
   // Check the protection domain has the right access
-  if (dictionary->is_valid_protection_domain(d_index, d_hash, name,
+  if (dictionary->is_valid_protection_domain(d_hash, name,
                                              protection_domain)) {
     return k;
   }
@@ -965,8 +963,7 @@
 
   Dictionary* dictionary = loader_data->dictionary();
   unsigned int d_hash = dictionary->compute_hash(class_name);
-  int d_index = dictionary->hash_to_index(d_hash);
-  return dictionary->find(d_index, d_hash, class_name,
+  return dictionary->find(d_hash, class_name,
                           protection_domain);
 }
 
@@ -1644,8 +1641,7 @@
   Symbol*  name_h = k->name();
   Dictionary* dictionary = loader_data->dictionary();
   unsigned int d_hash = dictionary->compute_hash(name_h);
-  int d_index = dictionary->hash_to_index(d_hash);
-  check_constraints(d_index, d_hash, k, class_loader_h, true, CHECK);
+  check_constraints(d_hash, k, class_loader_h, true, CHECK);
 
   // Register class just loaded with class loader (placed in Vector)
   // Note we do this before updating the dictionary, as this can
@@ -1673,7 +1669,7 @@
 
     // Add to systemDictionary - so other classes can see it.
     // Grabs and releases SystemDictionary_lock
-    update_dictionary(d_index, d_hash, p_index, p_hash,
+    update_dictionary(d_hash, p_index, p_hash,
                       k, class_loader_h, THREAD);
   }
   k->eager_initialize(THREAD);
@@ -1715,7 +1711,6 @@
   Dictionary* dictionary = loader_data->dictionary();
 
   unsigned int d_hash = dictionary->compute_hash(name_h);
-  int d_index = dictionary->hash_to_index(d_hash);
 
   // Hold SD lock around find_class and placeholder creation for DEFINE_CLASS
   unsigned int p_hash = placeholders()->compute_hash(name_h);
@@ -1726,7 +1721,7 @@
     MutexLocker mu(SystemDictionary_lock, THREAD);
     // First check if class already defined
     if (UnsyncloadClass || (is_parallelDefine(class_loader))) {
-      InstanceKlass* check = find_class(d_index, d_hash, name_h, dictionary);
+      InstanceKlass* check = find_class(d_hash, name_h, dictionary);
       if (check != NULL) {
         return check;
       }
@@ -1748,7 +1743,7 @@
         placeholders()->find_and_remove(p_index, p_hash, name_h, loader_data, PlaceholderTable::DEFINE_CLASS, THREAD);
         SystemDictionary_lock->notify_all();
 #ifdef ASSERT
-        InstanceKlass* check = find_class(d_index, d_hash, name_h, dictionary);
+        InstanceKlass* check = find_class(d_hash, name_h, dictionary);
         assert(check != NULL, "definer missed recording success");
 #endif
         return probe->instance_klass();
@@ -1823,10 +1818,11 @@
 // ----------------------------------------------------------------------------
 // Lookup
 
-InstanceKlass* SystemDictionary::find_class(int index, unsigned int hash,
+InstanceKlass* SystemDictionary::find_class(unsigned int hash,
                                             Symbol* class_name,
                                             Dictionary* dictionary) {
   assert_locked_or_safepoint(SystemDictionary_lock);
+  int index = dictionary->hash_to_index(hash);
   return dictionary->find_class(index, hash, class_name);
 }
 
@@ -1856,8 +1852,7 @@
 
   Dictionary* dictionary = loader_data->dictionary();
   unsigned int d_hash = dictionary->compute_hash(class_name);
-  int d_index = dictionary->hash_to_index(d_hash);
-  return find_class(d_index, d_hash, class_name, dictionary);
+  return find_class(d_hash, class_name, dictionary);
 }
 
 
@@ -2210,7 +2205,7 @@
 // if defining is true, then LinkageError if already in dictionary
 // if initiating loader, then ok if InstanceKlass matches existing entry
 
-void SystemDictionary::check_constraints(int d_index, unsigned int d_hash,
+void SystemDictionary::check_constraints(unsigned int d_hash,
                                          InstanceKlass* k,
                                          Handle class_loader, bool defining,
                                          TRAPS) {
@@ -2222,7 +2217,7 @@
 
     MutexLocker mu(SystemDictionary_lock, THREAD);
 
-    InstanceKlass* check = find_class(d_index, d_hash, name, loader_data->dictionary());
+    InstanceKlass* check = find_class(d_hash, name, loader_data->dictionary());
     if (check != NULL) {
       // if different InstanceKlass - duplicate class definition,
       // else - ok, class loaded by a different thread in parallel,
@@ -2270,7 +2265,7 @@
 
 // Update class loader data dictionary - done after check_constraint and add_to_hierachy
 // have been called.
-void SystemDictionary::update_dictionary(int d_index, unsigned int d_hash,
+void SystemDictionary::update_dictionary(unsigned int d_hash,
                                          int p_index, unsigned int p_hash,
                                          InstanceKlass* k,
                                          Handle class_loader,
@@ -2305,13 +2300,13 @@
 
     // Make a new dictionary entry.
     Dictionary* dictionary = loader_data->dictionary();
-    InstanceKlass* sd_check = find_class(d_index, d_hash, name, dictionary);
+    InstanceKlass* sd_check = find_class(d_hash, name, dictionary);
     if (sd_check == NULL) {
-      dictionary->add_klass(d_index, d_hash, name, k);
+      dictionary->add_klass(d_hash, name, k);
       notice_modification();
     }
   #ifdef ASSERT
-    sd_check = find_class(d_index, d_hash, name, dictionary);
+    sd_check = find_class(d_hash, name, dictionary);
     assert (sd_check != NULL, "should have entry in dictionary");
     // Note: there may be a placeholder entry: for circularity testing
     // or for parallel defines
@@ -2388,16 +2383,14 @@
 
   Dictionary* dictionary1 = loader_data1->dictionary();
   unsigned int d_hash1 = dictionary1->compute_hash(constraint_name);
-  int d_index1 = dictionary1->hash_to_index(d_hash1);
 
   Dictionary* dictionary2 = loader_data2->dictionary();
   unsigned int d_hash2 = dictionary2->compute_hash(constraint_name);
-  int d_index2 = dictionary2->hash_to_index(d_hash2);
 
   {
     MutexLocker mu_s(SystemDictionary_lock, THREAD);
-    InstanceKlass* klass1 = find_class(d_index1, d_hash1, constraint_name, dictionary1);
-    InstanceKlass* klass2 = find_class(d_index2, d_hash2, constraint_name, dictionary2);
+    InstanceKlass* klass1 = find_class(d_hash1, constraint_name, dictionary1);
+    InstanceKlass* klass2 = find_class(d_hash2, constraint_name, dictionary2);
     return constraints()->add_entry(constraint_name, klass1, class_loader1,
                                     klass2, class_loader2);
   }
--- a/src/hotspot/share/classfile/systemDictionary.hpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/classfile/systemDictionary.hpp	Thu Nov 02 11:00:34 2017 -0500
@@ -655,11 +655,8 @@
   // Setup link to hierarchy
   static void add_to_hierarchy(InstanceKlass* k, TRAPS);
 
-  // We pass in the hashtable index so we can calculate it outside of
-  // the SystemDictionary_lock.
-
   // Basic find on loaded classes
-  static InstanceKlass* find_class(int index, unsigned int hash,
+  static InstanceKlass* find_class(unsigned int hash,
                                    Symbol* name, Dictionary* dictionary);
   static InstanceKlass* find_class(Symbol* class_name, ClassLoaderData* loader_data);
 
@@ -685,10 +682,10 @@
   static void initialize_preloaded_classes(TRAPS);
 
   // Class loader constraints
-  static void check_constraints(int index, unsigned int hash,
+  static void check_constraints(unsigned int hash,
                                 InstanceKlass* k, Handle loader,
                                 bool defining, TRAPS);
-  static void update_dictionary(int d_index, unsigned int d_hash,
+  static void update_dictionary(unsigned int d_hash,
                                 int p_index, unsigned int p_hash,
                                 InstanceKlass* k, Handle loader,
                                 TRAPS);
--- a/src/hotspot/share/runtime/globals.hpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/runtime/globals.hpp	Thu Nov 02 11:00:34 2017 -0500
@@ -1144,8 +1144,8 @@
   notproduct(bool, PrintSystemDictionaryAtExit, false,                      \
           "Print the system dictionary at exit")                            \
                                                                             \
-  experimental(intx, PredictedLoadedClassCount, 0,                          \
-          "Experimental: Tune loaded class cache starting size")            \
+  diagnostic(bool, DynamicallyResizeSystemDictionaries, true,               \
+          "Dynamically resize system dictionaries as needed")               \
                                                                             \
   diagnostic(bool, UnsyncloadClass, false,                                  \
           "Unstable: VM calls loadClass unsynchronized. Custom "            \
--- a/src/hotspot/share/runtime/safepoint.cpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/runtime/safepoint.cpp	Thu Nov 02 11:00:34 2017 -0500
@@ -23,6 +23,7 @@
  */
 
 #include "precompiled.hpp"
+#include "classfile/classLoaderData.hpp"
 #include "classfile/stringTable.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
@@ -618,6 +619,14 @@
       ClassLoaderDataGraph::purge_if_needed();
       event_safepoint_cleanup_task_commit(event, name);
     }
+
+    if (!_subtasks.is_task_claimed(SafepointSynchronize::SAFEPOINT_CLEANUP_SYSTEM_DICTIONARY_RESIZE)) {
+      const char* name = "resizing system dictionaries";
+      EventSafepointCleanupTask event;
+      TraceTime timer(name, TRACETIME_LOG(Info, safepoint, cleanup));
+      ClassLoaderDataGraph::resize_if_needed();
+      event_safepoint_cleanup_task_commit(event, name);
+    }
     _subtasks.all_tasks_completed(_num_workers);
   }
 };
--- a/src/hotspot/share/runtime/safepoint.hpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/runtime/safepoint.hpp	Thu Nov 02 11:00:34 2017 -0500
@@ -83,6 +83,7 @@
     SAFEPOINT_CLEANUP_SYMBOL_TABLE_REHASH,
     SAFEPOINT_CLEANUP_STRING_TABLE_REHASH,
     SAFEPOINT_CLEANUP_CLD_PURGE,
+    SAFEPOINT_CLEANUP_SYSTEM_DICTIONARY_RESIZE,
     // Leave this one last.
     SAFEPOINT_CLEANUP_NUM_TASKS
   };
--- a/src/hotspot/share/utilities/hashtable.cpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/utilities/hashtable.cpp	Thu Nov 02 11:00:34 2017 -0500
@@ -264,6 +264,49 @@
   }
 }
 
+template <MEMFLAGS F> bool BasicHashtable<F>::resize(int new_size) {
+  assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
+
+  // Allocate new buckets
+  HashtableBucket<F>* buckets_new = NEW_C_HEAP_ARRAY2_RETURN_NULL(HashtableBucket<F>, new_size, F, CURRENT_PC);
+  if (buckets_new == NULL) {
+    return false;
+  }
+
+  // Clear the new buckets
+  for (int i = 0; i < new_size; i++) {
+    buckets_new[i].clear();
+  }
+
+  int table_size_old = _table_size;
+  // hash_to_index() uses _table_size, so switch the sizes now
+  _table_size = new_size;
+
+  // Move entries from the old table to a new table
+  for (int index_old = 0; index_old < table_size_old; index_old++) {
+    for (BasicHashtableEntry<F>* p = _buckets[index_old].get_entry(); p != NULL; ) {
+      BasicHashtableEntry<F>* next = p->next();
+      bool keep_shared = p->is_shared();
+      int index_new = hash_to_index(p->hash());
+
+      p->set_next(buckets_new[index_new].get_entry());
+      buckets_new[index_new].set_entry(p);
+
+      if (keep_shared) {
+        p->set_shared();
+      }
+      p = next;
+    }
+  }
+
+  // The old backets now can be released
+  BasicHashtable<F>::free_buckets();
+
+  // Switch to the new storage
+  _buckets = buckets_new;
+
+  return true;
+}
 
 // Dump footprint and bucket length statistics
 //
--- a/src/hotspot/share/utilities/hashtable.hpp	Thu Nov 02 18:44:44 2017 +0300
+++ b/src/hotspot/share/utilities/hashtable.hpp	Thu Nov 02 11:00:34 2017 -0500
@@ -237,6 +237,8 @@
 
   int number_of_entries() const { return _number_of_entries; }
 
+  bool resize(int new_size);
+
   template <class T> void verify_table(const char* table_name) PRODUCT_RETURN;
 };
 
@@ -281,7 +283,6 @@
   HashtableEntry<T, F>** bucket_addr(int i) {
     return (HashtableEntry<T, F>**)BasicHashtable<F>::bucket_addr(i);
   }
-
 };
 
 template <class T, MEMFLAGS F> class RehashableHashtable : public Hashtable<T, F> {
--- a/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java	Thu Nov 02 18:44:44 2017 +0300
+++ b/test/hotspot/jtreg/runtime/CommandLine/VMOptionWarning.java	Thu Nov 02 11:00:34 2017 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -36,9 +36,9 @@
 
 public class VMOptionWarning {
     public static void main(String[] args) throws Exception {
-        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+PredictedLoadedClassCount", "-version");
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+AlwaysSafeConstructors", "-version");
         OutputAnalyzer output = new OutputAnalyzer(pb.start());
-        output.shouldContain("Error: VM option 'PredictedLoadedClassCount' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions.");
+        output.shouldContain("Error: VM option 'AlwaysSafeConstructors' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions.");
 
         if (Platform.isDebugBuild()) {
             System.out.println("Skip the rest of the tests on debug builds since diagnostic, develop, and notproduct options are available on debug builds.");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/LoadClass/TestResize.java	Thu Nov 02 11:00:34 2017 -0500
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+/*
+ * @test
+ * @bug 8184765
+ * @summary make sure the SystemDictionary gets resized when load factor is too high
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ * @compile TriggerResize.java
+ * @run driver TestResize
+ */
+
+import java.lang.ProcessBuilder;
+import java.lang.Process;
+import jdk.test.lib.process.ProcessTools;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.Scanner;
+
+public class TestResize {
+
+  static double MAX_LOAD_FACTOR = 5.0; // see _resize_load_trigger in dictionary.cpp
+
+  static int getInt(String string) {
+    int start = 0;
+    for (int i = 0; i < string.length(); i++) {
+      if (!Character.isDigit(string.charAt(i))) {
+        start++;
+      } else {
+        break;
+      }
+    }
+    int end = start;
+    for (int i = end; i < string.length(); i++) {
+      if (Character.isDigit(string.charAt(i))) {
+        end++;
+      } else {
+        break;
+      }
+    }
+    return Integer.parseInt(string.substring(start, end));
+  }
+
+  static void analyzeOutputOn(ProcessBuilder pb) throws Exception {
+    pb.redirectErrorStream(true);
+    Process process = pb.start();
+    BufferedReader rd = new BufferedReader(new InputStreamReader(process.getInputStream()));
+    String line = rd.readLine();
+    while (line != null) {
+      if (line.startsWith("Java dictionary (")) {
+        // ex. "Java dictionary (table_size=107, classes=6)"
+        // ex. "Java dictionary (table_size=20201, classes=50002)"
+        Scanner scanner = new Scanner(line);
+        scanner.next();
+        scanner.next();
+        int table_size = getInt(scanner.next());
+        int classes = getInt(scanner.next());
+        scanner.close();
+
+        double loadFactor = (double)classes / (double)table_size;
+        if (loadFactor > MAX_LOAD_FACTOR) {
+          throw new RuntimeException("Load factor too high, expected MAX "+MAX_LOAD_FACTOR+", got "+loadFactor);
+        } else {
+          System.out.println("PASS table_size:"+table_size+", classes:"+classes+" OK");
+        }
+      }
+      line = rd.readLine();
+    }
+    int retval = process.waitFor();
+    if (retval != 0) {
+      throw new RuntimeException("Error: test returned non-zero value");
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+    ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+PrintSystemDictionaryAtExit",
+                                                              "TriggerResize",
+                                                              "50000");
+    analyzeOutputOn(pb);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/LoadClass/TriggerResize.java	Thu Nov 02 11:00:34 2017 -0500
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+
+import java.lang.ClassLoader;
+
+public class TriggerResize extends ClassLoader
+{
+  static private int[] DATA = // bytes for "class TestCase00000 {}"
+  {
+    -54,    -2,   -70,   -66,     0,     0,     0,    52,    0,     13, //   0
+     10,     0,     3,     0,    10,     7,     0,    11,    7,      0, //  10
+     12,     1,     0,     6,    60,   105,   110,   105,  116,     62, //  20
+      1,     0,     3,    40,    41,    86,     1,     0,     4,    67, //  30
+    111,   100,   101,     1,     0,    15,    76,   105,   110,   101, //  40
+     78,   117,   109,    98,   101,   114,    84,    97,    98,   108, //  50
+    101,     1,     0,    10,    83,   111,   117,   114,    99,   101, //  60
+     70,   105,   108,   101,     1,     0,    18,    84,   101,   115, //  70
+    116,    67,    97,   115,   101,    48,    48,    48,    48,    48, //  80
+     46,   106,    97,   118,    97,    12,     0,     4,     0,     5, //  90
+      1,     0,    13,    84,   101,   115,   116,    67,    97,   115, // 100
+    101,    48,    48,    48,    48,    48,     1,     0,    16,   106, // 110
+     97,   118,    97,    47,   108,    97,   110,   103,    47,    79, // 120
+     98,   106,   101,    99,   116,     0,    32,     0,     2,     0, // 130
+      3,     0,     0,     0,     0,     0,     1,     0,     0,     0, // 140
+      4,     0,     5,     0,     1,     0,     6,     0,     0,     0, // 150
+     29,     0,     1,     0,     1,     0,     0,     0,     5,    42, // 160
+    -73,     0,     1,   -79,     0,     0,     0,     1,     0,     7, // 170
+      0,     0,     0,     6,     0,     1,     0,     0,     0,     1, // 180
+      0,     1,     0,     8,     0,     0,     0,     2,     0,     9  // 190
+  };
+
+  static private int INDEX1 = 85;
+  static private int INDEX2 = 111;
+  static private int BASE = 48;
+
+  public TriggerResize()
+  {
+    super();
+  }
+
+  public void load(int index)
+  {
+    byte[] bytes = new byte[TriggerResize.DATA.length];
+    for (int i=0; i<bytes.length; i++)
+    {
+      bytes[i] = (byte)TriggerResize.DATA[i];
+    }
+
+    // replace id "00000" in TestCase00000 to generate new class on the fly
+    {
+      int byte1 = index % 10;
+      int byte2 = index / 10 % 10;
+      int byte3 = index / 100 % 10;
+      int byte4 = index / 1000 % 10;
+      int byte5 = index / 10000 % 10;
+
+      bytes[INDEX1+0] = bytes[INDEX2+0] = (byte)(BASE+byte5);
+      bytes[INDEX1+1] = bytes[INDEX2+1] = (byte)(BASE+byte4);
+      bytes[INDEX1+2] = bytes[INDEX2+2] = (byte)(BASE+byte3);
+      bytes[INDEX1+3] = bytes[INDEX2+3] = (byte)(BASE+byte2);
+      bytes[INDEX1+4] = bytes[INDEX2+4] = (byte)(BASE+byte1);
+    }
+
+    Class generatedClass = defineClass(bytes, 0, bytes.length);
+    resolveClass(generatedClass);
+  }
+
+  public static void main(String args[]) throws Exception
+  {
+    int count = 0;
+    if (args.length >= 1) {
+      Integer i = new Integer(args[0]);
+      count = i.intValue();
+    }
+
+    TriggerResize test = new TriggerResize();
+    for (int i = 0; i <= count; i++)
+    {
+      test.load(i);
+    }
+
+    // trigger safepoint to resize the SystemDictionary if needed
+    System.gc();
+  }
+}