mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 23:34:52 +02:00
8213346: Re-implement shared dictionary using CompactHashtable
Reviewed-by: jiangli
This commit is contained in:
parent
14b8e187e2
commit
147fc3ed13
31 changed files with 832 additions and 1049 deletions
|
@ -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(©);
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue