From 8f039b56294604271c8ee896aca49d325b5472e5 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 31 Jul 2024 17:55:01 +0000 Subject: [PATCH] 8336919: Cleanup and rename tags in placeholders code Co-authored-by: Frederic Parain Reviewed-by: iklam, fparain, dholmes --- .../share/classfile/classFileParser.cpp | 14 ++-- src/hotspot/share/classfile/placeholders.cpp | 49 +++++------ src/hotspot/share/classfile/placeholders.hpp | 36 ++++---- .../share/classfile/systemDictionary.cpp | 84 ++++++++++--------- .../share/classfile/systemDictionary.hpp | 20 +++-- .../gtest/classfile/test_placeholders.cpp | 8 +- 6 files changed, 110 insertions(+), 101 deletions(-) diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index ddde58e5d20..407078d64fc 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -835,14 +835,12 @@ void ClassFileParser::parse_interfaces(const ClassFileStream* const stream, guarantee_property(unresolved_klass->char_at(0) != JVM_SIGNATURE_ARRAY, "Bad interface name in class file %s", CHECK); - // Call resolve_super so class circularity is checked - interf = SystemDictionary::resolve_super_or_fail( - _class_name, - unresolved_klass, - Handle(THREAD, _loader_data->class_loader()), - _protection_domain, - false, - CHECK); + // Call resolve on the interface class name with class circularity checking + interf = SystemDictionary::resolve_super_or_fail(_class_name, + unresolved_klass, + Handle(THREAD, _loader_data->class_loader()), + _protection_domain, + false, CHECK); } if (!interf->is_interface()) { diff --git a/src/hotspot/share/classfile/placeholders.cpp b/src/hotspot/share/classfile/placeholders.cpp index a6a86473ea7..38d359efcc2 100644 --- a/src/hotspot/share/classfile/placeholders.cpp +++ b/src/hotspot/share/classfile/placeholders.cpp @@ -95,8 +95,8 @@ SeenThread* PlaceholderEntry::actionToQueue(PlaceholderTable::classloadAction ac case PlaceholderTable::LOAD_INSTANCE: queuehead = _loadInstanceThreadQ; break; - case PlaceholderTable::LOAD_SUPER: - queuehead = _superThreadQ; + case PlaceholderTable::DETECT_CIRCULARITY: + queuehead = _circularityThreadQ; break; case PlaceholderTable::DEFINE_CLASS: queuehead = _defineThreadQ; @@ -111,8 +111,8 @@ void PlaceholderEntry::set_threadQ(SeenThread* seenthread, PlaceholderTable::cla case PlaceholderTable::LOAD_INSTANCE: _loadInstanceThreadQ = seenthread; break; - case PlaceholderTable::LOAD_SUPER: - _superThreadQ = seenthread; + case PlaceholderTable::DETECT_CIRCULARITY: + _circularityThreadQ = seenthread; break; case PlaceholderTable::DEFINE_CLASS: _defineThreadQ = seenthread; @@ -188,10 +188,10 @@ bool PlaceholderEntry::remove_seen_thread(JavaThread* thread, PlaceholderTable:: } -void PlaceholderEntry::set_supername(Symbol* supername) { +void PlaceholderEntry::set_next_klass_name(Symbol* next_klass_name) { assert_locked_or_safepoint(SystemDictionary_lock); - assert(_supername == nullptr || _supername->refcount() > 1, "must be referenced also by the loader"); - _supername = supername; + assert(_next_klass_name == nullptr || _next_klass_name->refcount() > 1, "must be referenced also by the loader"); + _next_klass_name = next_klass_name; } // Placeholder objects represent classes currently being loaded. @@ -199,12 +199,12 @@ void PlaceholderEntry::set_supername(Symbol* supername) { // SystemDictionary_lock, so we don't need special precautions // on store ordering here. static PlaceholderEntry* add_entry(Symbol* class_name, ClassLoaderData* loader_data, - Symbol* supername){ + Symbol* next_klass_name){ assert_locked_or_safepoint(SystemDictionary_lock); assert(class_name != nullptr, "adding nullptr obj"); PlaceholderEntry entry; - entry.set_supername(supername); + entry.set_next_klass_name(next_klass_name); PlaceholderKey key(class_name, loader_data); bool created; PlaceholderEntry* table_copy = _placeholders->put_if_absent(key, entry, &created); @@ -230,7 +230,7 @@ PlaceholderEntry* PlaceholderTable::get_entry(Symbol* class_name, ClassLoaderDat static const char* action_to_string(PlaceholderTable::classloadAction action) { switch (action) { case PlaceholderTable::LOAD_INSTANCE: return "LOAD_INSTANCE"; - case PlaceholderTable::LOAD_SUPER: return "LOAD_SUPER"; + case PlaceholderTable::DETECT_CIRCULARITY: return "DETECT_CIRCULARITY"; case PlaceholderTable::DEFINE_CLASS: return "DEFINE_CLASS"; } return ""; @@ -250,20 +250,21 @@ inline void log(Symbol* name, PlaceholderEntry* entry, const char* function, Pla // If no entry exists, add a placeholder entry // If entry exists, reuse entry // For both, push SeenThread for classloadAction -// If LOAD_SUPER, this is used for circularity detection for instanceklass loading. +// If DETECT_CIRCULARITY, this is used for circularity detection for instanceklass loading. PlaceholderEntry* PlaceholderTable::find_and_add(Symbol* name, ClassLoaderData* loader_data, classloadAction action, - Symbol* supername, + Symbol* next_klass_name, JavaThread* thread) { - assert(action != LOAD_SUPER || supername != nullptr, "must have a super class name"); + assert(action != DETECT_CIRCULARITY || next_klass_name != nullptr, + "must have a class name for the next step in the class resolution recursion"); PlaceholderEntry* probe = get_entry(name, loader_data); if (probe == nullptr) { // Nothing found, add place holder - probe = add_entry(name, loader_data, supername); + probe = add_entry(name, loader_data, next_klass_name); } else { - if (action == LOAD_SUPER) { - probe->set_supername(supername); + if (action == DETECT_CIRCULARITY) { + probe->set_next_klass_name(next_klass_name); } } probe->add_seen_thread(thread, action); @@ -295,11 +296,11 @@ void PlaceholderTable::find_and_remove(Symbol* name, ClassLoaderData* loader_dat assert(probe != nullptr, "must find an entry"); log(name, probe, "find_and_remove", action); probe->remove_seen_thread(thread, action); - if (probe->superThreadQ() == nullptr) { - probe->set_supername(nullptr); + if (probe->circularityThreadQ() == nullptr) { + probe->set_next_klass_name(nullptr); } // If no other threads using this entry, and this thread is not using this entry for other states - if ((probe->superThreadQ() == nullptr) && (probe->loadInstanceThreadQ() == nullptr) + if ((probe->circularityThreadQ() == nullptr) && (probe->loadInstanceThreadQ() == nullptr) && (probe->defineThreadQ() == nullptr) && (probe->definer() == nullptr)) { remove_entry(name, loader_data); } @@ -312,9 +313,9 @@ void PlaceholderKey::print_on(outputStream* st) const { } void PlaceholderEntry::print_on(outputStream* st) const { - if (supername() != nullptr) { - st->print(", supername "); - supername()->print_value_on(st); + if (next_klass_name() != nullptr) { + st->print(", next_klass_name "); + next_klass_name()->print_value_on(st); } if (definer() != nullptr) { st->print(", definer "); @@ -328,8 +329,8 @@ void PlaceholderEntry::print_on(outputStream* st) const { st->print("loadInstanceThreadQ threads:"); SeenThread::print_action_queue(loadInstanceThreadQ(), st); st->cr(); - st->print("superThreadQ threads:"); - SeenThread::print_action_queue(superThreadQ(), st); + st->print("circularityThreadQ threads:"); + SeenThread::print_action_queue(circularityThreadQ(), st); st->cr(); st->print("defineThreadQ threads:"); SeenThread::print_action_queue(defineThreadQ(), st); diff --git a/src/hotspot/share/classfile/placeholders.hpp b/src/hotspot/share/classfile/placeholders.hpp index 7f83cf5d058..6348f76a14e 100644 --- a/src/hotspot/share/classfile/placeholders.hpp +++ b/src/hotspot/share/classfile/placeholders.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, 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 @@ -41,15 +41,15 @@ class PlaceholderTable : public AllStatic { // caller claims ownership of that action // For parallel classloading: // multiple LOAD_INSTANCE threads can proceed in parallel - // multiple LOAD_SUPER threads can proceed in parallel - // LOAD_SUPER needed to check for class circularity + // multiple DETECT_CIRCULARITY threads can proceed in parallel + // DETECT_CIRCULARITY needed to check for class circularity // DEFINE_CLASS: ultimately define class must be single threaded // on a class/classloader basis // so the head of that queue owns the token // and the rest of the threads return the result the first thread gets enum classloadAction { LOAD_INSTANCE = 1, // calling load_instance_class - LOAD_SUPER = 2, // loading superclass for this class + DETECT_CIRCULARITY = 2, // loading while detecting class circularity DEFINE_CLASS = 3 // find_or_define class }; static void initialize(); @@ -81,13 +81,13 @@ class SeenThread; class PlaceholderEntry { friend class PlaceholderTable; private: - SymbolHandle _supername; - JavaThread* _definer; // owner of define token - InstanceKlass* _instanceKlass; // InstanceKlass from successful define - SeenThread* _superThreadQ; // doubly-linked queue of Threads loading a superclass for this class - SeenThread* _loadInstanceThreadQ; // loadInstance thread - // This can't be multiple threads since class loading waits for - // this token to be removed. + SymbolHandle _next_klass_name; // next step in the recursive process of class loading + JavaThread* _definer; // owner of define token + InstanceKlass* _instanceKlass; // InstanceKlass from successful define + SeenThread* _circularityThreadQ; // doubly-linked queue of Threads loading with circularity detection + SeenThread* _loadInstanceThreadQ; // loadInstance thread + // This can't be multiple threads since class loading + // waits for this token to be removed. SeenThread* _defineThreadQ; // queue of Threads trying to define this class // including _definer @@ -99,8 +99,8 @@ class PlaceholderEntry { void add_seen_thread(JavaThread* thread, PlaceholderTable::classloadAction action); bool remove_seen_thread(JavaThread* thread, PlaceholderTable::classloadAction action); - SeenThread* superThreadQ() const { return _superThreadQ; } - void set_superThreadQ(SeenThread* SeenThread) { _superThreadQ = SeenThread; } + SeenThread* circularityThreadQ() const { return _circularityThreadQ; } + void set_circularityThreadQ(SeenThread* SeenThread) { _circularityThreadQ = SeenThread; } SeenThread* loadInstanceThreadQ() const { return _loadInstanceThreadQ; } void set_loadInstanceThreadQ(SeenThread* SeenThread) { _loadInstanceThreadQ = SeenThread; } @@ -110,10 +110,10 @@ class PlaceholderEntry { public: PlaceholderEntry() : _definer(nullptr), _instanceKlass(nullptr), - _superThreadQ(nullptr), _loadInstanceThreadQ(nullptr), _defineThreadQ(nullptr) { } + _circularityThreadQ(nullptr), _loadInstanceThreadQ(nullptr), _defineThreadQ(nullptr) { } - Symbol* supername() const { return _supername; } - void set_supername(Symbol* supername); + Symbol* next_klass_name() const { return _next_klass_name; } + void set_next_klass_name(Symbol* next_klass_name); JavaThread* definer() const {return _definer; } void set_definer(JavaThread* definer) { _definer = definer; } @@ -121,8 +121,8 @@ class PlaceholderEntry { InstanceKlass* instance_klass() const {return _instanceKlass; } void set_instance_klass(InstanceKlass* ik) { _instanceKlass = ik; } - bool super_load_in_progress() { - return (_superThreadQ != nullptr); + bool circularity_detection_in_progress() { + return (_circularityThreadQ != nullptr); } bool instance_load_in_progress() { diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp index 852416e30f5..3016d41b289 100644 --- a/src/hotspot/share/classfile/systemDictionary.cpp +++ b/src/hotspot/share/classfile/systemDictionary.cpp @@ -399,32 +399,32 @@ static inline void log_circularity_error(Symbol* name, PlaceholderEntry* probe) // superclass checks on its own thread to catch class circularity and // to avoid deadlock. // -// resolve_super_or_fail adds a LOAD_SUPER placeholder to the placeholder table before calling -// resolve_instance_class_or_null. ClassCircularityError is detected when a LOAD_SUPER or LOAD_INSTANCE +// resolve_with_circularity_detection adds a DETECT_CIRCULARITY placeholder to the placeholder table before calling +// resolve_instance_class_or_null. ClassCircularityError is detected when a DETECT_CIRCULARITY or LOAD_INSTANCE // placeholder for the same thread, class, classloader is found. // This can be seen with logging option: -Xlog:class+load+placeholders=debug. // -InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name, - Symbol* super_name, - Handle class_loader, - Handle protection_domain, - bool is_superclass, - TRAPS) { +InstanceKlass* SystemDictionary::resolve_with_circularity_detection(Symbol* class_name, + Symbol* next_name, + Handle class_loader, + Handle protection_domain, + bool is_superclass, + TRAPS) { - assert(super_name != nullptr, "null superclass for resolving"); - assert(!Signature::is_array(super_name), "invalid superclass name"); + assert(next_name != nullptr, "null superclass for resolving"); + assert(!Signature::is_array(next_name), "invalid superclass name"); #if INCLUDE_CDS if (CDSConfig::is_dumping_static_archive()) { // Special processing for handling UNREGISTERED shared classes. InstanceKlass* k = SystemDictionaryShared::lookup_super_for_unregistered_class(class_name, - super_name, is_superclass); + next_name, is_superclass); if (k) { return k; } } #endif // INCLUDE_CDS - // If klass is already loaded, just return the superclass or superinterface. + // If class_name is already loaded, just return the superclass or superinterface. // Make sure there's a placeholder for the class_name before resolving. // This is used as a claim that this thread is currently loading superclass/classloader // and for ClassCircularity checks. @@ -439,28 +439,27 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name, InstanceKlass* klassk = dictionary->find_class(THREAD, class_name); InstanceKlass* quicksuperk; // To support parallel loading: if class is done loading, just return the superclass - // if the super_name matches class->super()->name() and if the class loaders match. - // Otherwise, a LinkageError will be thrown later. + // if the next_name matches class->super()->name() and if the class loaders match. if (klassk != nullptr && is_superclass && - ((quicksuperk = klassk->java_super()) != nullptr) && - ((quicksuperk->name() == super_name) && - (quicksuperk->class_loader() == class_loader()))) { - return quicksuperk; + ((quicksuperk = klassk->java_super()) != nullptr) && + ((quicksuperk->name() == next_name) && + (quicksuperk->class_loader() == class_loader()))) { + return quicksuperk; } else { // Must check ClassCircularity before checking if superclass is already loaded. PlaceholderEntry* probe = PlaceholderTable::get_entry(class_name, loader_data); - if (probe && probe->check_seen_thread(THREAD, PlaceholderTable::LOAD_SUPER)) { + if (probe && probe->check_seen_thread(THREAD, PlaceholderTable::DETECT_CIRCULARITY)) { log_circularity_error(class_name, probe); throw_circularity_error = true; } } if (!throw_circularity_error) { - // Be careful not to exit resolve_super without removing this placeholder. + // Be careful not to exit resolve_with_circularity_detection without removing this placeholder. PlaceholderEntry* newprobe = PlaceholderTable::find_and_add(class_name, loader_data, - PlaceholderTable::LOAD_SUPER, - super_name, THREAD); + PlaceholderTable::DETECT_CIRCULARITY, + next_name, THREAD); } } @@ -471,7 +470,7 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name, // Resolve the superclass or superinterface, check results on return InstanceKlass* superk = - SystemDictionary::resolve_instance_class_or_null(super_name, + SystemDictionary::resolve_instance_class_or_null(next_name, class_loader, protection_domain, THREAD); @@ -479,13 +478,13 @@ InstanceKlass* SystemDictionary::resolve_super_or_fail(Symbol* class_name, // Clean up placeholder entry. { MutexLocker mu(THREAD, SystemDictionary_lock); - PlaceholderTable::find_and_remove(class_name, loader_data, PlaceholderTable::LOAD_SUPER, THREAD); + PlaceholderTable::find_and_remove(class_name, loader_data, PlaceholderTable::DETECT_CIRCULARITY, THREAD); SystemDictionary_lock->notify_all(); } // Check for pending exception or null superk, and throw exception if (HAS_PENDING_EXCEPTION || superk == nullptr) { - handle_resolution_exception(super_name, true, CHECK_NULL); + handle_resolution_exception(next_name, true, CHECK_NULL); } return superk; @@ -502,13 +501,15 @@ static void handle_parallel_super_load(Symbol* name, Handle class_loader, Handle protection_domain, TRAPS) { - // superk is not used; resolve_super_or_fail is called for circularity check only. - Klass* superk = SystemDictionary::resolve_super_or_fail(name, - superclassname, - class_loader, - protection_domain, - true, - CHECK); + // The result superk is not used; resolve_with_circularity_detection is called for circularity check only. + // This passes true to is_superclass even though it might not be the super class in order to perform the + // optimization anyway. + Klass* superk = SystemDictionary::resolve_with_circularity_detection(name, + superclassname, + class_loader, + protection_domain, + true, + CHECK); } // Bootstrap and non-parallel capable class loaders use the LOAD_INSTANCE placeholder to @@ -536,7 +537,7 @@ static InstanceKlass* handle_parallel_loading(JavaThread* current, // Wait until the first thread has finished loading this class. Also wait until all the // threads trying to load its superclass have removed their placeholders. while (oldprobe != nullptr && - (oldprobe->instance_load_in_progress() || oldprobe->super_load_in_progress())) { + (oldprobe->instance_load_in_progress() || oldprobe->circularity_detection_in_progress())) { // LOAD_INSTANCE placeholders are used to implement parallel capable class loading // for the bootclass loader. @@ -575,8 +576,9 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle protection_domain, TRAPS) { // name must be in the form of "java/lang/Object" -- cannot be "Ljava/lang/Object;" + DEBUG_ONLY(ResourceMark rm(THREAD)); assert(name != nullptr && !Signature::is_array(name) && - !Signature::has_envelope(name), "invalid class name"); + !Signature::has_envelope(name), "invalid class name: %s", name == nullptr ? "nullptr" : name->as_C_string()); EventClassLoad class_load_start_event; @@ -607,7 +609,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle lockObject = get_loader_lock_or_null(class_loader); ObjectLocker ol(lockObject, THREAD); - bool super_load_in_progress = false; + bool circularity_detection_in_progress = false; InstanceKlass* loaded_class = nullptr; SymbolHandle superclassname; // Keep alive while loading in parallel thread. @@ -625,9 +627,9 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, loaded_class = check; } else { PlaceholderEntry* placeholder = PlaceholderTable::get_entry(name, loader_data); - if (placeholder != nullptr && placeholder->super_load_in_progress()) { - super_load_in_progress = true; - superclassname = placeholder->supername(); + if (placeholder != nullptr && placeholder->circularity_detection_in_progress()) { + circularity_detection_in_progress = true; + superclassname = placeholder->next_klass_name(); assert(superclassname != nullptr, "superclass has to have a name"); } } @@ -635,7 +637,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, // If the class is in the placeholder table with super_class set, // handle superclass loading in progress. - if (super_load_in_progress) { + if (circularity_detection_in_progress) { handle_parallel_super_load(name, superclassname, class_loader, protection_domain, @@ -1052,8 +1054,8 @@ bool SystemDictionary::check_shared_class_super_type(InstanceKlass* klass, Insta } } - Klass *found = resolve_super_or_fail(klass->name(), super_type->name(), - class_loader, protection_domain, is_superclass, CHECK_0); + Klass *found = resolve_with_circularity_detection(klass->name(), super_type->name(), + class_loader, protection_domain, is_superclass, CHECK_0); if (found == super_type) { return true; } else { diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp index 6355de9c4ce..ee50aa38dd0 100644 --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -102,15 +102,23 @@ class SystemDictionary : AllStatic { return resolve_or_null(class_name, Handle(), Handle(), THREAD); } + static InstanceKlass* resolve_with_circularity_detection(Symbol* class_name, + Symbol* next_name, + Handle class_loader, + Handle protection_domain, + bool is_superclass, + TRAPS); + // Resolve a superclass or superinterface. Called from ClassFileParser, // parse_interfaces, resolve_instance_class_or_null, load_shared_class // "class_name" is the class whose super class or interface is being resolved. - static InstanceKlass* resolve_super_or_fail(Symbol* class_name, - Symbol* super_name, + static InstanceKlass* resolve_super_or_fail(Symbol* class_name, Symbol* super_name, Handle class_loader, - Handle protection_domain, - bool is_superclass, - TRAPS); + Handle protection_domain, bool is_superclass, TRAPS) { + return resolve_with_circularity_detection(class_name, super_name, class_loader, protection_domain, + is_superclass, THREAD); + } + private: // Parse the stream to create a hidden class. // Used by jvm_lookup_define_class. diff --git a/test/hotspot/gtest/classfile/test_placeholders.cpp b/test/hotspot/gtest/classfile/test_placeholders.cpp index 4179a1fc695..8cd9536271e 100644 --- a/test/hotspot/gtest/classfile/test_placeholders.cpp +++ b/test/hotspot/gtest/classfile/test_placeholders.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, 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 @@ -49,7 +49,7 @@ TEST_VM(PlaceholderTable, supername) { { MutexLocker ml(THREAD, SystemDictionary_lock); - PlaceholderTable::classloadAction super_action = PlaceholderTable::LOAD_SUPER; + PlaceholderTable::classloadAction super_action = PlaceholderTable::DETECT_CIRCULARITY; PlaceholderTable::classloadAction define_action = PlaceholderTable::DEFINE_CLASS; // DefineClass A and D @@ -71,7 +71,7 @@ TEST_VM(PlaceholderTable, supername) { // Another thread comes in and finds A loading Super PlaceholderEntry* placeholder = PlaceholderTable::get_entry(A, loader_data); - SymbolHandle supername = placeholder->supername(); + SymbolHandle supername = placeholder->next_klass_name(); // Other thread is done before handle_parallel_super_load PlaceholderTable::find_and_remove(A, loader_data, super_action, THREAD); @@ -86,7 +86,7 @@ TEST_VM(PlaceholderTable, supername) { // Refcount should be 3: one in table for class A, one in table for class D // and one locally with SymbolHandle keeping it alive placeholder = PlaceholderTable::get_entry(A, loader_data); - supername = placeholder->supername(); + supername = placeholder->next_klass_name(); EXPECT_EQ(super->refcount(), 3) << "super class name refcount should be 3"; // Second thread's done too