8267189: Remove duplicated unregistered classes from dynamic archive

Reviewed-by: ccheung, minqi
This commit is contained in:
Ioi Lam 2021-06-17 22:19:23 +00:00
parent fa3b44d438
commit bb24fa652a
8 changed files with 316 additions and 19 deletions

View file

@ -1165,29 +1165,43 @@ InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread(
return shared_klass;
}
class LoadedUnregisteredClassesTable : public ResourceHashtable<
Symbol*, bool,
class UnregisteredClassesTable : public ResourceHashtable<
Symbol*, InstanceKlass*,
primitive_hash<Symbol*>,
primitive_equals<Symbol*>,
6661, // prime number
15889, // prime number
ResourceObj::C_HEAP> {};
static LoadedUnregisteredClassesTable* _loaded_unregistered_classes = NULL;
static UnregisteredClassesTable* _unregistered_classes_table = NULL;
bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKlass* k) {
// We don't allow duplicated unregistered classes of the same name.
assert(DumpSharedSpaces, "only when dumping");
Symbol* name = k->name();
if (_loaded_unregistered_classes == NULL) {
_loaded_unregistered_classes = new (ResourceObj::C_HEAP, mtClass)LoadedUnregisteredClassesTable();
bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKlass* klass) {
// We don't allow duplicated unregistered classes with the same name.
// We only archive the first class with that name that succeeds putting
// itself into the table.
Arguments::assert_is_dumping_archive();
MutexLocker ml(current, UnregisteredClassesTable_lock);
Symbol* name = klass->name();
if (_unregistered_classes_table == NULL) {
_unregistered_classes_table = new (ResourceObj::C_HEAP, mtClass)UnregisteredClassesTable();
}
bool created = false;
_loaded_unregistered_classes->put_if_absent(name, true, &created);
bool created;
InstanceKlass** v = _unregistered_classes_table->put_if_absent(name, klass, &created);
if (created) {
name->increment_refcount();
}
return (klass == *v);
}
// true == class was successfully added; false == a duplicated class (with the same name) already exists.
bool SystemDictionaryShared::add_unregistered_class_for_static_archive(Thread* current, InstanceKlass* k) {
assert(DumpSharedSpaces, "only when dumping");
if (add_unregistered_class(current, k)) {
MutexLocker mu_r(current, Compile_lock); // add_to_hierarchy asserts this.
SystemDictionary::add_to_hierarchy(k);
return true;
} else {
return false;
}
return created;
}
// This function is called to lookup the super/interfaces of shared classes for
@ -1295,6 +1309,21 @@ void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) {
_dumptime_table->remove(k);
}
void SystemDictionaryShared::handle_class_unloading(InstanceKlass* klass) {
remove_dumptime_info(klass);
if (_unregistered_classes_table != NULL) {
// Remove the class from _unregistered_classes_table: keep the entry but
// set it to NULL. This ensure no classes with the same name can be
// added again.
MutexLocker ml(Thread::current(), UnregisteredClassesTable_lock);
InstanceKlass** v = _unregistered_classes_table->get(klass->name());
if (v != NULL) {
*v = NULL;
}
}
}
bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) {
while (k) {
if (k->name()->equals("jdk/internal/event/Event")) {
@ -1476,6 +1505,48 @@ void SystemDictionaryShared::validate_before_archiving(InstanceKlass* k) {
}
}
class UnregisteredClassesDuplicationChecker : StackObj {
GrowableArray<InstanceKlass*> _list;
Thread* _thread;
public:
UnregisteredClassesDuplicationChecker() : _thread(Thread::current()) {}
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
if (!SystemDictionaryShared::is_builtin(k)) {
_list.append(k);
}
return true; // keep on iterating
}
static int compare_by_loader(InstanceKlass** a, InstanceKlass** b) {
ClassLoaderData* loader_a = a[0]->class_loader_data();
ClassLoaderData* loader_b = b[0]->class_loader_data();
if (loader_a != loader_b) {
return intx(loader_a) - intx(loader_b);
} else {
return intx(a[0]) - intx(b[0]);
}
}
void mark_duplicated_classes() {
// Two loaders may load two identical or similar hierarchies of classes. If we
// check for duplication in random order, we may end up excluding important base classes
// in both hierarchies, causing most of the classes to be excluded.
// We sort the classes by their loaders. This way we're likely to archive
// all classes in the one of the two hierarchies.
_list.sort(compare_by_loader);
for (int i = 0; i < _list.length(); i++) {
InstanceKlass* k = _list.at(i);
bool i_am_first = SystemDictionaryShared::add_unregistered_class(_thread, k);
if (!i_am_first) {
SystemDictionaryShared::warn_excluded(k, "Duplicated unregistered class");
SystemDictionaryShared::set_excluded_locked(k);
}
}
}
};
class ExcludeDumpTimeSharedClasses : StackObj {
public:
bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
@ -1487,6 +1558,16 @@ public:
void SystemDictionaryShared::check_excluded_classes() {
assert(no_class_loading_should_happen(), "sanity");
assert_lock_strong(DumpTimeTable_lock);
if (DynamicDumpSharedSpaces) {
// Do this first -- if a base class is excluded due to duplication,
// all of its subclasses will also be excluded by ExcludeDumpTimeSharedClasses
ResourceMark rm;
UnregisteredClassesDuplicationChecker dup_checker;
_dumptime_table->iterate(&dup_checker);
dup_checker.mark_duplicated_classes();
}
ExcludeDumpTimeSharedClasses excl;
_dumptime_table->iterate(&excl);
_dumptime_table->update_counts();
@ -1500,6 +1581,15 @@ bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) {
return (p == NULL) ? true : p->is_excluded();
}
void SystemDictionaryShared::set_excluded_locked(InstanceKlass* k) {
assert_lock_strong(DumpTimeTable_lock);
Arguments::assert_is_dumping_archive();
DumpTimeSharedClassInfo* info = find_or_allocate_info_for_locked(k);
if (info != NULL) {
info->set_excluded();
}
}
void SystemDictionaryShared::set_excluded(InstanceKlass* k) {
Arguments::assert_is_dumping_archive();
DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);