mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-17 09:34:38 +02:00
8292680: Convert Dictionary to ConcurrentHashTable
Reviewed-by: rehn, hseigel
This commit is contained in:
parent
2fe0ce0148
commit
4f50316a1a
30 changed files with 436 additions and 356 deletions
|
@ -376,7 +376,7 @@ ciInstance* ciEnv::get_or_create_exception(jobject& handle, Symbol* name) {
|
|||
VM_ENTRY_MARK;
|
||||
if (handle == NULL) {
|
||||
// Cf. universe.cpp, creation of Universe::_null_ptr_exception_instance.
|
||||
InstanceKlass* ik = SystemDictionary::find_instance_klass(name, Handle(), Handle());
|
||||
InstanceKlass* ik = SystemDictionary::find_instance_klass(THREAD, name, Handle(), Handle());
|
||||
jobject objh = NULL;
|
||||
if (ik != NULL) {
|
||||
oop obj = ik->allocate_instance(THREAD);
|
||||
|
@ -529,7 +529,7 @@ ciKlass* ciEnv::get_klass_by_name_impl(ciKlass* accessing_klass,
|
|||
if (!require_local) {
|
||||
kls = SystemDictionary::find_constrained_instance_or_array_klass(current, sym, loader);
|
||||
} else {
|
||||
kls = SystemDictionary::find_instance_or_array_klass(sym, loader, domain);
|
||||
kls = SystemDictionary::find_instance_or_array_klass(current, sym, loader, domain);
|
||||
}
|
||||
found_klass = kls;
|
||||
}
|
||||
|
|
|
@ -572,18 +572,6 @@ void ClassLoaderDataGraph::purge(bool at_safepoint) {
|
|||
}
|
||||
}
|
||||
|
||||
int ClassLoaderDataGraph::resize_dictionaries() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!");
|
||||
int resized = 0;
|
||||
assert (Dictionary::does_any_dictionary_needs_resizing(), "some dictionary should need resizing");
|
||||
FOR_ALL_DICTIONARY(cld) {
|
||||
if (cld->dictionary()->resize_if_needed()) {
|
||||
resized++;
|
||||
}
|
||||
}
|
||||
return resized;
|
||||
}
|
||||
|
||||
ClassLoaderDataGraphKlassIteratorAtomic::ClassLoaderDataGraphKlassIteratorAtomic()
|
||||
: _next_klass(NULL) {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint!");
|
||||
|
|
|
@ -104,8 +104,6 @@ class ClassLoaderDataGraph : public AllStatic {
|
|||
static void print_dictionary(outputStream* st);
|
||||
static void print_table_statistics(outputStream* st);
|
||||
|
||||
static int resize_dictionaries();
|
||||
|
||||
static bool has_metaspace_oom() { return _metaspace_oom; }
|
||||
static void set_metaspace_oom(bool value) { _metaspace_oom = value; }
|
||||
|
||||
|
|
|
@ -44,94 +44,69 @@
|
|||
#include "runtime/javaCalls.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/safepointVerifiers.hpp"
|
||||
#include "utilities/concurrentHashTable.inline.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "utilities/hashtable.inline.hpp"
|
||||
#include "utilities/tableStatistics.hpp"
|
||||
|
||||
// Optimization: if any dictionary needs resizing, we set this flag,
|
||||
// so that we don'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;
|
||||
// 2^24 is max size, like StringTable.
|
||||
const size_t END_SIZE = 24;
|
||||
// If a chain gets to 100 something might be wrong
|
||||
const size_t REHASH_LEN = 100;
|
||||
|
||||
Dictionary::Dictionary(ClassLoaderData* loader_data, int table_size, bool resizable)
|
||||
: Hashtable<InstanceKlass*, mtClass>(table_size, (int)sizeof(DictionaryEntry)),
|
||||
_resizable(resizable), _needs_resizing(false), _loader_data(loader_data) {
|
||||
};
|
||||
Dictionary::Dictionary(ClassLoaderData* loader_data, size_t table_size, bool resizable)
|
||||
: _resizable(resizable), _number_of_entries(0), _loader_data(loader_data) {
|
||||
|
||||
|
||||
Dictionary::Dictionary(ClassLoaderData* loader_data,
|
||||
int table_size, HashtableBucket<mtClass>* t,
|
||||
int number_of_entries, bool resizable)
|
||||
: Hashtable<InstanceKlass*, mtClass>(table_size, (int)sizeof(DictionaryEntry), t, number_of_entries),
|
||||
_resizable(resizable), _needs_resizing(false), _loader_data(loader_data) {
|
||||
};
|
||||
size_t start_size_log_2 = MAX2(ceil_log2(table_size), (size_t)2); // 2 is minimum size even though some dictionaries only have one entry
|
||||
size_t current_size = ((size_t)1) << start_size_log_2;
|
||||
log_info(class, loader, data)("Dictionary start size: " SIZE_FORMAT " (" SIZE_FORMAT ")",
|
||||
current_size, start_size_log_2);
|
||||
_table = new ConcurrentTable(start_size_log_2, END_SIZE, REHASH_LEN);
|
||||
}
|
||||
|
||||
Dictionary::~Dictionary() {
|
||||
DictionaryEntry* probe = NULL;
|
||||
for (int index = 0; index < table_size(); index++) {
|
||||
for (DictionaryEntry** p = bucket_addr(index); *p != NULL; ) {
|
||||
probe = *p;
|
||||
*p = probe->next();
|
||||
free_entry(probe);
|
||||
}
|
||||
}
|
||||
assert(number_of_entries() == 0, "should have removed all entries");
|
||||
// This deletes the table and all the nodes, by calling free_node in Config.
|
||||
delete _table;
|
||||
}
|
||||
|
||||
DictionaryEntry* Dictionary::new_entry(unsigned int hash, InstanceKlass* klass) {
|
||||
DictionaryEntry* entry = (DictionaryEntry*)Hashtable<InstanceKlass*, mtClass>::new_entry(hash, klass);
|
||||
entry->release_set_pd_set(NULL);
|
||||
assert(klass->is_instance_klass(), "Must be");
|
||||
return entry;
|
||||
uintx Dictionary::Config::get_hash(Value const& value, bool* is_dead) {
|
||||
return value->instance_klass()->name()->identity_hash();
|
||||
}
|
||||
|
||||
void Dictionary::free_entry(DictionaryEntry* entry) {
|
||||
void* Dictionary::Config::allocate_node(void* context, size_t size, Value const& value) {
|
||||
return AllocateHeap(size, mtClass);
|
||||
}
|
||||
|
||||
void Dictionary::Config::free_node(void* context, void* memory, Value const& value) {
|
||||
delete value; // Call DictionaryEntry destructor
|
||||
FreeHeap(memory);
|
||||
}
|
||||
|
||||
DictionaryEntry::DictionaryEntry(InstanceKlass* klass) : _instance_klass(klass) {
|
||||
release_set_pd_set(nullptr);
|
||||
}
|
||||
|
||||
DictionaryEntry::~DictionaryEntry() {
|
||||
// avoid recursion when deleting linked list
|
||||
// pd_set is accessed during a safepoint.
|
||||
// This doesn't require a lock because nothing is reading this
|
||||
// entry anymore. The ClassLoader is dead.
|
||||
while (entry->pd_set_acquire() != NULL) {
|
||||
ProtectionDomainEntry* to_delete = entry->pd_set_acquire();
|
||||
entry->release_set_pd_set(to_delete->next_acquire());
|
||||
while (pd_set_acquire() != NULL) {
|
||||
ProtectionDomainEntry* to_delete = pd_set_acquire();
|
||||
release_set_pd_set(to_delete->next_acquire());
|
||||
delete to_delete;
|
||||
}
|
||||
BasicHashtable<mtClass>::free_entry(entry);
|
||||
}
|
||||
|
||||
const int _resize_load_trigger = 5; // load factor that will trigger the resize
|
||||
|
||||
bool Dictionary::does_any_dictionary_needs_resizing() {
|
||||
return Dictionary::_some_dictionary_needs_resizing;
|
||||
int Dictionary::table_size() const {
|
||||
return 1 << _table->get_size_log2(Thread::current());
|
||||
}
|
||||
|
||||
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() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
|
||||
int desired_size = 0;
|
||||
if (_needs_resizing == true) {
|
||||
desired_size = calculate_resize(false);
|
||||
assert(desired_size != 0, "bug in calculate_resize");
|
||||
if (desired_size == table_size()) {
|
||||
_resizable = false; // hit max
|
||||
} else {
|
||||
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 Dictionary::check_if_needs_resize() {
|
||||
return (_resizable &&
|
||||
(_number_of_entries > (_resize_load_trigger * table_size())) &&
|
||||
!_table->is_max_size_reached());
|
||||
}
|
||||
|
||||
bool DictionaryEntry::is_valid_protection_domain(Handle protection_domain) {
|
||||
|
@ -213,77 +188,84 @@ void DictionaryEntry::add_protection_domain(ClassLoaderData* loader_data, Handle
|
|||
|
||||
// Just the classes from defining class loaders
|
||||
void Dictionary::classes_do(void f(InstanceKlass*)) {
|
||||
for (int index = 0; index < table_size(); index++) {
|
||||
for (DictionaryEntry* probe = bucket(index);
|
||||
probe != NULL;
|
||||
probe = probe->next()) {
|
||||
InstanceKlass* k = probe->instance_klass();
|
||||
auto doit = [&] (DictionaryEntry** value) {
|
||||
InstanceKlass* k = (*value)->instance_klass();
|
||||
if (loader_data() == k->class_loader_data()) {
|
||||
f(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// Added for initialize_itable_for_klass to handle exceptions
|
||||
// Just the classes from defining class loaders
|
||||
void Dictionary::classes_do(void f(InstanceKlass*, TRAPS), TRAPS) {
|
||||
for (int index = 0; index < table_size(); index++) {
|
||||
for (DictionaryEntry* probe = bucket(index);
|
||||
probe != NULL;
|
||||
probe = probe->next()) {
|
||||
InstanceKlass* k = probe->instance_klass();
|
||||
if (loader_data() == k->class_loader_data()) {
|
||||
f(k, CHECK);
|
||||
}
|
||||
}
|
||||
}
|
||||
_table->do_scan(Thread::current(), doit);
|
||||
}
|
||||
|
||||
// All classes, and their class loaders, including initiating class loaders
|
||||
void Dictionary::all_entries_do(KlassClosure* closure) {
|
||||
for (int index = 0; index < table_size(); index++) {
|
||||
for (DictionaryEntry* probe = bucket(index);
|
||||
probe != NULL;
|
||||
probe = probe->next()) {
|
||||
InstanceKlass* k = probe->instance_klass();
|
||||
auto all_doit = [&] (DictionaryEntry** value) {
|
||||
InstanceKlass* k = (*value)->instance_klass();
|
||||
closure->do_klass(k);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
_table->do_scan(Thread::current(), all_doit);
|
||||
}
|
||||
|
||||
// Used to scan and relocate the classes during CDS archive dump.
|
||||
void Dictionary::classes_do(MetaspaceClosure* it) {
|
||||
Arguments::assert_is_dumping_archive();
|
||||
for (int index = 0; index < table_size(); index++) {
|
||||
for (DictionaryEntry* probe = bucket(index);
|
||||
probe != NULL;
|
||||
probe = probe->next()) {
|
||||
it->push(probe->klass_addr());
|
||||
}
|
||||
}
|
||||
|
||||
auto push = [&] (DictionaryEntry** value) {
|
||||
InstanceKlass** k = (*value)->instance_klass_addr();
|
||||
it->push(k);
|
||||
return true;
|
||||
};
|
||||
_table->do_scan(Thread::current(), push);
|
||||
}
|
||||
|
||||
|
||||
class DictionaryLookup : StackObj {
|
||||
private:
|
||||
Symbol* _name;
|
||||
public:
|
||||
DictionaryLookup(Symbol* name) : _name(name) { }
|
||||
uintx get_hash() const {
|
||||
return _name->identity_hash();
|
||||
}
|
||||
bool equals(DictionaryEntry** value, bool* is_dead) {
|
||||
DictionaryEntry *entry = *value;
|
||||
*is_dead = false;
|
||||
return (entry->instance_klass()->name() == _name);
|
||||
}
|
||||
};
|
||||
|
||||
// Add a loaded class to the dictionary.
|
||||
// Readers of the SystemDictionary aren't always locked, so _buckets
|
||||
// is volatile. The store of the next field in the constructor is
|
||||
// also cast to volatile; we do this to ensure store order is maintained
|
||||
// by the compilers.
|
||||
|
||||
void Dictionary::add_klass(unsigned int hash, Symbol* class_name,
|
||||
void Dictionary::add_klass(JavaThread* current, Symbol* class_name,
|
||||
InstanceKlass* obj) {
|
||||
assert_locked_or_safepoint(SystemDictionary_lock);
|
||||
assert_locked_or_safepoint(SystemDictionary_lock); // doesn't matter now
|
||||
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();
|
||||
}
|
||||
DictionaryEntry* entry = new DictionaryEntry(obj);
|
||||
DictionaryLookup lookup(class_name);
|
||||
bool needs_rehashing, clean_hint;
|
||||
bool created = _table->insert(current, lookup, entry, &needs_rehashing, &clean_hint);
|
||||
assert(created, "should be because we have a lock");
|
||||
assert (!needs_rehashing, "should never need rehashing");
|
||||
assert(!clean_hint, "no class should be unloaded");
|
||||
_number_of_entries++; // still locked
|
||||
// This table can be resized while another thread is reading it.
|
||||
if (check_if_needs_resize()) {
|
||||
_table->grow(current);
|
||||
|
||||
// It would be nice to have a JFR event here, add some logging.
|
||||
LogTarget(Info, class, loader, data) lt;
|
||||
if (lt.is_enabled()) {
|
||||
ResourceMark rm;
|
||||
LogStream ls(<);
|
||||
ls.print("Dictionary resized to %d entries %d for ", table_size(), _number_of_entries);
|
||||
loader_data()->print_value_on(&ls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This routine does not lock the dictionary.
|
||||
//
|
||||
|
@ -293,27 +275,28 @@ void Dictionary::add_klass(unsigned int hash, Symbol* class_name,
|
|||
// be updated in an MT-safe manner).
|
||||
//
|
||||
// Callers should be aware that an entry could be added just after
|
||||
// _buckets[index] is read here, so the caller will not see the new entry.
|
||||
DictionaryEntry* Dictionary::get_entry(int index, unsigned int hash,
|
||||
// the table is read here, so the caller will not see the new entry.
|
||||
// The entry may be accessed by the VM thread in verification.
|
||||
DictionaryEntry* Dictionary::get_entry(Thread* current,
|
||||
Symbol* class_name) {
|
||||
for (DictionaryEntry* entry = bucket(index);
|
||||
entry != NULL;
|
||||
entry = entry->next()) {
|
||||
if (entry->hash() == hash && entry->instance_klass()->name() == class_name) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
DictionaryLookup lookup(class_name);
|
||||
DictionaryEntry* result = nullptr;
|
||||
auto get = [&] (DictionaryEntry** value) {
|
||||
// function called if value is found so is never null
|
||||
result = (*value);
|
||||
};
|
||||
bool needs_rehashing = false;
|
||||
_table->get(current, lookup, get, &needs_rehashing);
|
||||
assert (!needs_rehashing, "should never need rehashing");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
InstanceKlass* Dictionary::find(unsigned int hash, Symbol* name,
|
||||
InstanceKlass* Dictionary::find(Thread* current, Symbol* name,
|
||||
Handle protection_domain) {
|
||||
NoSafepointVerifier nsv;
|
||||
|
||||
int index = hash_to_index(hash);
|
||||
DictionaryEntry* entry = get_entry(index, hash, name);
|
||||
DictionaryEntry* entry = get_entry(current, name);
|
||||
if (entry != NULL && entry->is_valid_protection_domain(protection_domain)) {
|
||||
return entry->instance_klass();
|
||||
} else {
|
||||
|
@ -321,23 +304,19 @@ InstanceKlass* Dictionary::find(unsigned int hash, Symbol* name,
|
|||
}
|
||||
}
|
||||
|
||||
InstanceKlass* Dictionary::find_class(unsigned int hash,
|
||||
InstanceKlass* Dictionary::find_class(Thread* current,
|
||||
Symbol* name) {
|
||||
assert_locked_or_safepoint(SystemDictionary_lock);
|
||||
|
||||
int index = hash_to_index(hash);
|
||||
assert (index == index_for(name), "incorrect index?");
|
||||
|
||||
DictionaryEntry* entry = get_entry(index, hash, name);
|
||||
DictionaryEntry* entry = get_entry(current, name);
|
||||
return (entry != NULL) ? entry->instance_klass() : NULL;
|
||||
}
|
||||
|
||||
void Dictionary::add_protection_domain(int index, unsigned int hash,
|
||||
void Dictionary::add_protection_domain(JavaThread* current,
|
||||
InstanceKlass* klass,
|
||||
Handle protection_domain) {
|
||||
assert(java_lang_System::allow_security_manager(), "only needed if security manager allowed");
|
||||
Symbol* klass_name = klass->name();
|
||||
DictionaryEntry* entry = get_entry(index, hash, klass_name);
|
||||
DictionaryEntry* entry = get_entry(current, klass_name);
|
||||
|
||||
assert(entry != NULL,"entry must be present, we just created it");
|
||||
assert(protection_domain() != NULL,
|
||||
|
@ -354,16 +333,14 @@ void Dictionary::add_protection_domain(int index, unsigned int hash,
|
|||
}
|
||||
|
||||
|
||||
inline bool Dictionary::is_valid_protection_domain(unsigned int hash,
|
||||
inline bool Dictionary::is_valid_protection_domain(JavaThread* current,
|
||||
Symbol* name,
|
||||
Handle protection_domain) {
|
||||
int index = hash_to_index(hash);
|
||||
DictionaryEntry* entry = get_entry(index, hash, name);
|
||||
DictionaryEntry* entry = get_entry(current, name);
|
||||
return entry->is_valid_protection_domain(protection_domain);
|
||||
}
|
||||
|
||||
void Dictionary::validate_protection_domain(unsigned int name_hash,
|
||||
InstanceKlass* klass,
|
||||
void Dictionary::validate_protection_domain(InstanceKlass* klass,
|
||||
Handle class_loader,
|
||||
Handle protection_domain,
|
||||
TRAPS) {
|
||||
|
@ -372,7 +349,7 @@ void Dictionary::validate_protection_domain(unsigned int name_hash,
|
|||
assert(protection_domain() != NULL, "Should not call this");
|
||||
|
||||
if (!java_lang_System::allow_security_manager() ||
|
||||
is_valid_protection_domain(name_hash, klass->name(), protection_domain)) {
|
||||
is_valid_protection_domain(THREAD, klass->name(), protection_domain)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -424,8 +401,7 @@ void Dictionary::validate_protection_domain(unsigned int name_hash,
|
|||
// and protection domain are expected to succeed.
|
||||
{
|
||||
MutexLocker mu(THREAD, SystemDictionary_lock);
|
||||
int d_index = hash_to_index(name_hash);
|
||||
add_protection_domain(d_index, name_hash, klass,
|
||||
add_protection_domain(THREAD, klass,
|
||||
protection_domain);
|
||||
}
|
||||
}
|
||||
|
@ -442,10 +418,8 @@ void Dictionary::clean_cached_protection_domains(GrowableArray<ProtectionDomainE
|
|||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < table_size(); index++) {
|
||||
for (DictionaryEntry* probe = bucket(index);
|
||||
probe != NULL;
|
||||
probe = probe->next()) {
|
||||
auto clean_entries = [&] (DictionaryEntry** value) {
|
||||
DictionaryEntry* probe = *value;
|
||||
Klass* e = probe->instance_klass();
|
||||
|
||||
ProtectionDomainEntry* current = probe->pd_set_acquire();
|
||||
|
@ -458,7 +432,7 @@ void Dictionary::clean_cached_protection_domains(GrowableArray<ProtectionDomainE
|
|||
// Print out trace information
|
||||
LogStream ls(lt);
|
||||
ls.print_cr("PD in set is not alive:");
|
||||
ls.print("class loader: "); loader_data()->class_loader()->print_value_on(&ls);
|
||||
ls.print("class loader: "); _loader_data->class_loader()->print_value_on(&ls);
|
||||
ls.print(" loading: "); probe->instance_klass()->print_value_on(&ls);
|
||||
ls.cr();
|
||||
}
|
||||
|
@ -477,8 +451,10 @@ void Dictionary::clean_cached_protection_domains(GrowableArray<ProtectionDomainE
|
|||
current = current->next_acquire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
_table->do_scan(Thread::current(), clean_entries);
|
||||
}
|
||||
|
||||
void DictionaryEntry::verify_protection_domain_set() {
|
||||
|
@ -505,7 +481,7 @@ void DictionaryEntry::print_count(outputStream *st) {
|
|||
|
||||
void Dictionary::print_size(outputStream* st) const {
|
||||
st->print_cr("Java dictionary (table_size=%d, classes=%d, resizable=%s)",
|
||||
table_size(), number_of_entries(), BOOL_TO_STR(_resizable));
|
||||
table_size(), _number_of_entries, BOOL_TO_STR(_resizable));
|
||||
}
|
||||
|
||||
void Dictionary::print_on(outputStream* st) const {
|
||||
|
@ -516,16 +492,14 @@ void Dictionary::print_on(outputStream* st) const {
|
|||
print_size(st);
|
||||
st->print_cr("^ indicates that initiating loader is different from defining loader");
|
||||
|
||||
for (int index = 0; index < table_size(); index++) {
|
||||
for (DictionaryEntry* probe = bucket(index);
|
||||
probe != NULL;
|
||||
probe = probe->next()) {
|
||||
auto printer = [&] (DictionaryEntry** entry) {
|
||||
DictionaryEntry* probe = *entry;
|
||||
Klass* e = probe->instance_klass();
|
||||
bool is_defining_class =
|
||||
(loader_data() == e->class_loader_data());
|
||||
st->print("%4d: %s%s", index, is_defining_class ? " " : "^", e->external_name());
|
||||
(_loader_data == e->class_loader_data());
|
||||
st->print(" %s%s", is_defining_class ? " " : "^", e->external_name());
|
||||
ClassLoaderData* cld = e->class_loader_data();
|
||||
if (!loader_data()->is_the_null_class_loader_data()) {
|
||||
if (!_loader_data->is_the_null_class_loader_data()) {
|
||||
// Class loader output for the dictionary for the null class loader data is
|
||||
// redundant and obvious.
|
||||
st->print(", ");
|
||||
|
@ -534,7 +508,13 @@ void Dictionary::print_on(outputStream* st) const {
|
|||
probe->print_count(st);
|
||||
}
|
||||
st->cr();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (SafepointSynchronize::is_at_safepoint()) {
|
||||
_table->do_safepoint_scan(printer);
|
||||
} else {
|
||||
_table->do_scan(Thread::current(), printer);
|
||||
}
|
||||
tty->cr();
|
||||
}
|
||||
|
@ -548,7 +528,7 @@ void DictionaryEntry::verify() {
|
|||
}
|
||||
|
||||
void Dictionary::verify() {
|
||||
guarantee(number_of_entries() >= 0, "Verify of dictionary failed");
|
||||
guarantee(_number_of_entries >= 0, "Verify of dictionary failed");
|
||||
|
||||
ClassLoaderData* cld = loader_data();
|
||||
// class loader must be present; a null class loader is the
|
||||
|
@ -557,8 +537,19 @@ void Dictionary::verify() {
|
|||
(cld->is_the_null_class_loader_data() || cld->class_loader_no_keepalive()->is_instance()),
|
||||
"checking type of class_loader");
|
||||
|
||||
ResourceMark rm;
|
||||
stringStream tempst;
|
||||
tempst.print("System Dictionary for %s class loader", cld->loader_name_and_id());
|
||||
verify_table<DictionaryEntry>(tempst.as_string());
|
||||
auto verifier = [&] (DictionaryEntry** val) {
|
||||
(*val)->verify();
|
||||
return true;
|
||||
};
|
||||
|
||||
_table->do_safepoint_scan(verifier);
|
||||
}
|
||||
|
||||
void Dictionary::print_table_statistics(outputStream* st, const char* table_name) {
|
||||
static TableStatistics ts;
|
||||
auto sz = [&] (DictionaryEntry** val) {
|
||||
return sizeof(**val);
|
||||
};
|
||||
ts = _table->statistics_get(Thread::current(), sz, ts);
|
||||
ts.print(st, table_name);
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "oops/instanceKlass.hpp"
|
||||
#include "oops/oop.hpp"
|
||||
#include "oops/oopHandle.hpp"
|
||||
#include "utilities/hashtable.hpp"
|
||||
#include "utilities/concurrentHashTable.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
||||
class DictionaryEntry;
|
||||
|
@ -38,77 +38,68 @@ template <typename T> class GrowableArray;
|
|||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// The data structure for the class loader data dictionaries.
|
||||
|
||||
class Dictionary : public Hashtable<InstanceKlass*, mtClass> {
|
||||
friend class VMStructs;
|
||||
class DictionaryEntry;
|
||||
|
||||
static bool _some_dictionary_needs_resizing;
|
||||
class Dictionary : public CHeapObj<mtClass> {
|
||||
bool _resizable;
|
||||
bool _needs_resizing;
|
||||
void check_if_needs_resize();
|
||||
int _number_of_entries;
|
||||
|
||||
class Config {
|
||||
public:
|
||||
using Value = DictionaryEntry*;
|
||||
static uintx get_hash(Value const& value, bool* is_dead);
|
||||
static void* allocate_node(void* context, size_t size, Value const& value);
|
||||
static void free_node(void* context, void* memory, Value const& value);
|
||||
};
|
||||
|
||||
using ConcurrentTable = ConcurrentHashTable<Config, mtClass>;
|
||||
ConcurrentTable* _table;
|
||||
|
||||
ClassLoaderData* _loader_data; // backpointer to owning loader
|
||||
ClassLoaderData* loader_data() const { return _loader_data; }
|
||||
|
||||
DictionaryEntry* get_entry(int index, unsigned int hash, Symbol* name);
|
||||
DictionaryEntry* get_entry(Thread* current, Symbol* name);
|
||||
bool check_if_needs_resize();
|
||||
int table_size() const;
|
||||
|
||||
public:
|
||||
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(ClassLoaderData* loader_data, size_t table_size, bool resizable = false);
|
||||
~Dictionary();
|
||||
|
||||
static bool does_any_dictionary_needs_resizing();
|
||||
bool resize_if_needed();
|
||||
void add_klass(JavaThread* current, Symbol* class_name, InstanceKlass* obj);
|
||||
|
||||
void add_klass(unsigned int hash, Symbol* class_name, InstanceKlass* obj);
|
||||
|
||||
InstanceKlass* find_class(unsigned int hash, Symbol* name);
|
||||
InstanceKlass* find_class(Thread* current, Symbol* name);
|
||||
|
||||
void classes_do(void f(InstanceKlass*));
|
||||
void classes_do(void f(InstanceKlass*, TRAPS), TRAPS);
|
||||
void all_entries_do(KlassClosure* closure);
|
||||
void classes_do(MetaspaceClosure* it);
|
||||
|
||||
void clean_cached_protection_domains(GrowableArray<ProtectionDomainEntry*>* delete_list);
|
||||
|
||||
// Protection domains
|
||||
InstanceKlass* find(unsigned int hash, Symbol* name, Handle protection_domain);
|
||||
void validate_protection_domain(unsigned int name_hash,
|
||||
InstanceKlass* klass,
|
||||
InstanceKlass* find(Thread* current, Symbol* name, Handle protection_domain);
|
||||
void validate_protection_domain(InstanceKlass* klass,
|
||||
Handle class_loader,
|
||||
Handle protection_domain,
|
||||
TRAPS);
|
||||
|
||||
void print_table_statistics(outputStream* st, const char* table_name);
|
||||
|
||||
void print_on(outputStream* st) const;
|
||||
void print_size(outputStream* st) const;
|
||||
void verify();
|
||||
|
||||
private:
|
||||
DictionaryEntry* new_entry(unsigned int hash, InstanceKlass* klass);
|
||||
|
||||
DictionaryEntry* bucket(int i) const {
|
||||
return (DictionaryEntry*)Hashtable<InstanceKlass*, mtClass>::bucket(i);
|
||||
}
|
||||
|
||||
// The following method is not MT-safe and must be done under lock.
|
||||
DictionaryEntry** bucket_addr(int i) {
|
||||
return (DictionaryEntry**)Hashtable<InstanceKlass*, mtClass>::bucket_addr(i);
|
||||
}
|
||||
|
||||
void free_entry(DictionaryEntry* entry);
|
||||
|
||||
bool is_valid_protection_domain(unsigned int hash,
|
||||
Symbol* name,
|
||||
bool is_valid_protection_domain(JavaThread* current, Symbol* name,
|
||||
Handle protection_domain);
|
||||
void add_protection_domain(int index, unsigned int hash,
|
||||
InstanceKlass* klass,
|
||||
void add_protection_domain(JavaThread* current, InstanceKlass* klass,
|
||||
Handle protection_domain);
|
||||
};
|
||||
|
||||
// An entry in the class loader data dictionaries, this describes a class as
|
||||
// { InstanceKlass*, protection_domain }.
|
||||
// { InstanceKlass*, protection_domain_set }.
|
||||
|
||||
class DictionaryEntry : public HashtableEntry<InstanceKlass*, mtClass> {
|
||||
friend class VMStructs;
|
||||
class DictionaryEntry : public CHeapObj<mtClass> {
|
||||
private:
|
||||
// Contains the set of approved protection domains that can access
|
||||
// this dictionary entry.
|
||||
|
@ -123,24 +114,20 @@ class DictionaryEntry : public HashtableEntry<InstanceKlass*, mtClass> {
|
|||
// It is essentially a cache to avoid repeated Java up-calls to
|
||||
// ClassLoader.checkPackageAccess().
|
||||
//
|
||||
InstanceKlass* _instance_klass;
|
||||
ProtectionDomainEntry* volatile _pd_set;
|
||||
|
||||
public:
|
||||
DictionaryEntry(InstanceKlass* instance_klass);
|
||||
~DictionaryEntry();
|
||||
|
||||
// Tells whether a protection is in the approved set.
|
||||
bool contains_protection_domain(oop protection_domain) const;
|
||||
// Adds a protection domain to the approved set.
|
||||
void add_protection_domain(ClassLoaderData* loader_data, Handle protection_domain);
|
||||
|
||||
InstanceKlass* instance_klass() const { return literal(); }
|
||||
InstanceKlass** klass_addr() { return (InstanceKlass**)literal_addr(); }
|
||||
|
||||
DictionaryEntry* next() const {
|
||||
return (DictionaryEntry*)HashtableEntry<InstanceKlass*, mtClass>::next();
|
||||
}
|
||||
|
||||
DictionaryEntry** next_addr() {
|
||||
return (DictionaryEntry**)HashtableEntry<InstanceKlass*, mtClass>::next_addr();
|
||||
}
|
||||
InstanceKlass* instance_klass() const { return _instance_klass; }
|
||||
InstanceKlass** instance_klass_addr() { return &_instance_klass; }
|
||||
|
||||
ProtectionDomainEntry* pd_set_acquire() const { return Atomic::load_acquire(&_pd_set); }
|
||||
void release_set_pd_set(ProtectionDomainEntry* entry) { Atomic::release_store(&_pd_set, entry); }
|
||||
|
|
|
@ -454,6 +454,7 @@ void LoaderConstraintTable::merge_loader_constraints(Symbol* class_name,
|
|||
}
|
||||
|
||||
void LoaderConstraintTable::verify() {
|
||||
Thread* thread = Thread::current();
|
||||
auto check = [&] (Symbol*& key, ConstraintSet& set) {
|
||||
// foreach constraint in the set, check the klass is in the dictionary or placeholder table.
|
||||
int len = set.num_constraints();
|
||||
|
@ -465,8 +466,7 @@ void LoaderConstraintTable::verify() {
|
|||
Symbol* name = ik->name();
|
||||
ClassLoaderData* loader_data = ik->class_loader_data();
|
||||
Dictionary* dictionary = loader_data->dictionary();
|
||||
unsigned int name_hash = dictionary->compute_hash(name);
|
||||
InstanceKlass* k = dictionary->find_class(name_hash, name);
|
||||
InstanceKlass* k = dictionary->find_class(thread, name);
|
||||
if (k != NULL) {
|
||||
// We found the class in the dictionary, so we should
|
||||
// make sure that the Klass* matches what we already have.
|
||||
|
|
|
@ -212,12 +212,6 @@ class StringTableLookupOop : public StackObj {
|
|||
}
|
||||
};
|
||||
|
||||
static size_t ceil_log2(size_t val) {
|
||||
size_t ret;
|
||||
for (ret = 1; ((size_t)1 << ret) < val; ++ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void StringTable::create_table() {
|
||||
size_t start_size_log_2 = ceil_log2(StringTableSize);
|
||||
_current_size = ((size_t)1) << start_size_log_2;
|
||||
|
|
|
@ -161,12 +161,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
static size_t ceil_log2(size_t value) {
|
||||
size_t ret;
|
||||
for (ret = 1; ((size_t)1 << ret) < value; ++ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SymbolTable::create_table () {
|
||||
size_t start_size_log_2 = ceil_log2(SymbolTableSize);
|
||||
_current_size = ((size_t)1) << start_size_log_2;
|
||||
|
|
|
@ -284,8 +284,7 @@ void verify_dictionary_entry(Symbol* class_name, InstanceKlass* k) {
|
|||
ClassLoaderData* loader_data = k->class_loader_data();
|
||||
Dictionary* dictionary = loader_data->dictionary();
|
||||
assert(class_name == k->name(), "Must be the same");
|
||||
unsigned int name_hash = dictionary->compute_hash(class_name);
|
||||
InstanceKlass* kk = dictionary->find_class(name_hash, class_name);
|
||||
InstanceKlass* kk = dictionary->find_class(JavaThread::current(), class_name);
|
||||
assert(kk == k, "should be present in dictionary");
|
||||
}
|
||||
#endif
|
||||
|
@ -431,13 +430,12 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name,
|
|||
|
||||
ClassLoaderData* loader_data = class_loader_data(class_loader);
|
||||
Dictionary* dictionary = loader_data->dictionary();
|
||||
unsigned int name_hash = dictionary->compute_hash(class_name);
|
||||
|
||||
// can't throw error holding a lock
|
||||
bool throw_circularity_error = false;
|
||||
{
|
||||
MutexLocker mu(THREAD, SystemDictionary_lock);
|
||||
InstanceKlass* klassk = dictionary->find_class(name_hash, class_name);
|
||||
InstanceKlass* klassk = dictionary->find_class(THREAD, class_name);
|
||||
InstanceKlass* quicksuperk;
|
||||
// To support parallel loading: if class is done loading, just return the superclass
|
||||
// if the super_name matches class->super()->name() and if the class loaders match.
|
||||
|
@ -562,7 +560,6 @@ static bool should_wait_for_loading(Handle class_loader) {
|
|||
// For bootstrap and non-parallelCapable class loaders, check and wait for
|
||||
// another thread to complete loading this class.
|
||||
InstanceKlass* SystemDictionary::handle_parallel_loading(JavaThread* current,
|
||||
unsigned int name_hash,
|
||||
Symbol* name,
|
||||
ClassLoaderData* loader_data,
|
||||
Handle lockObject,
|
||||
|
@ -602,7 +599,7 @@ InstanceKlass* SystemDictionary::handle_parallel_loading(JavaThread* current,
|
|||
}
|
||||
|
||||
// Check if classloading completed while we were waiting
|
||||
InstanceKlass* check = loader_data->dictionary()->find_class(name_hash, name);
|
||||
InstanceKlass* check = loader_data->dictionary()->find_class(current, name);
|
||||
if (check != NULL) {
|
||||
// Klass is already loaded, so just return it
|
||||
return check;
|
||||
|
@ -646,14 +643,13 @@ InstanceKlass* 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);
|
||||
Dictionary* dictionary = loader_data->dictionary();
|
||||
unsigned int name_hash = dictionary->compute_hash(name);
|
||||
|
||||
// Do lookup to see if class already exists and the protection domain
|
||||
// has the right access.
|
||||
// This call uses find which checks protection domain already matches
|
||||
// All subsequent calls use find_class, and set loaded_class so that
|
||||
// before we return a result, we call out to java to check for valid protection domain.
|
||||
InstanceKlass* probe = dictionary->find(name_hash, name, protection_domain);
|
||||
InstanceKlass* probe = dictionary->find(THREAD, name, protection_domain);
|
||||
if (probe != NULL) return probe;
|
||||
|
||||
// Non-bootstrap class loaders will call out to class loader and
|
||||
|
@ -680,7 +676,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
|||
// Check again (after locking) if the class already exists in SystemDictionary
|
||||
{
|
||||
MutexLocker mu(THREAD, SystemDictionary_lock);
|
||||
InstanceKlass* check = dictionary->find_class(name_hash, name);
|
||||
InstanceKlass* check = dictionary->find_class(THREAD, name);
|
||||
if (check != NULL) {
|
||||
// InstanceKlass is already loaded, but we still need to check protection domain below.
|
||||
loaded_class = check;
|
||||
|
@ -728,7 +724,6 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
|||
MutexLocker mu(THREAD, SystemDictionary_lock);
|
||||
if (should_wait_for_loading(class_loader)) {
|
||||
loaded_class = handle_parallel_loading(THREAD,
|
||||
name_hash,
|
||||
name,
|
||||
loader_data,
|
||||
lockObject,
|
||||
|
@ -738,7 +733,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
|||
// Recheck if the class has been loaded for all class loader cases and
|
||||
// add a LOAD_INSTANCE placeholder while holding the SystemDictionary_lock.
|
||||
if (!throw_circularity_error && loaded_class == NULL) {
|
||||
InstanceKlass* check = dictionary->find_class(name_hash, name);
|
||||
InstanceKlass* check = dictionary->find_class(THREAD, name);
|
||||
if (check != NULL) {
|
||||
loaded_class = check;
|
||||
} else if (should_wait_for_loading(class_loader)) {
|
||||
|
@ -766,7 +761,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
|||
|
||||
if (loaded_class == NULL) {
|
||||
// Do actual loading
|
||||
loaded_class = load_instance_class(name_hash, name, class_loader, THREAD);
|
||||
loaded_class = load_instance_class(name, class_loader, THREAD);
|
||||
}
|
||||
|
||||
if (load_placeholder_added) {
|
||||
|
@ -793,7 +788,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
|||
// Check if the protection domain is present it has the right access
|
||||
if (protection_domain() != NULL) {
|
||||
// Verify protection domain. If it fails an exception is thrown
|
||||
dictionary->validate_protection_domain(name_hash, loaded_class, class_loader, protection_domain, CHECK_NULL);
|
||||
dictionary->validate_protection_domain(loaded_class, class_loader, protection_domain, CHECK_NULL);
|
||||
}
|
||||
|
||||
return loaded_class;
|
||||
|
@ -808,10 +803,11 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
|
|||
// unloading, when this class loader is no longer referenced.
|
||||
//
|
||||
// Callers should be aware that an entry could be added just after
|
||||
// _dictionary->bucket(index) is read here, so the caller will not see
|
||||
// Dictionary is read here, so the caller will not see
|
||||
// the new entry.
|
||||
|
||||
InstanceKlass* SystemDictionary::find_instance_klass(Symbol* class_name,
|
||||
InstanceKlass* SystemDictionary::find_instance_klass(Thread* current,
|
||||
Symbol* class_name,
|
||||
Handle class_loader,
|
||||
Handle protection_domain) {
|
||||
|
||||
|
@ -828,13 +824,13 @@ InstanceKlass* SystemDictionary::find_instance_klass(Symbol* class_name,
|
|||
}
|
||||
|
||||
Dictionary* dictionary = loader_data->dictionary();
|
||||
unsigned int name_hash = dictionary->compute_hash(class_name);
|
||||
return dictionary->find(name_hash, class_name, protection_domain);
|
||||
return dictionary->find(current, class_name, protection_domain);
|
||||
}
|
||||
|
||||
// Look for a loaded instance or array klass by name. Do not do any loading.
|
||||
// return NULL in case of error.
|
||||
Klass* SystemDictionary::find_instance_or_array_klass(Symbol* class_name,
|
||||
Klass* SystemDictionary::find_instance_or_array_klass(Thread* current,
|
||||
Symbol* class_name,
|
||||
Handle class_loader,
|
||||
Handle protection_domain) {
|
||||
Klass* k = NULL;
|
||||
|
@ -850,13 +846,13 @@ Klass* SystemDictionary::find_instance_or_array_klass(Symbol* class_name,
|
|||
if (t != T_OBJECT) {
|
||||
k = Universe::typeArrayKlassObj(t);
|
||||
} else {
|
||||
k = SystemDictionary::find_instance_klass(ss.as_symbol(), class_loader, protection_domain);
|
||||
k = SystemDictionary::find_instance_klass(current, ss.as_symbol(), class_loader, protection_domain);
|
||||
}
|
||||
if (k != NULL) {
|
||||
k = k->array_klass_or_null(ndims);
|
||||
}
|
||||
} else {
|
||||
k = find_instance_klass(class_name, class_loader, protection_domain);
|
||||
k = find_instance_klass(current, class_name, class_loader, protection_domain);
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
@ -1110,7 +1106,7 @@ bool SystemDictionary::check_shared_class_super_type(InstanceKlass* klass, Insta
|
|||
if (!super_type->is_shared_unregistered_class() && super_type->class_loader_data() != NULL) {
|
||||
// Check if the superclass is loaded by the current class_loader
|
||||
Symbol* name = super_type->name();
|
||||
InstanceKlass* check = find_instance_klass(name, class_loader, protection_domain);
|
||||
InstanceKlass* check = find_instance_klass(THREAD, name, class_loader, protection_domain);
|
||||
if (check == super_type) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1401,8 +1397,7 @@ InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Ha
|
|||
}
|
||||
}
|
||||
|
||||
InstanceKlass* SystemDictionary::load_instance_class(unsigned int name_hash,
|
||||
Symbol* name,
|
||||
InstanceKlass* SystemDictionary::load_instance_class(Symbol* name,
|
||||
Handle class_loader,
|
||||
TRAPS) {
|
||||
|
||||
|
@ -1413,7 +1408,7 @@ InstanceKlass* SystemDictionary::load_instance_class(unsigned int name_hash,
|
|||
if (loaded_class != NULL &&
|
||||
loaded_class->class_loader() != class_loader()) {
|
||||
|
||||
check_constraints(name_hash, loaded_class, class_loader, false, CHECK_NULL);
|
||||
check_constraints(loaded_class, class_loader, false, CHECK_NULL);
|
||||
|
||||
// Record dependency for non-parent delegation.
|
||||
// This recording keeps the defining class loader of the klass (loaded_class) found
|
||||
|
@ -1426,7 +1421,7 @@ InstanceKlass* SystemDictionary::load_instance_class(unsigned int name_hash,
|
|||
{ // Grabbing the Compile_lock prevents systemDictionary updates
|
||||
// during compilations.
|
||||
MutexLocker mu(THREAD, Compile_lock);
|
||||
update_dictionary(name_hash, loaded_class, class_loader);
|
||||
update_dictionary(THREAD, loaded_class, class_loader);
|
||||
}
|
||||
|
||||
if (JvmtiExport::should_post_class_load()) {
|
||||
|
@ -1472,8 +1467,7 @@ void SystemDictionary::define_instance_class(InstanceKlass* k, Handle class_load
|
|||
// which will require a token to perform the define class
|
||||
Symbol* name_h = k->name();
|
||||
Dictionary* dictionary = loader_data->dictionary();
|
||||
unsigned int name_hash = dictionary->compute_hash(name_h);
|
||||
check_constraints(name_hash, k, class_loader, true, CHECK);
|
||||
check_constraints(k, class_loader, true, CHECK);
|
||||
|
||||
// Register class just loaded with class loader (placed in ArrayList)
|
||||
// Note we do this before updating the dictionary, as this can
|
||||
|
@ -1497,7 +1491,7 @@ void SystemDictionary::define_instance_class(InstanceKlass* k, Handle class_load
|
|||
|
||||
// Add to systemDictionary - so other classes can see it.
|
||||
// Grabs and releases SystemDictionary_lock
|
||||
update_dictionary(name_hash, k, class_loader);
|
||||
update_dictionary(THREAD, k, class_loader);
|
||||
}
|
||||
|
||||
// notify jvmti
|
||||
|
@ -1534,14 +1528,12 @@ InstanceKlass* SystemDictionary::find_or_define_helper(Symbol* class_name, Handl
|
|||
ClassLoaderData* loader_data = class_loader_data(class_loader);
|
||||
Dictionary* dictionary = loader_data->dictionary();
|
||||
|
||||
unsigned int name_hash = dictionary->compute_hash(name_h);
|
||||
|
||||
// Hold SD lock around find_class and placeholder creation for DEFINE_CLASS
|
||||
{
|
||||
MutexLocker mu(THREAD, SystemDictionary_lock);
|
||||
// First check if class already defined
|
||||
if (is_parallelDefine(class_loader)) {
|
||||
InstanceKlass* check = dictionary->find_class(name_hash, name_h);
|
||||
InstanceKlass* check = dictionary->find_class(THREAD, name_h);
|
||||
if (check != NULL) {
|
||||
return check;
|
||||
}
|
||||
|
@ -1565,7 +1557,7 @@ InstanceKlass* SystemDictionary::find_or_define_helper(Symbol* class_name, Handl
|
|||
PlaceholderTable::find_and_remove(name_h, loader_data, PlaceholderTable::DEFINE_CLASS, THREAD);
|
||||
SystemDictionary_lock->notify_all();
|
||||
#ifdef ASSERT
|
||||
InstanceKlass* check = dictionary->find_class(name_hash, name_h);
|
||||
InstanceKlass* check = dictionary->find_class(THREAD, name_h);
|
||||
assert(check != NULL, "definer missed recording success");
|
||||
#endif
|
||||
return ik;
|
||||
|
@ -1731,8 +1723,7 @@ void SystemDictionary::initialize(TRAPS) {
|
|||
// if defining is true, then LinkageError if already in dictionary
|
||||
// if initiating loader, then ok if InstanceKlass matches existing entry
|
||||
|
||||
void SystemDictionary::check_constraints(unsigned int name_hash,
|
||||
InstanceKlass* k,
|
||||
void SystemDictionary::check_constraints(InstanceKlass* k,
|
||||
Handle class_loader,
|
||||
bool defining,
|
||||
TRAPS) {
|
||||
|
@ -1746,7 +1737,7 @@ void SystemDictionary::check_constraints(unsigned int name_hash,
|
|||
|
||||
MutexLocker mu(THREAD, SystemDictionary_lock);
|
||||
|
||||
InstanceKlass* check = loader_data->dictionary()->find_class(name_hash, name);
|
||||
InstanceKlass* check = loader_data->dictionary()->find_class(THREAD, name);
|
||||
if (check != NULL) {
|
||||
// If different InstanceKlass - duplicate class definition,
|
||||
// else - ok, class loaded by a different thread in parallel.
|
||||
|
@ -1790,7 +1781,7 @@ void SystemDictionary::check_constraints(unsigned int name_hash,
|
|||
|
||||
// Update class loader data dictionary - done after check_constraint and add_to_hierarchy
|
||||
// have been called.
|
||||
void SystemDictionary::update_dictionary(unsigned int hash,
|
||||
void SystemDictionary::update_dictionary(JavaThread* current,
|
||||
InstanceKlass* k,
|
||||
Handle class_loader) {
|
||||
// Compile_lock prevents systemDictionary updates during compilations
|
||||
|
@ -1803,9 +1794,9 @@ void SystemDictionary::update_dictionary(unsigned int hash,
|
|||
|
||||
// Make a new dictionary entry.
|
||||
Dictionary* dictionary = loader_data->dictionary();
|
||||
InstanceKlass* sd_check = dictionary->find_class(hash, name);
|
||||
InstanceKlass* sd_check = dictionary->find_class(current, name);
|
||||
if (sd_check == NULL) {
|
||||
dictionary->add_klass(hash, name, k);
|
||||
dictionary->add_klass(current, name, k);
|
||||
}
|
||||
SystemDictionary_lock->notify_all();
|
||||
}
|
||||
|
@ -1821,7 +1812,7 @@ Klass* SystemDictionary::find_constrained_instance_or_array_klass(
|
|||
// First see if it has been loaded directly.
|
||||
// Force the protection domain to be null. (This removes protection checks.)
|
||||
Handle no_protection_domain;
|
||||
Klass* klass = find_instance_or_array_klass(class_name, class_loader,
|
||||
Klass* klass = find_instance_or_array_klass(current, class_name, class_loader,
|
||||
no_protection_domain);
|
||||
if (klass != NULL)
|
||||
return klass;
|
||||
|
@ -1881,15 +1872,13 @@ bool SystemDictionary::add_loader_constraint(Symbol* class_name,
|
|||
}
|
||||
|
||||
Dictionary* dictionary1 = loader_data1->dictionary();
|
||||
unsigned int name_hash1 = dictionary1->compute_hash(constraint_name);
|
||||
|
||||
Dictionary* dictionary2 = loader_data2->dictionary();
|
||||
unsigned int name_hash2 = dictionary2->compute_hash(constraint_name);
|
||||
|
||||
JavaThread* current = JavaThread::current();
|
||||
{
|
||||
MutexLocker mu_s(SystemDictionary_lock);
|
||||
InstanceKlass* klass1 = dictionary1->find_class(name_hash1, constraint_name);
|
||||
InstanceKlass* klass2 = dictionary2->find_class(name_hash2, constraint_name);
|
||||
InstanceKlass* klass1 = dictionary1->find_class(current, constraint_name);
|
||||
InstanceKlass* klass2 = dictionary2->find_class(current, constraint_name);
|
||||
bool result = LoaderConstraintTable::add_entry(constraint_name, klass1, class_loader1,
|
||||
klass2, class_loader2);
|
||||
#if INCLUDE_CDS
|
||||
|
|
|
@ -144,12 +144,13 @@ class SystemDictionary : AllStatic {
|
|||
TRAPS);
|
||||
|
||||
// Lookup an already loaded class. If not found NULL is returned.
|
||||
static InstanceKlass* find_instance_klass(Symbol* class_name, Handle class_loader, Handle protection_domain);
|
||||
static InstanceKlass* find_instance_klass(Thread* current, Symbol* class_name,
|
||||
Handle class_loader, Handle protection_domain);
|
||||
|
||||
// Lookup an already loaded instance or array class.
|
||||
// Do not make any queries to class loaders; consult only the cache.
|
||||
// If not found NULL is returned.
|
||||
static Klass* find_instance_or_array_klass(Symbol* class_name,
|
||||
static Klass* find_instance_or_array_klass(Thread* current, Symbol* class_name,
|
||||
Handle class_loader,
|
||||
Handle protection_domain);
|
||||
|
||||
|
@ -324,7 +325,6 @@ private:
|
|||
Handle class_loader,
|
||||
Handle protection_domain, TRAPS);
|
||||
static InstanceKlass* handle_parallel_loading(JavaThread* current,
|
||||
unsigned int name_hash,
|
||||
Symbol* name,
|
||||
ClassLoaderData* loader_data,
|
||||
Handle lockObject,
|
||||
|
@ -335,8 +335,7 @@ private:
|
|||
Handle class_loader,
|
||||
InstanceKlass* k, TRAPS);
|
||||
static InstanceKlass* load_instance_class_impl(Symbol* class_name, Handle class_loader, TRAPS);
|
||||
static InstanceKlass* load_instance_class(unsigned int name_hash,
|
||||
Symbol* class_name,
|
||||
static InstanceKlass* load_instance_class(Symbol* class_name,
|
||||
Handle class_loader, TRAPS);
|
||||
|
||||
static bool is_shared_class_visible(Symbol* class_name, InstanceKlass* ik,
|
||||
|
@ -400,11 +399,9 @@ protected:
|
|||
static Symbol* find_placeholder(Symbol* name, ClassLoaderData* loader_data);
|
||||
|
||||
// Class loader constraints
|
||||
static void check_constraints(unsigned int hash,
|
||||
InstanceKlass* k, Handle loader,
|
||||
static void check_constraints(InstanceKlass* k, Handle loader,
|
||||
bool defining, TRAPS);
|
||||
static void update_dictionary(unsigned int hash,
|
||||
InstanceKlass* k, Handle loader);
|
||||
static void update_dictionary(JavaThread* current, InstanceKlass* k, Handle loader);
|
||||
};
|
||||
|
||||
#endif // SHARE_CLASSFILE_SYSTEMDICTIONARY_HPP
|
||||
|
|
|
@ -401,7 +401,6 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class(
|
|||
THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
|
||||
ClassLoaderData *loader_data = register_loader(class_loader);
|
||||
Dictionary* dictionary = loader_data->dictionary();
|
||||
unsigned int d_hash = dictionary->compute_hash(name);
|
||||
|
||||
// Note: currently, find_or_load_shared_class is called only from
|
||||
// JVM_FindLoadedClass and used for PlatformClassLoader and AppClassLoader,
|
||||
|
@ -409,7 +408,7 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class(
|
|||
assert(get_loader_lock_or_null(class_loader) == NULL, "ObjectLocker not required");
|
||||
{
|
||||
MutexLocker mu(THREAD, SystemDictionary_lock);
|
||||
InstanceKlass* check = dictionary->find_class(d_hash, name);
|
||||
InstanceKlass* check = dictionary->find_class(THREAD, name);
|
||||
if (check != NULL) {
|
||||
return check;
|
||||
}
|
||||
|
|
|
@ -247,8 +247,7 @@ void vmClasses::resolve_shared_class(InstanceKlass* klass, ClassLoaderData* load
|
|||
klass->restore_unshareable_info(loader_data, domain, NULL, THREAD);
|
||||
SystemDictionary::load_shared_class_misc(klass, loader_data);
|
||||
Dictionary* dictionary = loader_data->dictionary();
|
||||
unsigned int hash = dictionary->compute_hash(klass->name());
|
||||
dictionary->add_klass(hash, klass->name(), klass);
|
||||
dictionary->add_klass(THREAD, klass->name(), klass);
|
||||
SystemDictionary::add_to_hierarchy(klass);
|
||||
assert(klass->is_loaded(), "Must be in at least loaded state");
|
||||
}
|
||||
|
|
|
@ -516,7 +516,7 @@ C2V_VMENTRY_NULL(jobject, lookupType, (JNIEnv* env, jobject, jstring jname, ARGU
|
|||
// This is a name from a signature. Strip off the trimmings.
|
||||
// Call recursive to keep scope of strippedsym.
|
||||
TempNewSymbol strippedsym = Signature::strip_envelope(class_name);
|
||||
resolved_klass = SystemDictionary::find_instance_klass(strippedsym,
|
||||
resolved_klass = SystemDictionary::find_instance_klass(THREAD, strippedsym,
|
||||
class_loader,
|
||||
protection_domain);
|
||||
} else if (Signature::is_array(class_name)) {
|
||||
|
@ -524,7 +524,7 @@ C2V_VMENTRY_NULL(jobject, lookupType, (JNIEnv* env, jobject, jstring jname, ARGU
|
|||
int ndim = ss.skip_array_prefix();
|
||||
if (ss.type() == T_OBJECT) {
|
||||
Symbol* strippedsym = ss.as_symbol();
|
||||
resolved_klass = SystemDictionary::find_instance_klass(strippedsym,
|
||||
resolved_klass = SystemDictionary::find_instance_klass(THREAD, strippedsym,
|
||||
class_loader,
|
||||
protection_domain);
|
||||
if (!resolved_klass.is_null()) {
|
||||
|
@ -534,7 +534,7 @@ C2V_VMENTRY_NULL(jobject, lookupType, (JNIEnv* env, jobject, jstring jname, ARGU
|
|||
resolved_klass = TypeArrayKlass::cast(Universe::typeArrayKlassObj(ss.type()))->array_klass(ndim, CHECK_NULL);
|
||||
}
|
||||
} else {
|
||||
resolved_klass = SystemDictionary::find_instance_klass(class_name,
|
||||
resolved_klass = SystemDictionary::find_instance_klass(THREAD, class_name,
|
||||
class_loader,
|
||||
protection_domain);
|
||||
}
|
||||
|
|
|
@ -1656,7 +1656,7 @@ Klass* JVMCIRuntime::get_klass_by_name_impl(Klass*& accessing_klass,
|
|||
if (!require_local) {
|
||||
found_klass = SystemDictionary::find_constrained_instance_or_array_klass(THREAD, sym, loader);
|
||||
} else {
|
||||
found_klass = SystemDictionary::find_instance_or_array_klass(sym, loader, domain);
|
||||
found_klass = SystemDictionary::find_instance_or_array_klass(THREAD, sym, loader, domain);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -584,7 +584,7 @@ Klass* ConstantPool::klass_at_if_loaded(const constantPoolHandle& this_cp, int w
|
|||
oop protection_domain = this_cp->pool_holder()->protection_domain();
|
||||
Handle h_prot (current, protection_domain);
|
||||
Handle h_loader (current, loader);
|
||||
Klass* k = SystemDictionary::find_instance_klass(name, h_loader, h_prot);
|
||||
Klass* k = SystemDictionary::find_instance_klass(current, name, h_loader, h_prot);
|
||||
|
||||
// Avoid constant pool verification at a safepoint, as it takes the Module_lock.
|
||||
if (k != NULL && current->is_Java_thread()) {
|
||||
|
|
|
@ -971,7 +971,7 @@ bool Method::is_klass_loaded_by_klass_index(int klass_index) const {
|
|||
Symbol* klass_name = constants()->klass_name_at(klass_index);
|
||||
Handle loader(thread, method_holder()->class_loader());
|
||||
Handle prot (thread, method_holder()->protection_domain());
|
||||
return SystemDictionary::find_instance_klass(klass_name, loader, prot) != NULL;
|
||||
return SystemDictionary::find_instance_klass(thread, klass_name, loader, prot) != NULL;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1095,7 +1095,7 @@ JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name)
|
|||
// The Java level wrapper will perform the necessary security check allowing
|
||||
// us to pass the NULL as the initiating class loader.
|
||||
Handle h_loader(THREAD, JNIHandles::resolve(loader));
|
||||
Klass* k = SystemDictionary::find_instance_or_array_klass(klass_name,
|
||||
Klass* k = SystemDictionary::find_instance_or_array_klass(THREAD, klass_name,
|
||||
h_loader,
|
||||
Handle());
|
||||
#if INCLUDE_CDS
|
||||
|
|
|
@ -958,10 +958,10 @@ Deoptimization::DeoptAction Deoptimization::_unloaded_action
|
|||
template<typename CacheType>
|
||||
class BoxCacheBase : public CHeapObj<mtCompiler> {
|
||||
protected:
|
||||
static InstanceKlass* find_cache_klass(Symbol* klass_name) {
|
||||
ResourceMark rm;
|
||||
static InstanceKlass* find_cache_klass(Thread* thread, Symbol* klass_name) {
|
||||
ResourceMark rm(thread);
|
||||
char* klass_name_str = klass_name->as_C_string();
|
||||
InstanceKlass* ik = SystemDictionary::find_instance_klass(klass_name, Handle(), Handle());
|
||||
InstanceKlass* ik = SystemDictionary::find_instance_klass(thread, klass_name, Handle(), Handle());
|
||||
guarantee(ik != NULL, "%s must be loaded", klass_name_str);
|
||||
guarantee(ik->is_initialized(), "%s must be initialized", klass_name_str);
|
||||
CacheType::compute_offsets(ik);
|
||||
|
@ -976,7 +976,7 @@ template<typename PrimitiveType, typename CacheType, typename BoxType> class Box
|
|||
protected:
|
||||
static BoxCache<PrimitiveType, CacheType, BoxType> *_singleton;
|
||||
BoxCache(Thread* thread) {
|
||||
InstanceKlass* ik = BoxCacheBase<CacheType>::find_cache_klass(CacheType::symbol());
|
||||
InstanceKlass* ik = BoxCacheBase<CacheType>::find_cache_klass(thread, CacheType::symbol());
|
||||
objArrayOop cache = CacheType::cache(ik);
|
||||
assert(cache->length() > 0, "Empty cache");
|
||||
_low = BoxType::value(cache->obj_at(0));
|
||||
|
@ -1032,7 +1032,7 @@ class BooleanBoxCache : public BoxCacheBase<java_lang_Boolean> {
|
|||
protected:
|
||||
static BooleanBoxCache *_singleton;
|
||||
BooleanBoxCache(Thread *thread) {
|
||||
InstanceKlass* ik = find_cache_klass(java_lang_Boolean::symbol());
|
||||
InstanceKlass* ik = find_cache_klass(thread, java_lang_Boolean::symbol());
|
||||
_true_cache = JNIHandles::make_global(Handle(thread, java_lang_Boolean::get_TRUE(ik)));
|
||||
_false_cache = JNIHandles::make_global(Handle(thread, java_lang_Boolean::get_FALSE(ik)));
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/classLoaderDataGraph.hpp"
|
||||
#include "classfile/dictionary.hpp"
|
||||
#include "classfile/stringTable.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "code/codeCache.hpp"
|
||||
|
@ -559,13 +558,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_SYSTEM_DICTIONARY_RESIZE)) {
|
||||
if (Dictionary::does_any_dictionary_needs_resizing()) {
|
||||
Tracer t("resizing system dictionaries");
|
||||
ClassLoaderDataGraph::resize_dictionaries();
|
||||
}
|
||||
}
|
||||
|
||||
if (_subtasks.try_claim_task(SafepointSynchronize::SAFEPOINT_CLEANUP_LAZY_ROOT_PROCESSING)) {
|
||||
if (_do_lazy_roots) {
|
||||
Tracer t("lazy partial thread root processing");
|
||||
|
|
|
@ -74,7 +74,6 @@ class SafepointSynchronize : AllStatic {
|
|||
SAFEPOINT_CLEANUP_UPDATE_INLINE_CACHES,
|
||||
SAFEPOINT_CLEANUP_SYMBOL_TABLE_REHASH,
|
||||
SAFEPOINT_CLEANUP_STRING_TABLE_REHASH,
|
||||
SAFEPOINT_CLEANUP_SYSTEM_DICTIONARY_RESIZE,
|
||||
SAFEPOINT_CLEANUP_REQUEST_OOPSTORAGE_CLEANUP,
|
||||
// Leave this one last.
|
||||
SAFEPOINT_CLEANUP_NUM_TASKS
|
||||
|
|
|
@ -507,7 +507,7 @@ Klass* SignatureStream::as_klass(Handle class_loader, Handle protection_domain,
|
|||
} else if (failure_mode == CachedOrNull) {
|
||||
NoSafepointVerifier nsv; // no loading, now, we mean it!
|
||||
assert(!HAS_PENDING_EXCEPTION, "");
|
||||
k = SystemDictionary::find_instance_klass(name, class_loader, protection_domain);
|
||||
k = SystemDictionary::find_instance_klass(THREAD, name, class_loader, protection_domain);
|
||||
// SD::find does not trigger loading, so there should be no throws
|
||||
// Still, bad things can happen, so we CHECK_NULL and ask callers
|
||||
// to do likewise.
|
||||
|
|
|
@ -373,7 +373,7 @@ void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) {
|
|||
// Get the Java runtime name, version, and vendor info after java.lang.System is initialized.
|
||||
// Some values are actually configure-time constants but some can be set via the jlink tool and
|
||||
// so must be read dynamically. We treat them all the same.
|
||||
InstanceKlass* ik = SystemDictionary::find_instance_klass(vmSymbols::java_lang_VersionProps(),
|
||||
InstanceKlass* ik = SystemDictionary::find_instance_klass(THREAD, vmSymbols::java_lang_VersionProps(),
|
||||
Handle(), Handle());
|
||||
{
|
||||
ResourceMark rm(main_thread);
|
||||
|
|
|
@ -166,12 +166,6 @@ static const size_t DEFAULT_TABLE_SIZE = 2048;
|
|||
static const size_t MAX_SIZE = 24;
|
||||
static volatile bool _has_work = false;
|
||||
|
||||
static size_t ceil_log2(size_t value) {
|
||||
size_t ret;
|
||||
for (ret = 1; ((size_t)1 << ret) < value; ++ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
class FinalizerEntryLookupResult {
|
||||
private:
|
||||
FinalizerEntry* _result;
|
||||
|
|
|
@ -80,12 +80,6 @@ class ThreadIdTableConfig : public AllStatic {
|
|||
}
|
||||
};
|
||||
|
||||
static size_t ceil_log2(size_t val) {
|
||||
size_t ret;
|
||||
for (ret = 1; ((size_t)1 << ret) < val; ++ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Lazily creates the table and populates it with the given
|
||||
// thread list
|
||||
void ThreadIdTable::lazy_initialize(const ThreadsList *threads) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2022, 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
|
||||
|
@ -1229,8 +1229,12 @@ inline TableStatistics ConcurrentHashTable<CONFIG, F>::
|
|||
summary.add((double)count);
|
||||
}
|
||||
|
||||
if (_stats_rate == nullptr) {
|
||||
return TableStatistics(summary, literal_bytes, sizeof(Bucket), sizeof(Node));
|
||||
} else {
|
||||
return TableStatistics(*_stats_rate, summary, literal_bytes, sizeof(Bucket), sizeof(Node));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CONFIG, MEMFLAGS F>
|
||||
template <typename VALUE_SIZE_FUNC>
|
||||
|
|
|
@ -1133,7 +1133,6 @@ inline intx byte_size(void* from, void* to) {
|
|||
return (address)to - (address)from;
|
||||
}
|
||||
|
||||
|
||||
// Pack and extract shorts to/from ints:
|
||||
|
||||
inline int extract_low_short_from_int(jint x) {
|
||||
|
|
|
@ -263,5 +263,4 @@ template class Hashtable<InstanceKlass*, mtClass>;
|
|||
template class Hashtable<WeakHandle, mtClass>;
|
||||
template class Hashtable<WeakHandle, mtServiceability>;
|
||||
|
||||
template void BasicHashtable<mtClass>::verify_table<DictionaryEntry>(char const*);
|
||||
template void BasicHashtable<mtClass>::verify_table<ProtectionDomainCacheEntry>(char const*);
|
||||
|
|
|
@ -120,4 +120,12 @@ inline T next_power_of_2(T value) {
|
|||
return round_up_power_of_2(value + 1);
|
||||
}
|
||||
|
||||
// Find log2 value greater than this input
|
||||
template <typename T, ENABLE_IF(std::is_integral<T>::value)>
|
||||
inline T ceil_log2(T value) {
|
||||
T ret;
|
||||
for (ret = 1; ((T)1 << ret) < value; ++ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // SHARE_UTILITIES_POWEROFTWO_HPP
|
||||
|
|
|
@ -132,7 +132,7 @@ public class TestResize {
|
|||
// -Xlog:safepoint+cleanup will print out cleanup details at safepoint
|
||||
// that will allow us to detect if the system dictionary resized.
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+PrintClassLoaderDataGraphAtExit",
|
||||
"-Xlog:safepoint+cleanup",
|
||||
"-Xlog:safepoint+cleanup,class+loader+data",
|
||||
"TriggerResize",
|
||||
String.valueOf(CLASSES_TO_LOAD));
|
||||
analyzeOutputOn(pb);
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 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
|
||||
* @summary Test of diagnostic command VM.systemdictionary which prints dictionary stats
|
||||
* @library /test/lib
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* jdk.compiler
|
||||
* jdk.internal.jvmstat/sun.jvmstat.monitor
|
||||
* @run testng DictionaryStatsTest
|
||||
*/
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.dcmd.CommandExecutor;
|
||||
import jdk.test.lib.dcmd.JMXExecutor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.lang.ref.Reference;
|
||||
|
||||
public class DictionaryStatsTest {
|
||||
|
||||
// Expecting some output like:
|
||||
|
||||
// System Dictionary for 'TestClassLoader' @10ba88b9 class loader statistics:
|
||||
// Number of buckets : 128 = 1024 bytes, each 8
|
||||
// Number of entries : 6 = 96 bytes, each 16
|
||||
// Number of literals : 6 = 96 bytes, avg 16.000
|
||||
// Total footprint : = 1216 bytes
|
||||
// Average bucket size : 0.047
|
||||
// Variance of bucket size : 0.045
|
||||
// Std. dev. of bucket size: 0.211
|
||||
// Maximum bucket size : 1
|
||||
|
||||
public void run(CommandExecutor executor) throws ClassNotFoundException {
|
||||
|
||||
ClassLoader named_cl = new TestClassLoader("TestClassLoader", null);
|
||||
Class<?> c2 = Class.forName("TestClass2", true, named_cl);
|
||||
if (c2.getClassLoader() != named_cl) {
|
||||
Assert.fail("TestClass defined by wrong classloader: " + c2.getClassLoader());
|
||||
}
|
||||
|
||||
// First test: simple output, no classes displayed
|
||||
OutputAnalyzer output = executor.execute("VM.systemdictionary");
|
||||
output.shouldContain("System Dictionary for 'bootstrap'");
|
||||
output.shouldMatch("System Dictionary for 'TestClassLoader'");
|
||||
output.shouldContain("class loader statistics:");
|
||||
output.shouldContain("Number of buckets");
|
||||
output.shouldContain("Number of entries");
|
||||
output.shouldContain("Number of literals");
|
||||
output.shouldContain("Total footprint");
|
||||
output.shouldContain("Average bucket size");
|
||||
output.shouldContain("Variance of bucket size");
|
||||
output.shouldContain("Std. dev. of bucket size");
|
||||
output.shouldContain("Maximum bucket size");
|
||||
|
||||
// what is this?
|
||||
Reference.reachabilityFence(named_cl);
|
||||
}
|
||||
|
||||
static class TestClassLoader extends ClassLoader {
|
||||
|
||||
public TestClassLoader() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TestClassLoader(String name, ClassLoader parent) {
|
||||
super(name, parent);
|
||||
}
|
||||
|
||||
public static final String CLASS_NAME = "TestClass2";
|
||||
|
||||
static ByteBuffer readClassFile(String name)
|
||||
{
|
||||
File f = new File(System.getProperty("test.classes", "."),
|
||||
name);
|
||||
try (FileInputStream fin = new FileInputStream(f);
|
||||
FileChannel fc = fin.getChannel())
|
||||
{
|
||||
return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
|
||||
} catch (IOException e) {
|
||||
Assert.fail("Can't open file: " + name, e);
|
||||
}
|
||||
|
||||
/* Will not reach here as Assert.fail() throws exception */
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Class<?> loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
Class<?> c;
|
||||
if (!CLASS_NAME.equals(name)) {
|
||||
c = super.loadClass(name, resolve);
|
||||
} else {
|
||||
// should not delegate to the system class loader
|
||||
c = findClass(name);
|
||||
if (resolve) {
|
||||
resolveClass(c);
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
protected Class<?> findClass(String name)
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
if (!CLASS_NAME.equals(name)) {
|
||||
throw new ClassNotFoundException("Unexpected class: " + name);
|
||||
}
|
||||
return defineClass(name, readClassFile(name + ".class"), null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jmx() throws ClassNotFoundException {
|
||||
run(new JMXExecutor());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TestClass2 {
|
||||
static {
|
||||
Runnable r = () -> System.out.println("Hello");
|
||||
r.run();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue