8184765: Dynamically resize SystemDictionary
Summary: Implemented dynamic resizing, which triggers when load factor is too high
Reviewed-by: coleenp, rehn
--- 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();
+ }
+}