Skip to content

Commit 7b0a336

Browse files
committed
8308387: CLD created and unloading list sharing _next node pointer leads to concurrent YC missing CLD roots
Reviewed-by: stefank, coleenp, dholmes, eosterlund
1 parent 60f3b87 commit 7b0a336

File tree

5 files changed

+81
-44
lines changed

5 files changed

+81
-44
lines changed

src/hotspot/share/classfile/classLoaderData.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_ho
148148
_jmethod_ids(nullptr),
149149
_deallocate_list(nullptr),
150150
_next(nullptr),
151+
_unloading_next(nullptr),
151152
_class_loader_klass(nullptr), _name(nullptr), _name_and_id(nullptr) {
152153

153154
if (!h_class_loader.is_null()) {

src/hotspot/share/classfile/classLoaderData.hpp

+29-3
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,41 @@ class ClassLoaderData : public CHeapObj<mtClass> {
153153
GrowableArray<Metadata*>* _deallocate_list;
154154

155155
// Support for walking class loader data objects
156-
ClassLoaderData* _next; /// Next loader_datas created
156+
//
157+
// The ClassLoaderDataGraph maintains two lists to keep track of CLDs.
158+
//
159+
// The first list [_head, _next] is where new CLDs are registered. The CLDs
160+
// are only inserted at the _head, and the _next pointers are only rewritten
161+
// from unlink_next() which unlinks one unloading CLD by setting _next to
162+
// _next->_next. This allows GCs to concurrently walk the list while the CLDs
163+
// are being concurrently unlinked.
164+
//
165+
// The second list [_unloading_head, _unloading_next] is where dead CLDs get
166+
// moved to during class unloading. See: ClassLoaderDataGraph::do_unloading().
167+
// This list is never modified while other threads are iterating over it.
168+
//
169+
// After all dead CLDs have been moved to the unloading list, there's a
170+
// synchronization point (handshake) to ensure that all threads reading these
171+
// CLDs finish their work. This ensures that we don't have a use-after-free
172+
// when we later delete the CLDs.
173+
//
174+
// And finally, when no threads are using the unloading CLDs anymore, we
175+
// remove them from the class unloading list and delete them. See:
176+
// ClassLoaderDataGraph::purge();
177+
ClassLoaderData* _next;
178+
ClassLoaderData* _unloading_next;
157179

158180
Klass* _class_loader_klass;
159181
Symbol* _name;
160182
Symbol* _name_and_id;
161183
JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
162184

163-
void set_next(ClassLoaderData* next) { Atomic::store(&_next, next); }
164-
ClassLoaderData* next() const { return Atomic::load(&_next); }
185+
void set_next(ClassLoaderData* next);
186+
ClassLoaderData* next() const;
187+
void unlink_next();
188+
189+
void set_unloading_next(ClassLoaderData* unloading_next);
190+
ClassLoaderData* unloading_next() const;
165191

166192
ClassLoaderData(Handle h_class_loader, bool has_class_mirror_holder);
167193
~ClassLoaderData();

src/hotspot/share/classfile/classLoaderData.inline.hpp

+23
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,29 @@
3333
#include "oops/oopHandle.inline.hpp"
3434
#include "oops/weakHandle.inline.hpp"
3535

36+
inline void ClassLoaderData::set_next(ClassLoaderData* next) {
37+
assert(this->next() == nullptr, "only link once");
38+
Atomic::store(&_next, next);
39+
}
40+
41+
inline ClassLoaderData* ClassLoaderData::next() const {
42+
return Atomic::load(&_next);
43+
}
44+
45+
inline void ClassLoaderData::unlink_next() {
46+
assert(next()->is_unloading(), "only remove unloading clds");
47+
Atomic::store(&_next, _next->_next);
48+
}
49+
50+
inline void ClassLoaderData::set_unloading_next(ClassLoaderData* unloading_next) {
51+
assert(this->unloading_next() == nullptr, "only link once");
52+
_unloading_next = unloading_next;
53+
}
54+
55+
inline ClassLoaderData* ClassLoaderData::unloading_next() const {
56+
return _unloading_next;
57+
}
58+
3659
inline oop ClassLoaderData::class_loader() const {
3760
assert(!_unloading, "This oop is not available to unloading class loader data");
3861
assert(_holder.is_null() || holder_no_keepalive() != nullptr , "This class loader data holder must be alive");

src/hotspot/share/classfile/classLoaderDataGraph.cpp

+24-37
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424

2525
#include "precompiled.hpp"
26+
#include "classfile/classLoaderData.inline.hpp"
2627
#include "classfile/classLoaderDataGraph.inline.hpp"
2728
#include "classfile/dictionary.hpp"
2829
#include "classfile/javaClasses.hpp"
@@ -200,9 +201,9 @@ void ClassLoaderDataGraph::walk_metadata_and_clean_metaspaces() {
200201
clean_deallocate_lists(walk_all_metadata);
201202
}
202203

203-
// GC root of class loader data created.
204+
// List head of all class loader data.
204205
ClassLoaderData* volatile ClassLoaderDataGraph::_head = nullptr;
205-
ClassLoaderData* ClassLoaderDataGraph::_unloading = nullptr;
206+
ClassLoaderData* ClassLoaderDataGraph::_unloading_head = nullptr;
206207

207208
bool ClassLoaderDataGraph::_should_clean_deallocate_lists = false;
208209
bool ClassLoaderDataGraph::_safepoint_cleanup_needed = false;
@@ -268,16 +269,7 @@ inline void assert_is_safepoint_or_gc() {
268269
"Must be called by safepoint or GC");
269270
}
270271

271-
void ClassLoaderDataGraph::cld_unloading_do(CLDClosure* cl) {
272-
assert_is_safepoint_or_gc();
273-
for (ClassLoaderData* cld = _unloading; cld != nullptr; cld = cld->next()) {
274-
assert(cld->is_unloading(), "invariant");
275-
cl->do_cld(cld);
276-
}
277-
}
278-
279-
// These are functions called by the GC, which require all of the CLDs, including the
280-
// unloading ones.
272+
// These are functions called by the GC, which require all of the CLDs, including not yet unlinked CLDs.
281273
void ClassLoaderDataGraph::cld_do(CLDClosure* cl) {
282274
assert_is_safepoint_or_gc();
283275
for (ClassLoaderData* cld = Atomic::load_acquire(&_head); cld != nullptr; cld = cld->next()) {
@@ -430,7 +422,7 @@ void ClassLoaderDataGraph::loaded_classes_do(KlassClosure* klass_closure) {
430422

431423
void ClassLoaderDataGraph::classes_unloading_do(void f(Klass* const)) {
432424
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
433-
for (ClassLoaderData* cld = _unloading; cld != nullptr; cld = cld->next()) {
425+
for (ClassLoaderData* cld = _unloading_head; cld != nullptr; cld = cld->unloading_next()) {
434426
assert(cld->is_unloading(), "invariant");
435427
cld->classes_do(f);
436428
}
@@ -501,37 +493,32 @@ bool ClassLoaderDataGraph::is_valid(ClassLoaderData* loader_data) {
501493
bool ClassLoaderDataGraph::do_unloading() {
502494
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
503495

504-
ClassLoaderData* data = _head;
505496
ClassLoaderData* prev = nullptr;
506497
bool seen_dead_loader = false;
507498
uint loaders_processed = 0;
508499
uint loaders_removed = 0;
509500

510-
data = _head;
511-
while (data != nullptr) {
501+
for (ClassLoaderData* data = _head; data != nullptr; data = data->next()) {
512502
if (data->is_alive()) {
513503
prev = data;
514-
data = data->next();
515504
loaders_processed++;
516-
continue;
517-
}
518-
seen_dead_loader = true;
519-
loaders_removed++;
520-
ClassLoaderData* dead = data;
521-
dead->unload();
522-
data = data->next();
523-
// Remove from loader list.
524-
// This class loader data will no longer be found
525-
// in the ClassLoaderDataGraph.
526-
if (prev != nullptr) {
527-
prev->set_next(data);
528505
} else {
529-
assert(dead == _head, "sanity check");
530-
// The GC might be walking this concurrently
531-
Atomic::store(&_head, data);
506+
// Found dead CLD.
507+
loaders_removed++;
508+
seen_dead_loader = true;
509+
data->unload();
510+
511+
// Move dead CLD to unloading list.
512+
if (prev != nullptr) {
513+
prev->unlink_next();
514+
} else {
515+
assert(data == _head, "sanity check");
516+
// The GC might be walking this concurrently
517+
Atomic::store(&_head, data->next());
518+
}
519+
data->set_unloading_next(_unloading_head);
520+
_unloading_head = data;
532521
}
533-
dead->set_next(_unloading);
534-
_unloading = dead;
535522
}
536523

537524
log_debug(class, loader, data)("do_unloading: loaders processed %u, loaders removed %u", loaders_processed, loaders_removed);
@@ -563,13 +550,13 @@ void ClassLoaderDataGraph::clean_module_and_package_info() {
563550
}
564551

565552
void ClassLoaderDataGraph::purge(bool at_safepoint) {
566-
ClassLoaderData* list = _unloading;
567-
_unloading = nullptr;
553+
ClassLoaderData* list = _unloading_head;
554+
_unloading_head = nullptr;
568555
ClassLoaderData* next = list;
569556
bool classes_unloaded = false;
570557
while (next != nullptr) {
571558
ClassLoaderData* purge_me = next;
572-
next = purge_me->next();
559+
next = purge_me->unloading_next();
573560
delete purge_me;
574561
classes_unloaded = true;
575562
}

src/hotspot/share/classfile/classLoaderDataGraph.hpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ class ClassLoaderDataGraph : public AllStatic {
4141
friend class ClassLoaderDataGraphIteratorBase;
4242
friend class VMStructs;
4343
private:
44-
// All CLDs (except the null CLD) can be reached by walking _head->_next->...
44+
// All CLDs (except unlinked CLDs) can be reached by walking _head->_next->...
4545
static ClassLoaderData* volatile _head;
46-
static ClassLoaderData* _unloading;
46+
// All unlinked CLDs
47+
static ClassLoaderData* _unloading_head;
4748

4849
// Set if there's anything to purge in the deallocate lists or previous versions
4950
// during a safepoint after class unloading in a full GC.
@@ -67,9 +68,8 @@ class ClassLoaderDataGraph : public AllStatic {
6768
static void clear_claimed_marks();
6869
static void clear_claimed_marks(int claim);
6970
static void verify_claimed_marks_cleared(int claim);
70-
// Iteration through CLDG inside a safepoint; GC support
71+
// Iteration through CLDG; GC support
7172
static void cld_do(CLDClosure* cl);
72-
static void cld_unloading_do(CLDClosure* cl);
7373
static void roots_cld_do(CLDClosure* strong, CLDClosure* weak);
7474
static void always_strong_cld_do(CLDClosure* cl);
7575
// Iteration through CLDG not by GC.

0 commit comments

Comments
 (0)