8213346: Re-implement shared dictionary using CompactHashtable

Reviewed-by: jiangli
This commit is contained in:
Ioi Lam 2018-11-07 19:40:27 -08:00
parent 14b8e187e2
commit 147fc3ed13
31 changed files with 832 additions and 1049 deletions

View file

@ -53,12 +53,277 @@
#include "runtime/javaCalls.hpp"
#include "runtime/mutexLocker.hpp"
#include "utilities/hashtable.inline.hpp"
#include "utilities/resourceHash.hpp"
#include "utilities/stringUtils.hpp"
objArrayOop SystemDictionaryShared::_shared_protection_domains = NULL;
objArrayOop SystemDictionaryShared::_shared_jar_urls = NULL;
objArrayOop SystemDictionaryShared::_shared_jar_manifests = NULL;
DEBUG_ONLY(bool SystemDictionaryShared::_checked_excluded_classes = false;)
class DumpTimeSharedClassInfo: public CHeapObj<mtClass> {
public:
struct DTConstraint {
Symbol* _name;
Symbol* _from_name;
DTConstraint() : _name(NULL), _from_name(NULL) {}
DTConstraint(Symbol* n, Symbol* fn) : _name(n), _from_name(fn) {}
};
InstanceKlass* _klass;
int _id;
int _clsfile_size;
int _clsfile_crc32;
bool _excluded;
GrowableArray<DTConstraint>* _verifier_constraints;
GrowableArray<char>* _verifier_constraint_flags;
DumpTimeSharedClassInfo() {
_klass = NULL;
_id = -1;
_clsfile_size = -1;
_clsfile_crc32 = -1;
_excluded = false;
_verifier_constraints = NULL;
_verifier_constraint_flags = NULL;
}
void add_verification_constraint(InstanceKlass* k, Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object);
bool is_builtin() {
return SystemDictionaryShared::is_builtin(_klass);
}
int num_constraints() {
if (_verifier_constraint_flags != NULL) {
return _verifier_constraint_flags->length();
} else {
return 0;
}
}
void metaspace_pointers_do(MetaspaceClosure* it) {
it->push(&_klass);
if (_verifier_constraints != NULL) {
for (int i = 0; i < _verifier_constraints->length(); i++) {
DTConstraint* cons = _verifier_constraints->adr_at(i);
it->push(&cons->_name);
it->push(&cons->_from_name);
}
}
}
};
class DumpTimeSharedClassTable: public ResourceHashtable<
InstanceKlass*,
DumpTimeSharedClassInfo,
primitive_hash<InstanceKlass*>,
primitive_equals<InstanceKlass*>,
15889, // prime number
ResourceObj::C_HEAP>
{
int _builtin_count;
int _unregistered_count;
public:
DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k) {
DumpTimeSharedClassInfo* p = get(k);
if (p == NULL) {
assert(!SystemDictionaryShared::checked_excluded_classes(),
"no new classes can be added after check_excluded_classes");
put(k, DumpTimeSharedClassInfo());
p = get(k);
assert(p != NULL, "sanity");
p->_klass = k;
}
return p;
}
class CountClassByCategory : StackObj {
DumpTimeSharedClassTable* _table;
public:
CountClassByCategory(DumpTimeSharedClassTable* table) : _table(table) {}
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
if (SystemDictionaryShared::is_builtin(k)) {
++ _table->_builtin_count;
} else {
++ _table->_unregistered_count;
}
return true; // keep on iterating
}
};
void update_counts() {
CountClassByCategory counter(this);
iterate(&counter);
}
int count_of(bool is_builtin) const {
if (is_builtin) {
return _builtin_count;
} else {
return _unregistered_count;
}
}
};
class RunTimeSharedClassInfo {
public:
struct CrcInfo {
int _clsfile_size;
int _clsfile_crc32;
};
// This is different than DumpTimeSharedClassInfo::DTConstraint. We use
// u4 instead of Symbol* to save space on 64-bit CPU.
struct RTConstraint {
u4 _name;
u4 _from_name;
};
InstanceKlass* _klass;
int _num_constraints;
// optional CrcInfo _crc; (only for UNREGISTERED classes)
// optional RTConstraint _verifier_constraints[_num_constraints]
// optional char _verifier_constraint_flags[_num_constraints]
private:
static size_t header_size_size() {
return sizeof(RunTimeSharedClassInfo);
}
static size_t crc_size(InstanceKlass* klass) {
if (!SystemDictionaryShared::is_builtin(klass)) {
return sizeof(CrcInfo);
} else {
return 0;
}
}
static size_t verifier_constraints_size(int num_constraints) {
return sizeof(RTConstraint) * num_constraints;
}
static size_t verifier_constraint_flags_size(int num_constraints) {
return sizeof(char) * num_constraints;
}
public:
static size_t byte_size(InstanceKlass* klass, int num_constraints) {
return header_size_size() +
crc_size(klass) +
verifier_constraints_size(num_constraints) +
verifier_constraint_flags_size(num_constraints);
}
private:
size_t crc_offset() const {
return header_size_size();
}
size_t verifier_constraints_offset() const {
return crc_offset() + crc_size(_klass);
}
size_t verifier_constraint_flags_offset() const {
return verifier_constraints_offset() + verifier_constraints_size(_num_constraints);
}
void check_constraint_offset(int i) const {
assert(0 <= i && i < _num_constraints, "sanity");
}
public:
CrcInfo* crc() const {
assert(crc_size(_klass) > 0, "must be");
return (CrcInfo*)(address(this) + crc_offset());
}
RTConstraint* verifier_constraints() {
assert(_num_constraints > 0, "sanity");
return (RTConstraint*)(address(this) + verifier_constraints_offset());
}
RTConstraint* verifier_constraint_at(int i) {
check_constraint_offset(i);
return verifier_constraints() + i;
}
char* verifier_constraint_flags() {
assert(_num_constraints > 0, "sanity");
return (char*)(address(this) + verifier_constraint_flags_offset());
}
void init(DumpTimeSharedClassInfo& info) {
_klass = info._klass;
_num_constraints = info.num_constraints();
if (!SystemDictionaryShared::is_builtin(_klass)) {
CrcInfo* c = crc();
c->_clsfile_size = info._clsfile_size;
c->_clsfile_crc32 = info._clsfile_crc32;
}
if (_num_constraints > 0) {
RTConstraint* constraints = verifier_constraints();
char* flags = verifier_constraint_flags();
int i;
for (i = 0; i < _num_constraints; i++) {
constraints[i]._name = MetaspaceShared::object_delta_u4(info._verifier_constraints->at(i)._name);
constraints[i]._from_name = MetaspaceShared::object_delta_u4(info._verifier_constraints->at(i)._from_name);
}
for (i = 0; i < _num_constraints; i++) {
flags[i] = info._verifier_constraint_flags->at(i);
}
}
}
bool matches(int clsfile_size, int clsfile_crc32) const {
return crc()->_clsfile_size == clsfile_size &&
crc()->_clsfile_crc32 == clsfile_crc32;
}
Symbol* get_constraint_name(int i) {
return (Symbol*)(SharedBaseAddress + verifier_constraint_at(i)->_name);
}
Symbol* get_constraint_from_name(int i) {
return (Symbol*)(SharedBaseAddress + verifier_constraint_at(i)->_from_name);
}
char get_constraint_flag(int i) {
check_constraint_offset(i);
return verifier_constraint_flags()[i];
}
private:
// ArchiveCompactor::allocate() has reserved a pointer immediately before
// archived InstanceKlasses. We can use this slot to do a quick
// lookup of InstanceKlass* -> RunTimeSharedClassInfo* without
// building a new hashtable.
//
// info_pointer_addr(klass) --> 0x0100 RunTimeSharedClassInfo*
// InstanceKlass* klass --> 0x0108 <C++ vtbl>
// 0x0110 fields from Klass ...
static RunTimeSharedClassInfo** info_pointer_addr(InstanceKlass* klass) {
return &((RunTimeSharedClassInfo**)klass)[-1];
}
public:
static RunTimeSharedClassInfo* get_for(InstanceKlass* klass) {
return *info_pointer_addr(klass);
}
static void set_for(InstanceKlass* klass, RunTimeSharedClassInfo* record) {
*info_pointer_addr(klass) = record;
}
// Used by RunTimeSharedDictionary to implement OffsetCompactHashtable::EQUALS
static inline bool EQUALS(
const RunTimeSharedClassInfo* value, Symbol* key, int len_unused) {
return (value->_klass->name() == key);
}
};
class RunTimeSharedDictionary : public OffsetCompactHashtable<
Symbol*,
const RunTimeSharedClassInfo*,
RunTimeSharedClassInfo::EQUALS> {};
static DumpTimeSharedClassTable* _dumptime_table = NULL;
static RunTimeSharedDictionary _builtin_dictionary;
static RunTimeSharedDictionary _unregistered_dictionary;
oop SystemDictionaryShared::shared_protection_domain(int index) {
return _shared_protection_domains->obj_at(index);
@ -478,9 +743,8 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class(
return NULL;
}
if (shared_dictionary() != NULL &&
(SystemDictionary::is_system_class_loader(class_loader()) ||
SystemDictionary::is_platform_class_loader(class_loader()))) {
if (SystemDictionary::is_system_class_loader(class_loader()) ||
SystemDictionary::is_platform_class_loader(class_loader())) {
// Fix for 4474172; see evaluation for more details
class_loader = Handle(
THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
@ -523,8 +787,7 @@ InstanceKlass* SystemDictionaryShared::find_or_load_shared_class(
InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader(
Symbol* class_name, Handle class_loader, TRAPS) {
assert(UseSharedSpaces, "must be");
assert(shared_dictionary() != NULL, "already checked");
InstanceKlass* ik = shared_dictionary()->find_class_for_builtin_loader(class_name);
InstanceKlass* ik = find_builtin_class(class_name);
if (ik != NULL) {
if ((ik->is_shared_app_class() &&
@ -536,7 +799,6 @@ InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader(
return load_shared_class(ik, class_loader, protection_domain, THREAD);
}
}
return NULL;
}
@ -574,12 +836,12 @@ void SystemDictionaryShared::allocate_shared_data_arrays(int size, TRAPS) {
}
// This function is called for loading only UNREGISTERED classes
InstanceKlass* SystemDictionaryShared::lookup_from_stream(const Symbol* class_name,
InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name,
Handle class_loader,
Handle protection_domain,
const ClassFileStream* cfs,
TRAPS) {
if (shared_dictionary() == NULL) {
if (!UseSharedSpaces) {
return NULL;
}
if (class_name == NULL) { // don't do this for anonymous classes
@ -592,27 +854,18 @@ InstanceKlass* SystemDictionaryShared::lookup_from_stream(const Symbol* class_na
return NULL;
}
ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader());
InstanceKlass* k;
{ // UNREGISTERED loader
if (!shared_dictionary()->class_exists_for_unregistered_loader(class_name)) {
// No classes of this name for unregistered loaders.
return NULL;
}
int clsfile_size = cfs->length();
int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
k = shared_dictionary()->find_class_for_unregistered_loader(class_name,
clsfile_size, clsfile_crc32);
}
if (k == NULL) { // not archived
const RunTimeSharedClassInfo* record = find_record(&_unregistered_dictionary, class_name);
if (record == NULL) {
return NULL;
}
return acquire_class_for_current_thread(k, class_loader,
int clsfile_size = cfs->length();
int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
if (!record->matches(clsfile_size, clsfile_crc32)) {
return NULL;
}
return acquire_class_for_current_thread(record->_klass, class_loader,
protection_domain, THREAD);
}
@ -649,19 +902,27 @@ InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread(
return shared_klass;
}
bool SystemDictionaryShared::add_non_builtin_klass(Symbol* name,
ClassLoaderData* loader_data,
InstanceKlass* k,
TRAPS) {
assert(DumpSharedSpaces, "only when dumping");
assert(boot_loader_dictionary() != NULL, "must be");
static ResourceHashtable<
Symbol*, bool,
primitive_hash<Symbol*>,
primitive_equals<Symbol*>,
6661, // prime number
ResourceObj::C_HEAP> _loaded_unregistered_classes;
if (boot_loader_dictionary()->add_non_builtin_klass(name, loader_data, k)) {
MutexLocker mu_r(Compile_lock, THREAD); // not really necessary, but add_to_hierarchy asserts this.
add_to_hierarchy(k, CHECK_0);
bool SystemDictionaryShared::add_unregistered_class(InstanceKlass* k, TRAPS) {
assert(DumpSharedSpaces, "only when dumping");
Symbol* name = k->name();
if (_loaded_unregistered_classes.get(name) != NULL) {
// We don't allow duplicated unregistered classes of the same name.
return false;
} else {
bool isnew = _loaded_unregistered_classes.put(name, true);
assert(isnew, "sanity");
MutexLocker mu_r(Compile_lock, THREAD); // add_to_hierarchy asserts this.
SystemDictionary::add_to_hierarchy(k, CHECK_0);
return true;
}
return false;
}
// This function is called to resolve the super/interfaces of shared classes for
@ -698,81 +959,138 @@ InstanceKlass* SystemDictionaryShared::dump_time_resolve_super_or_fail(
}
}
struct SharedMiscInfo {
InstanceKlass* _klass;
int _clsfile_size;
int _clsfile_crc32;
};
static GrowableArray<SharedMiscInfo>* misc_info_array = NULL;
DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for(InstanceKlass* k) {
if (_dumptime_table == NULL) {
_dumptime_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeSharedClassTable();
}
return _dumptime_table->find_or_allocate_info_for(k);
}
void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) {
assert(DumpSharedSpaces, "only when dumping");
int clsfile_size = cfs->length();
int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
if (misc_info_array == NULL) {
misc_info_array = new (ResourceObj::C_HEAP, mtClass) GrowableArray<SharedMiscInfo>(20, /*c heap*/ true);
}
SharedMiscInfo misc_info;
DEBUG_ONLY({
for (int i=0; i<misc_info_array->length(); i++) {
misc_info = misc_info_array->at(i);
assert(misc_info._klass != k, "cannot call set_shared_class_misc_info twice for the same class");
}
});
misc_info._klass = k;
misc_info._clsfile_size = clsfile_size;
misc_info._clsfile_crc32 = clsfile_crc32;
misc_info_array->append(misc_info);
assert(!is_builtin(k), "must be unregistered class");
DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);
info->_clsfile_size = cfs->length();
info->_clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
}
void SystemDictionaryShared::init_shared_dictionary_entry(InstanceKlass* k, DictionaryEntry* ent) {
SharedDictionaryEntry* entry = (SharedDictionaryEntry*)ent;
entry->_id = -1;
entry->_clsfile_size = -1;
entry->_clsfile_crc32 = -1;
entry->_verifier_constraints = NULL;
entry->_verifier_constraint_flags = NULL;
void SystemDictionaryShared::init_dumptime_info(InstanceKlass* k) {
(void)find_or_allocate_info_for(k);
}
if (misc_info_array != NULL) {
for (int i=0; i<misc_info_array->length(); i++) {
SharedMiscInfo misc_info = misc_info_array->at(i);
if (misc_info._klass == k) {
entry->_clsfile_size = misc_info._clsfile_size;
entry->_clsfile_crc32 = misc_info._clsfile_crc32;
misc_info_array->remove_at(i);
return;
}
void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) {
_dumptime_table->remove(k);
}
bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) {
while (k) {
if (k->name()->equals("jdk/internal/event/Event")) {
return true;
}
k = k->java_super();
}
return false;
}
void SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) {
ResourceMark rm;
log_warning(cds)("Skipping %s: %s", k->name()->as_C_string(), reason);
}
bool SystemDictionaryShared::should_be_excluded(InstanceKlass* k) {
if (k->class_loader_data()->is_unsafe_anonymous()) {
return true; // unsafe anonymous classes are not archived, skip
}
if (k->is_in_error_state()) {
return true;
}
if (k->shared_classpath_index() < 0 && is_builtin(k)) {
// These are classes loaded from unsupported locations (such as those loaded by JVMTI native
// agent during dump time).
warn_excluded(k, "Unsupported location");
return true;
}
if (k->signers() != NULL) {
// We cannot include signed classes in the archive because the certificates
// used during dump time may be different than those used during
// runtime (due to expiration, etc).
warn_excluded(k, "Signed JAR");
return true;
}
if (is_jfr_event_class(k)) {
// We cannot include JFR event classes because they need runtime-specific
// instrumentation in order to work with -XX:FlightRecorderOptions=retransform=false.
// There are only a small number of these classes, so it's not worthwhile to
// support them and make CDS more complicated.
warn_excluded(k, "JFR event class");
return true;
}
return false;
}
// k is a class before relocating by ArchiveCompactor
void SystemDictionaryShared::validate_before_archiving(InstanceKlass* k) {
ResourceMark rm;
const char* name = k->name()->as_C_string();
DumpTimeSharedClassInfo* info = _dumptime_table->get(k);
guarantee(info != NULL, "Class %s must be entered into _dumptime_table", name);
guarantee(!info->_excluded, "Should not attempt to archive excluded class %s", name);
if (is_builtin(k)) {
guarantee(k->loader_type() != 0,
"Class loader type must be set for BUILTIN class %s", name);
} else {
guarantee(k->loader_type() == 0,
"Class loader type must not be set for UNREGISTERED class %s", name);
}
}
class ExcludeDumpTimeSharedClasses : StackObj {
public:
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
if (SystemDictionaryShared::should_be_excluded(k)) {
info._excluded = true;
}
return true; // keep on iterating
}
};
void SystemDictionaryShared::check_excluded_classes() {
ExcludeDumpTimeSharedClasses excl;
_dumptime_table->iterate(&excl);
DEBUG_ONLY(_checked_excluded_classes = true;)
}
bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) {
assert(_checked_excluded_classes, "sanity");
assert(DumpSharedSpaces, "only when dumping");
return find_or_allocate_info_for(k)->_excluded;
}
class IterateDumpTimeSharedClassTable : StackObj {
MetaspaceClosure *_it;
public:
IterateDumpTimeSharedClassTable(MetaspaceClosure* it) : _it(it) {}
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
if (!info._excluded) {
info.metaspace_pointers_do(_it);
}
return true; // keep on iterating
}
};
void SystemDictionaryShared::dumptime_classes_do(class MetaspaceClosure* it) {
IterateDumpTimeSharedClassTable iter(it);
_dumptime_table->iterate(&iter);
}
bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
assert(DumpSharedSpaces, "called at dump time only");
// Skip unsafe anonymous classes, which are not archived as they are not in
// dictionary (see assert_no_unsafe_anonymous_classes_in_dictionaries() in
// VM_PopulateDumpSharedSpace::doit()).
if (k->class_loader_data()->is_unsafe_anonymous()) {
return true; // unsafe anonymous classes are not archived, skip
}
SharedDictionaryEntry* entry = ((SharedDictionary*)(k->class_loader_data()->dictionary()))->find_entry_for(k);
ResourceMark rm;
// Lambda classes are not archived and will be regenerated at runtime.
if (entry == NULL) {
guarantee(strstr(k->name()->as_C_string(), "Lambda$") != NULL,
"class should be in dictionary before being verified");
return true;
}
entry->add_verification_constraint(name, from_name, from_field_is_protected,
from_is_array, from_is_object);
if (entry->is_builtin()) {
DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);
info->add_verification_constraint(k, name, from_name, from_field_is_protected,
from_is_array, from_is_object);
if (is_builtin(k)) {
// For builtin class loaders, we can try to complete the verification check at dump time,
// because we can resolve all the constraint classes.
return false;
@ -783,135 +1101,54 @@ bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo
}
}
void SystemDictionaryShared::finalize_verification_constraints_for(InstanceKlass* k) {
if (!k->is_unsafe_anonymous()) {
SharedDictionaryEntry* entry = ((SharedDictionary*)(k->class_loader_data()->dictionary()))->find_entry_for(k);
entry->finalize_verification_constraints();
}
}
void SystemDictionaryShared::finalize_verification_constraints() {
MutexLocker mcld(ClassLoaderDataGraph_lock);
ClassLoaderDataGraph::dictionary_classes_do(finalize_verification_constraints_for);
}
void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass,
TRAPS) {
assert(!DumpSharedSpaces && UseSharedSpaces, "called at run time with CDS enabled only");
SharedDictionaryEntry* entry = shared_dictionary()->find_entry_for(klass);
assert(entry != NULL, "call this only for shared classes");
entry->check_verification_constraints(klass, THREAD);
}
SharedDictionaryEntry* SharedDictionary::find_entry_for(InstanceKlass* klass) {
Symbol* class_name = klass->name();
unsigned int hash = compute_hash(class_name);
int index = hash_to_index(hash);
for (SharedDictionaryEntry* entry = bucket(index);
entry != NULL;
entry = entry->next()) {
if (entry->hash() == hash && entry->literal() == klass) {
return entry;
}
}
return NULL;
}
void SharedDictionaryEntry::add_verification_constraint(Symbol* name,
void DumpTimeSharedClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
if (_verifier_constraints == NULL) {
_verifier_constraints = new(ResourceObj::C_HEAP, mtClass) GrowableArray<Symbol*>(8, true, mtClass);
_verifier_constraints = new(ResourceObj::C_HEAP, mtClass) GrowableArray<DTConstraint>(4, true, mtClass);
}
if (_verifier_constraint_flags == NULL) {
_verifier_constraint_flags = new(ResourceObj::C_HEAP, mtClass) GrowableArray<char>(4, true, mtClass);
}
GrowableArray<Symbol*>* vc_array = (GrowableArray<Symbol*>*)_verifier_constraints;
for (int i=0; i<vc_array->length(); i+= 2) {
if (name == vc_array->at(i) &&
from_name == vc_array->at(i+1)) {
GrowableArray<DTConstraint>* vc_array = _verifier_constraints;
for (int i = 0; i < vc_array->length(); i++) {
DTConstraint* p = vc_array->adr_at(i);
if (name == p->_name && from_name == p->_from_name) {
return;
}
}
vc_array->append(name);
vc_array->append(from_name);
DTConstraint cons(name, from_name);
vc_array->append(cons);
GrowableArray<char>* vcflags_array = (GrowableArray<char>*)_verifier_constraint_flags;
GrowableArray<char>* vcflags_array = _verifier_constraint_flags;
char c = 0;
c |= from_field_is_protected ? FROM_FIELD_IS_PROTECTED : 0;
c |= from_is_array ? FROM_IS_ARRAY : 0;
c |= from_is_object ? FROM_IS_OBJECT : 0;
c |= from_field_is_protected ? SystemDictionaryShared::FROM_FIELD_IS_PROTECTED : 0;
c |= from_is_array ? SystemDictionaryShared::FROM_IS_ARRAY : 0;
c |= from_is_object ? SystemDictionaryShared::FROM_IS_OBJECT : 0;
vcflags_array->append(c);
if (log_is_enabled(Trace, cds, verification)) {
ResourceMark rm;
log_trace(cds, verification)("add_verification_constraint: %s: %s must be subclass of %s",
instance_klass()->external_name(), from_name->as_klass_external_name(),
k->external_name(), from_name->as_klass_external_name(),
name->as_klass_external_name());
}
}
int SharedDictionaryEntry::finalize_verification_constraints() {
assert(DumpSharedSpaces, "called at dump time only");
Thread* THREAD = Thread::current();
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
GrowableArray<Symbol*>* vc_array = (GrowableArray<Symbol*>*)_verifier_constraints;
GrowableArray<char>* vcflags_array = (GrowableArray<char>*)_verifier_constraint_flags;
void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass,
TRAPS) {
assert(!DumpSharedSpaces && UseSharedSpaces, "called at run time with CDS enabled only");
RunTimeSharedClassInfo* record = RunTimeSharedClassInfo::get_for(klass);
if (vc_array != NULL) {
if (log_is_enabled(Trace, cds, verification)) {
ResourceMark rm;
log_trace(cds, verification)("finalize_verification_constraint: %s",
literal()->external_name());
}
int length = record->_num_constraints;
if (length > 0) {
for (int i = 0; i < length; i++) {
Symbol* name = record->get_constraint_name(i);
Symbol* from_name = record->get_constraint_from_name(i);
char c = record->get_constraint_flag(i);
// Copy the constraints from C_HEAP-alloced GrowableArrays to Metaspace-alloced
// Arrays
int size = 0;
{
// FIXME: change this to be done after relocation, so we can use symbol offset??
int length = vc_array->length();
Array<Symbol*>* out = MetadataFactory::new_array<Symbol*>(loader_data, length, 0, THREAD);
assert(out != NULL, "Dump time allocation failure would have aborted VM");
for (int i=0; i<length; i++) {
out->at_put(i, vc_array->at(i));
}
_verifier_constraints = out;
size += out->size() * BytesPerWord;
delete vc_array;
}
{
int length = vcflags_array->length();
Array<char>* out = MetadataFactory::new_array<char>(loader_data, length, 0, THREAD);
assert(out != NULL, "Dump time allocation failure would have aborted VM");
for (int i=0; i<length; i++) {
out->at_put(i, vcflags_array->at(i));
}
_verifier_constraint_flags = out;
size += out->size() * BytesPerWord;
delete vcflags_array;
}
return size;
}
return 0;
}
void SharedDictionaryEntry::check_verification_constraints(InstanceKlass* klass, TRAPS) {
Array<Symbol*>* vc_array = (Array<Symbol*>*)_verifier_constraints;
Array<char>* vcflags_array = (Array<char>*)_verifier_constraint_flags;
if (vc_array != NULL) {
int length = vc_array->length();
for (int i=0; i<length; i+=2) {
Symbol* name = vc_array->at(i);
Symbol* from_name = vc_array->at(i+1);
char c = vcflags_array->at(i/2);
bool from_field_is_protected = (c & FROM_FIELD_IS_PROTECTED) ? true : false;
bool from_is_array = (c & FROM_IS_ARRAY) ? true : false;
bool from_is_object = (c & FROM_IS_OBJECT) ? true : false;
bool from_field_is_protected = (c & SystemDictionaryShared::FROM_FIELD_IS_PROTECTED) ? true : false;
bool from_is_array = (c & SystemDictionaryShared::FROM_IS_ARRAY) ? true : false;
bool from_is_object = (c & SystemDictionaryShared::FROM_IS_OBJECT) ? true : false;
bool ok = VerificationType::resolve_and_check_assignability(klass, name,
from_name, from_field_is_protected, from_is_array, from_is_object, CHECK);
@ -930,132 +1167,100 @@ void SharedDictionaryEntry::check_verification_constraints(InstanceKlass* klass,
}
}
void SharedDictionaryEntry::metaspace_pointers_do(MetaspaceClosure* it) {
it->push((Array<Symbol*>**)&_verifier_constraints);
it->push((Array<char>**)&_verifier_constraint_flags);
class CopySharedClassInfoToArchive : StackObj {
CompactHashtableWriter* _writer;
bool _is_builtin;
public:
CopySharedClassInfoToArchive(CompactHashtableWriter* writer, bool is_builtin)
: _writer(writer), _is_builtin(is_builtin) {}
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
if (!info._excluded && info.is_builtin() == _is_builtin) {
size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_constraints());
RunTimeSharedClassInfo* record =
(RunTimeSharedClassInfo*)MetaspaceShared::read_only_space_alloc(byte_size);
record->init(info);
unsigned int hash = primitive_hash<Symbol*>(info._klass->name());
_writer->add(hash, MetaspaceShared::object_delta_u4(record));
// Save this for quick runtime lookup of InstanceKlass* -> RunTimeSharedClassInfo*
RunTimeSharedClassInfo::set_for(info._klass, record);
}
return true; // keep on iterating
}
};
void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin) {
CompactHashtableStats stats;
dictionary->reset();
int num_buckets = CompactHashtableWriter::default_num_buckets(_dumptime_table->count_of(is_builtin));
CompactHashtableWriter writer(num_buckets, &stats);
CopySharedClassInfoToArchive copy(&writer, is_builtin);
_dumptime_table->iterate(&copy);
writer.dump(dictionary, is_builtin ? "builtin dictionary" : "unregistered dictionary");
}
bool SharedDictionary::add_non_builtin_klass(const Symbol* class_name,
ClassLoaderData* loader_data,
InstanceKlass* klass) {
void SystemDictionaryShared::write_to_archive() {
_dumptime_table->update_counts();
write_dictionary(&_builtin_dictionary, true);
write_dictionary(&_unregistered_dictionary, false);
}
void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc) {
_builtin_dictionary.serialize_header(soc);
_unregistered_dictionary.serialize_header(soc);
}
const RunTimeSharedClassInfo*
SystemDictionaryShared::find_record(RunTimeSharedDictionary* dict, Symbol* name) {
if (UseSharedSpaces) {
unsigned int hash = primitive_hash<Symbol*>(name);
return dict->lookup(name, hash, 0);
} else {
return NULL;
}
}
InstanceKlass* SystemDictionaryShared::find_builtin_class(Symbol* name) {
const RunTimeSharedClassInfo* record = find_record(&_builtin_dictionary, name);
if (record) {
return record->_klass;
} else {
return NULL;
}
}
void SystemDictionaryShared::update_shared_entry(InstanceKlass* k, int id) {
assert(DumpSharedSpaces, "supported only when dumping");
assert(klass != NULL, "adding NULL klass");
assert(klass->name() == class_name, "sanity check on name");
assert(klass->shared_classpath_index() < 0,
"the shared classpath index should not be set for shared class loaded by the custom loaders");
DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);
info->_id = id;
}
// Add an entry for a non-builtin class.
// For a shared class for custom class loaders, SystemDictionary::resolve_or_null will
// not find this class, because is_builtin() is false.
unsigned int hash = compute_hash(class_name);
int index = hash_to_index(hash);
class SharedDictionaryPrinter : StackObj {
outputStream* _st;
int _index;
public:
SharedDictionaryPrinter(outputStream* st) : _st(st), _index(0) {}
for (SharedDictionaryEntry* entry = bucket(index);
entry != NULL;
entry = entry->next()) {
if (entry->hash() == hash) {
InstanceKlass* klass = entry->instance_klass();
if (klass->name() == class_name && klass->class_loader_data() == loader_data) {
// There is already a class defined with the same name
return false;
}
}
void do_value(const RunTimeSharedClassInfo* record) {
ResourceMark rm;
_st->print_cr("%4d: %s", (_index++), record->_klass->external_name());
}
};
assert(Dictionary::entry_size() >= sizeof(SharedDictionaryEntry), "must be big enough");
SharedDictionaryEntry* entry = (SharedDictionaryEntry*)new_entry(hash, klass);
add_entry(index, entry);
assert(entry->is_unregistered(), "sanity");
assert(!entry->is_builtin(), "sanity");
return true;
}
//-----------------
// SharedDictionary
//-----------------
InstanceKlass* SharedDictionary::find_class_for_builtin_loader(const Symbol* name) const {
SharedDictionaryEntry* entry = get_entry_for_builtin_loader(name);
return entry != NULL ? entry->instance_klass() : (InstanceKlass*)NULL;
}
InstanceKlass* SharedDictionary::find_class_for_unregistered_loader(const Symbol* name,
int clsfile_size,
int clsfile_crc32) const {
const SharedDictionaryEntry* entry = get_entry_for_unregistered_loader(name,
clsfile_size,
clsfile_crc32);
return entry != NULL ? entry->instance_klass() : NULL;
}
void SharedDictionary::update_entry(InstanceKlass* klass, int id) {
assert(DumpSharedSpaces, "supported only when dumping");
Symbol* class_name = klass->name();
unsigned int hash = compute_hash(class_name);
int index = hash_to_index(hash);
for (SharedDictionaryEntry* entry = bucket(index);
entry != NULL;
entry = entry->next()) {
if (entry->hash() == hash && entry->literal() == klass) {
entry->_id = id;
return;
}
void SystemDictionaryShared::print_on(outputStream* st) {
if (UseSharedSpaces) {
st->print_cr("Shared Dictionary");
SharedDictionaryPrinter p(st);
_builtin_dictionary.iterate(&p);
_unregistered_dictionary.iterate(&p);
}
ShouldNotReachHere();
}
SharedDictionaryEntry* SharedDictionary::get_entry_for_builtin_loader(const Symbol* class_name) const {
assert(!DumpSharedSpaces, "supported only when at runtime");
unsigned int hash = compute_hash(class_name);
const int index = hash_to_index(hash);
for (SharedDictionaryEntry* entry = bucket(index);
entry != NULL;
entry = entry->next()) {
if (entry->hash() == hash && entry->equals(class_name)) {
if (entry->is_builtin()) {
return entry;
}
}
void SystemDictionaryShared::print_table_statistics(outputStream* st) {
if (UseSharedSpaces) {
_builtin_dictionary.print_table_statistics(st, "Builtin Shared Dictionary");
_unregistered_dictionary.print_table_statistics(st, "Unregistered Shared Dictionary");
}
return NULL;
}
SharedDictionaryEntry* SharedDictionary::get_entry_for_unregistered_loader(const Symbol* class_name,
int clsfile_size,
int clsfile_crc32) const {
assert(!DumpSharedSpaces, "supported only when at runtime");
unsigned int hash = compute_hash(class_name);
int index = hash_to_index(hash);
for (SharedDictionaryEntry* entry = bucket(index);
entry != NULL;
entry = entry->next()) {
if (entry->hash() == hash && entry->equals(class_name)) {
if (entry->is_unregistered()) {
if (clsfile_size == -1) {
// We're called from class_exists_for_unregistered_loader. At run time, we want to
// compute the CRC of a ClassFileStream only if there is an UNREGISTERED class
// with the matching name.
return entry;
} else {
// We're called from find_class_for_unregistered_loader
if (entry->_clsfile_size && clsfile_crc32 == entry->_clsfile_crc32) {
return entry;
}
}
// There can be only 1 class with this name for unregistered loaders.
return NULL;
}
}
}
return NULL;
}