/
githubmirror
/
panama-vector
Обзор
Документация
Войти
/
githubmirror
/
panama-vector
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/hotspot/share/classfile/loaderConstraints.cpp
577 строк
22 KB
Johan Sjölen
8365264: Rename ResourceHashtable to HashTable
13 авг 2025, 21:41
13 авг 2025, 21:41
4680dc9
Код
Авторство
О чём код?
/* * Copyright (c) 2003, 2025, 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. * */ #include "classfile/classLoaderData.inline.hpp" #include "classfile/classLoaderDataGraph.hpp" #include "classfile/dictionary.hpp" #include "classfile/loaderConstraints.hpp" #include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "oops/klass.inline.hpp" #include "oops/symbolHandle.hpp" #include "runtime/mutexLocker.hpp" #include "utilities/hashTable.hpp" // Overview // // The LoaderConstraintTable controls whether two ClassLoaders can resolve the same class name N // to different InstanceKlasses. // // The design of the algorithm can be found in the OOPSLA'98 paper "Dynamic Class Loading in // the Java Virtual Machine" by Sheng Liang and Gilad Bracha. // // To understand the implementation, start with LoaderConstraintTable::{add_entry, check_or_update} // // When a class name N is entered into the LoaderConstraintTable, it's mapped to a ConstraintSet which // contains one or more LoaderConstraints: // // LoaderConstraint_a = { _klass_a, loader_a1, loader_a2, ...} // LoaderConstraint_b = { _klass_b, loader_b1, loader_b2, ...} // LoaderConstraint_c = { _klass_c, loader_c1, loader_c2, ...} // ... // // If _klass_<m> is null, when the first loader_<m><n> resolves the name N to a class K, // we assign _klass_<m> = K. // // if _klass_<m> is non-null, when a loader loader_<m><n> tries to resolve the name N to a class K, // where _klass_<m> != K, a LinkageError is thrown, and the resolution fails. // // Management of LoaderConstraints // // When the SystemDictionary decides that loader_x and loader_y must resolve the name N to the same class: // For the name N, find two LoaderConstraints such that: // // - LoaderConstraint_x contains loader_x // - LoaderConstraint_y contains loader_y // // (Note that no class loader will appear in more than one LoaderConstraint for // each name N, as enforced by the following steps). // // If neither LoaderConstraint_x nor LoaderConstraint_y exist, add a new LoaderConstraint that contains // both loader_x and loader_y. // // Otherwise if LoaderConstraint_x exists but LoaderConstraint_y doesn't exist, add loader_y to LoaderConstraint_x, // or vice versa. // // Otherwise if both LoaderConstraints have different values for _klass, a LinkageError is thrown. // // Otherwise the two LoaderConstraints are merged into one. class LoaderConstraint : public CHeapObj<mtClass> { InstanceKlass* _klass; // Loader constraints enforce correct linking behavior. // Thus, it really operates on ClassLoaderData which represents linking domain, // not class loaders. GrowableArray<ClassLoaderData*>* _loaders; // initiating loaders public: LoaderConstraint(InstanceKlass* klass, ClassLoaderData* loader1, ClassLoaderData* loader2) : _klass(klass) { _loaders = new (mtClass) GrowableArray<ClassLoaderData*>(10, mtClass); add_loader_data(loader1); add_loader_data(loader2); } LoaderConstraint(const LoaderConstraint& src) = delete; LoaderConstraint& operator=(const LoaderConstraint&) = delete; ~LoaderConstraint() { delete _loaders; } InstanceKlass* klass() const { return _klass; } void set_klass(InstanceKlass* k) { _klass = k; } void extend_loader_constraint(Symbol* class_name, ClassLoaderData* loader, InstanceKlass* klass); int num_loaders() const { return _loaders->length(); } ClassLoaderData* loader_data(int i) { return _loaders->at(i); } void add_loader_data(ClassLoaderData* p) { _loaders->push(p); } void remove_loader_at(int n) { assert(_loaders->at(n)->is_unloading(), "should be unloading"); _loaders->remove_at(n); } }; // For this class name, these are the set of LoaderConstraints for classes loaded with this name. class ConstraintSet { // copied into hashtable as value private: GrowableArray<LoaderConstraint*>* _constraints; // loader constraints for this class name. public: ConstraintSet() : _constraints(nullptr) {} ConstraintSet(const ConstraintSet&) = delete; ConstraintSet& operator=(const ConstraintSet&) = delete; void initialize(LoaderConstraint* constraint) { _constraints = new (mtClass) GrowableArray<LoaderConstraint*>(5, mtClass); _constraints->push(constraint); } ~ConstraintSet() { while (!_constraints->is_empty()) { delete _constraints->pop(); } delete _constraints; } int num_constraints() const { return _constraints->length(); } LoaderConstraint* constraint_at(int i) const { return _constraints->at(i); } void add_constraint(LoaderConstraint* new_constraint) { _constraints->push(new_constraint); } void remove_constraint(LoaderConstraint* constraint) { _constraints->remove(constraint); delete constraint; } }; using InternalLoaderConstraintTable = HashTable<SymbolHandle, ConstraintSet, 107, AnyObj::C_HEAP, mtClass, SymbolHandle::compute_hash>; static InternalLoaderConstraintTable* _loader_constraint_table; void LoaderConstraint::extend_loader_constraint(Symbol* class_name, ClassLoaderData* loader, InstanceKlass* klass) { add_loader_data(loader); LogTarget(Info, class, loader, constraints) lt; if (lt.is_enabled()) { ResourceMark rm; lt.print("extending constraint for name %s by adding loader: %s %s", class_name->as_C_string(), loader->loader_name_and_id(), _klass == nullptr ? " and setting class object" : ""); } if (_klass == nullptr) { set_klass(klass); } else { assert(klass == nullptr || _klass == klass, "constraints corrupted"); } } // The loaderConstraintTable must always be accessed with the // SystemDictionary lock held. This is true even for readers as // entries in the table could be being dynamically resized. void LoaderConstraintTable::initialize() { _loader_constraint_table = new (mtClass) InternalLoaderConstraintTable(); } LoaderConstraint* LoaderConstraintTable::find_loader_constraint( Symbol* name, ClassLoaderData* loader_data) { assert_lock_strong(SystemDictionary_lock); ConstraintSet* set = _loader_constraint_table->get(name); if (set == nullptr) { return nullptr; } for (int i = 0; i < set->num_constraints(); i++) { LoaderConstraint* p = set->constraint_at(i); for (int i = p->num_loaders() - 1; i >= 0; i--) { if (p->loader_data(i) == loader_data && // skip unloaded klasses (p->klass() == nullptr || p->klass()->is_loader_alive())) { return p; } } } return nullptr; } // Either add it to an existing entry in the table or make a new one. void LoaderConstraintTable::add_loader_constraint(Symbol* name, InstanceKlass* klass, ClassLoaderData* loader1, ClassLoaderData* loader2) { assert_lock_strong(SystemDictionary_lock); LoaderConstraint* constraint = new LoaderConstraint(klass, loader1, loader2); // The klass may be null if it hasn't been loaded yet, for instance while checking // a parameter name to a method call. We impose this constraint that the // class that is eventually loaded must match between these two loaders. bool created; ConstraintSet* set = _loader_constraint_table->put_if_absent(name, &created); if (created) { set->initialize(constraint); } else { set->add_constraint(constraint); } } class PurgeUnloadedConstraints : public StackObj { public: bool do_entry(SymbolHandle& name, ConstraintSet& set) { LogTarget(Info, class, loader, constraints) lt; int len = set.num_constraints(); for (int i = len - 1; i >= 0; i--) { LoaderConstraint* probe = set.constraint_at(i); InstanceKlass* klass = probe->klass(); // Remove klass that is no longer alive if (klass != nullptr && !klass->is_loader_alive()) { probe->set_klass(nullptr); if (lt.is_enabled()) { ResourceMark rm; lt.print("purging class object from constraint for name %s," " loader list:", name->as_C_string()); for (int i = 0; i < probe->num_loaders(); i++) { lt.print(" [%d]: %s", i, probe->loader_data(i)->loader_name_and_id()); } } } // Remove entries no longer alive from loader array for (int n = probe->num_loaders() - 1; n >= 0; n--) { if (probe->loader_data(n)->is_unloading()) { if (lt.is_enabled()) { ResourceMark rm; lt.print("purging loader %s from constraint for name %s", probe->loader_data(n)->loader_name_and_id(), name->as_C_string()); } probe->remove_loader_at(n); if (lt.is_enabled()) { ResourceMark rm; lt.print("new loader list:"); for (int i = 0; i < probe->num_loaders(); i++) { lt.print(" [%d]: %s", i, probe->loader_data(i)->loader_name_and_id()); } } } } // Check whether the set should be purged if (probe->num_loaders() < 2) { if (lt.is_enabled()) { ResourceMark rm; lt.print("purging complete constraint for name %s", name->as_C_string()); } set.remove_constraint(probe); } else { #ifdef ASSERT if (probe->klass() != nullptr) { assert(probe->klass()->is_loader_alive(), "klass should be live"); } #endif } } if (set.num_constraints() == 0) { return true; } // Don't unlink this set return false; } }; void LoaderConstraintTable::purge_loader_constraints() { assert_locked_or_safepoint(SystemDictionary_lock); // Remove unloaded entries from constraint table PurgeUnloadedConstraints purge; _loader_constraint_table->unlink(&purge); } static void log_ldr_constraint_msg(Symbol* class_name, const char* reason, ClassLoaderData* loader1, ClassLoaderData* loader2) { LogTarget(Info, class, loader, constraints) lt; if (lt.is_enabled()) { ResourceMark rm; lt.print("Failed to add constraint for name: %s, loader[0]: %s," " loader[1]: %s, Reason: %s", class_name->as_C_string(), loader1->loader_name_and_id(), loader2->loader_name_and_id(), reason); } } bool LoaderConstraintTable::add_entry(Symbol* class_name, InstanceKlass* klass1, ClassLoaderData* loader1, InstanceKlass* klass2, ClassLoaderData* loader2) { LogTarget(Info, class, loader, constraints) lt; if (klass1 != nullptr && klass2 != nullptr) { if (klass1 == klass2) { // Same type already loaded in both places. There is no need for any constraint. return true; } else { log_ldr_constraint_msg(class_name, "The class objects presented by loader[0] and loader[1] " "are different", loader1, loader2); return false; } } InstanceKlass* klass = klass1 != nullptr ? klass1 : klass2; LoaderConstraint* pp1 = find_loader_constraint(class_name, loader1); if (pp1 != nullptr && pp1->klass() != nullptr) { if (klass != nullptr) { if (klass != pp1->klass()) { log_ldr_constraint_msg(class_name, "The class object presented by loader[0] does not match " "the stored class object in the constraint", loader1, loader2); return false; } } else { klass = pp1->klass(); } } LoaderConstraint* pp2 = find_loader_constraint(class_name, loader2); if (pp2 != nullptr && pp2->klass() != nullptr) { if (klass != nullptr) { if (klass != pp2->klass()) { log_ldr_constraint_msg(class_name, "The class object presented by loader[1] does not match " "the stored class object in the constraint", loader1, loader2); return false; } } else { klass = pp2->klass(); } } if (pp1 == nullptr && pp2 == nullptr) { add_loader_constraint(class_name, klass, loader1, loader2); if (lt.is_enabled()) { ResourceMark rm; lt.print("adding new constraint for name: %s, loader[0]: %s," " loader[1]: %s", class_name->as_C_string(), loader1->loader_name_and_id(), loader2->loader_name_and_id()); } } else if (pp1 == pp2) { /* constraint already imposed */ if (pp1->klass() == nullptr) { pp1->set_klass(klass); if (lt.is_enabled()) { ResourceMark rm; lt.print("setting class object in existing constraint for" " name: %s and loader %s", class_name->as_C_string(), loader1->loader_name_and_id()); } } else { assert(pp1->klass() == klass, "loader constraints corrupted"); } } else if (pp1 == nullptr) { pp2->extend_loader_constraint(class_name, loader1, klass); } else if (pp2 == nullptr) { pp1->extend_loader_constraint(class_name, loader2, klass); } else { merge_loader_constraints(class_name, pp1, pp2, klass); } return true; } // return true if the constraint was updated, false if the constraint is // violated bool LoaderConstraintTable::check_or_update(InstanceKlass* k, ClassLoaderData* loader, Symbol* name) { LogTarget(Info, class, loader, constraints) lt; LoaderConstraint* p = find_loader_constraint(name, loader); if (p && p->klass() != nullptr && p->klass() != k) { if (lt.is_enabled()) { ResourceMark rm; lt.print("constraint check failed for name %s, loader %s: " "the presented class object differs from that stored", name->as_C_string(), loader->loader_name_and_id()); } return false; } else { if (p && p->klass() == nullptr) { p->set_klass(k); if (lt.is_enabled()) { ResourceMark rm; lt.print("updating constraint for name %s, loader %s, " "by setting class object", name->as_C_string(), loader->loader_name_and_id()); } } return true; } } InstanceKlass* LoaderConstraintTable::find_constrained_klass(Symbol* name, ClassLoaderData* loader) { LoaderConstraint *p = find_loader_constraint(name, loader); if (p != nullptr && p->klass() != nullptr) { assert(p->klass()->is_instance_klass(), "sanity"); if (!p->klass()->is_loaded()) { // Only return fully loaded classes. Classes found through the // constraints might still be in the process of loading. return nullptr; } return p->klass(); } // No constraints, or else no klass loaded yet. return nullptr; } // Removes a class that was added to the table then class loading subsequently failed for this class, // so we don't have a dangling pointer to InstanceKlass in the LoaderConstraintTable. void LoaderConstraintTable::remove_failed_loaded_klass(InstanceKlass* klass, ClassLoaderData* loader) { MutexLocker ml(SystemDictionary_lock); Symbol* name = klass->name(); LoaderConstraint *p = find_loader_constraint(name, loader); if (p != nullptr && p->klass() != nullptr && p->klass() == klass) { // If this is the klass in the constraint, the error was OOM from the ClassLoader.addClass() call. // Other errors during loading (eg. constraint violations) will not have added this klass. log_info(class, loader, constraints)("removing klass %s: failed to load", name->as_C_string()); // We only null out the class, since the constraint for the class name for this loader is still valid as // it was added when checking signature loaders for a method or field resolution. p->set_klass(nullptr); } } void LoaderConstraintTable::merge_loader_constraints(Symbol* class_name, LoaderConstraint* p1, LoaderConstraint* p2, InstanceKlass* klass) { // Copy into the longer of the constraints. LoaderConstraint* dest = p1->num_loaders() <= p2->num_loaders() ? p2 : p1; LoaderConstraint* src = dest == p1 ? p2 : p1; for (int i = 0; i < src->num_loaders(); i++) { // We don't seem to care about duplicates. dest->add_loader_data(src->loader_data(i)); } LogTarget(Info, class, loader, constraints) lt; if (lt.is_enabled()) { ResourceMark rm; lt.print("merged constraints for name %s, new loader list:", class_name->as_C_string()); for (int i = 0; i < dest->num_loaders(); i++) { lt.print(" [%d]: %s", i, dest->loader_data(i)->loader_name_and_id()); } if (dest->klass() == nullptr) { lt.print("... and setting class object"); } } // dest->klass() will hold null if klass, src->klass(), and old // dest->klass() are all null. In addition, all three must have // matching non-null values, otherwise either the constraints would // have been violated, or the constraints had been corrupted (and an // assertion would fail). if (src->klass() != nullptr) { assert(src->klass() == klass, "constraints corrupted"); } if (dest->klass() == nullptr) { dest->set_klass(klass); } else { assert(dest->klass() == klass, "constraints corrupted"); } // Remove src from set ConstraintSet* set = _loader_constraint_table->get(class_name); set->remove_constraint(src); } void LoaderConstraintTable::verify() { Thread* thread = Thread::current(); auto check = [&] (SymbolHandle& key, ConstraintSet& set) { // foreach constraint in the set, check the klass is in the dictionary or placeholder table. int len = set.num_constraints(); for (int i = 0; i < len; i++) { LoaderConstraint* probe = set.constraint_at(i); if (probe->klass() != nullptr) { InstanceKlass* ik = probe->klass(); guarantee(key == ik->name(), "name should match"); Symbol* name = ik->name(); ClassLoaderData* loader_data = ik->class_loader_data(); Dictionary* dictionary = loader_data->dictionary(); InstanceKlass* k = dictionary->find_class(thread, name); if (k != nullptr) { // We found the class in the dictionary, so we should // make sure that the Klass* matches what we already have. guarantee(k == probe->klass(), "klass should be in dictionary"); // If we don't find the class in the dictionary, it is // in the process of loading and may or may not be in the placeholder table. } } for (int n = 0; n< probe->num_loaders(); n++) { assert(ClassLoaderDataGraph::contains_loader_data(probe->loader_data(n)), "The loader is missing"); } } }; assert_locked_or_safepoint(SystemDictionary_lock); _loader_constraint_table->iterate_all(check); } void LoaderConstraintTable::print_table_statistics(outputStream* st) { auto size = [&] (SymbolHandle& key, ConstraintSet& set) { // sizeof set is included in the size of the hashtable node int sum = 0; int len = set.num_constraints(); for (int i = 0; i < len; i++) { LoaderConstraint* probe = set.constraint_at(i); sum += (int)(sizeof(*probe) + (probe->num_loaders() * sizeof(ClassLoaderData*))); } return sum; }; TableStatistics ts = _loader_constraint_table->statistics_calculate(size); ts.print(st, "LoaderConstraintTable"); } // Called with the system dictionary lock held void LoaderConstraintTable::print_on(outputStream* st) { auto printer = [&] (SymbolHandle& key, ConstraintSet& set) { int len = set.num_constraints(); for (int i = 0; i < len; i++) { LoaderConstraint* probe = set.constraint_at(i); st->print("Symbol: %s loaders:", key->as_C_string()); for (int n = 0; n < probe->num_loaders(); n++) { st->cr(); st->print(" "); probe->loader_data(n)->print_value_on(st); } st->cr(); } }; assert_locked_or_safepoint(SystemDictionary_lock); ResourceMark rm; st->print_cr("Java loader constraints (table_size=%d, constraints=%d)", _loader_constraint_table->table_size(), _loader_constraint_table->number_of_entries()); _loader_constraint_table->iterate_all(printer); } void LoaderConstraintTable::print() { print_on(tty); }