8184765: Dynamically resize SystemDictionary

Implemented dynamic resizing, which triggers when load factor is too high

Reviewed-by: coleenp, rehn
This commit is contained in:
Gerard Ziemski 2017-11-02 11:00:34 -05:00
parent e878b3272b
commit a043febf6f
14 changed files with 415 additions and 85 deletions

View File

@ -604,40 +604,27 @@ ModuleEntryTable* ClassLoaderData::modules() {
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 @@ void ClassLoaderDataGraph::purge() {
}
}
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!");

View File

@ -143,6 +143,8 @@ class ClassLoaderDataGraph : public AllStatic {
}
}
static int resize_if_needed();
static bool has_metaspace_oom() { return _metaspace_oom; }
static void set_metaspace_oom(bool value) { _metaspace_oom = value; }

View File

@ -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 @@ size_t Dictionary::entry_size() {
}
}
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 @@ void Dictionary::free_entry(DictionaryEntry* entry) {
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 @@ void Dictionary::classes_do(MetaspaceClosure* it) {
// 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 @@ DictionaryEntry* Dictionary::get_entry(int index, unsigned int hash,
}
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 @@ void Dictionary::add_protection_domain(int index, unsigned int hash,
}
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);
}

View File

@ -43,6 +43,11 @@ class BoolObjectClosure;
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 @@ class Dictionary : public Hashtable<InstanceKlass*, mtClass> {
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 @@ public:
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,

View File

@ -371,7 +371,6 @@ Klass* SystemDictionary::resolve_super_or_fail(Symbol* child_name,
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 @@ Klass* SystemDictionary::resolve_super_or_fail(Symbol* child_name,
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 @@ void SystemDictionary::validate_protection_domain(InstanceKlass* klass,
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 @@ InstanceKlass* SystemDictionary::handle_parallel_super_load(
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 @@ InstanceKlass* SystemDictionary::handle_parallel_super_load(
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 @@ InstanceKlass* SystemDictionary::handle_parallel_super_load(
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 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
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 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
// 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 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
{
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 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
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 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
// 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 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
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 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
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 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
{ // 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 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
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 @@ Klass* SystemDictionary::find(Symbol* class_name,
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 @@ void SystemDictionary::define_instance_class(InstanceKlass* k, TRAPS) {
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 @@ void SystemDictionary::define_instance_class(InstanceKlass* k, TRAPS) {
// 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 @@ InstanceKlass* SystemDictionary::find_or_define_instance_class(Symbol* class_nam
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 @@ InstanceKlass* SystemDictionary::find_or_define_instance_class(Symbol* class_nam
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 @@ InstanceKlass* SystemDictionary::find_or_define_instance_class(Symbol* class_nam
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 @@ void SystemDictionary::check_loader_lock_contention(Handle loader_lock, TRAPS) {
// ----------------------------------------------------------------------------
// 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 @@ InstanceKlass* SystemDictionary::find_class(Symbol* class_name, ClassLoaderData*
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 @@ BasicType SystemDictionary::box_klass_type(Klass* k) {
// 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 @@ void SystemDictionary::check_constraints(int d_index, unsigned int d_hash,
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 @@ void SystemDictionary::check_constraints(int d_index, unsigned int d_hash,
// 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 @@ void SystemDictionary::update_dictionary(int d_index, unsigned int d_hash,
// 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 @@ bool SystemDictionary::add_loader_constraint(Symbol* class_name,
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);
}

View File

@ -655,11 +655,8 @@ protected:
// 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 @@ protected:
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);

View File

@ -1144,8 +1144,8 @@ public:
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 " \

View File

@ -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 @@ public:
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);
}
};

View File

@ -83,6 +83,7 @@ class SafepointSynchronize : AllStatic {
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
};

View File

@ -264,6 +264,49 @@ static int literal_size(oop obj) {
}
}
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
//

View File

@ -237,6 +237,8 @@ public:
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 @@ public:
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> {

View File

@ -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 @@ import jdk.test.lib.Platform;
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.");

View File

@ -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);
}
}

View File

@ -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();
}
}