Skip to content

Commit 8a5ed47

Browse files
committed
8350148: Native stack overflow when writing Java heap objects into AOT cache
Reviewed-by: iveresov, matsaave
1 parent 5928209 commit 8a5ed47

File tree

2 files changed

+87
-35
lines changed

2 files changed

+87
-35
lines changed

src/hotspot/share/cds/heapShared.cpp

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ void HeapShared::clear_root(int index) {
288288
}
289289
}
290290

291-
bool HeapShared::archive_object(oop obj, KlassSubGraphInfo* subgraph_info) {
291+
bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgraph_info) {
292292
assert(CDSConfig::is_dumping_heap(), "dump-time only");
293293

294294
assert(!obj->is_stackChunk(), "do not archive stack chunks");
@@ -304,7 +304,7 @@ bool HeapShared::archive_object(oop obj, KlassSubGraphInfo* subgraph_info) {
304304
} else {
305305
count_allocation(obj->size());
306306
ArchiveHeapWriter::add_source_obj(obj);
307-
CachedOopInfo info = make_cached_oop_info(obj);
307+
CachedOopInfo info = make_cached_oop_info(obj, referrer);
308308
archived_object_cache()->put_when_absent(obj, info);
309309
archived_object_cache()->maybe_grow();
310310
mark_native_pointers(obj);
@@ -1350,33 +1350,39 @@ void HeapShared::clear_archived_roots_of(Klass* k) {
13501350
}
13511351
}
13521352

1353-
class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
1353+
// Push all oops that are referenced by _referencing_obj onto the _stack.
1354+
class HeapShared::ReferentPusher: public BasicOopIterateClosure {
1355+
PendingOopStack* _stack;
1356+
GrowableArray<oop> _found_oop_fields;
13541357
int _level;
13551358
bool _record_klasses_only;
13561359
KlassSubGraphInfo* _subgraph_info;
13571360
oop _referencing_obj;
1358-
1359-
// The following are for maintaining a stack for determining
1360-
// CachedOopInfo::_referrer
1361-
static WalkOopAndArchiveClosure* _current;
1362-
WalkOopAndArchiveClosure* _last;
13631361
public:
1364-
WalkOopAndArchiveClosure(int level,
1362+
ReferentPusher(PendingOopStack* stack,
1363+
int level,
13651364
bool record_klasses_only,
13661365
KlassSubGraphInfo* subgraph_info,
13671366
oop orig) :
1367+
_stack(stack),
1368+
_found_oop_fields(),
13681369
_level(level),
13691370
_record_klasses_only(record_klasses_only),
13701371
_subgraph_info(subgraph_info),
13711372
_referencing_obj(orig) {
1372-
_last = _current;
1373-
_current = this;
13741373
}
1375-
~WalkOopAndArchiveClosure() {
1376-
_current = _last;
1374+
void do_oop(narrowOop *p) { ReferentPusher::do_oop_work(p); }
1375+
void do_oop( oop *p) { ReferentPusher::do_oop_work(p); }
1376+
1377+
~ReferentPusher() {
1378+
while (_found_oop_fields.length() > 0) {
1379+
// This produces the exact same traversal order as the previous version
1380+
// of ReferentPusher that recurses on the C stack -- a depth-first search,
1381+
// walking the oop fields in _referencing_obj by ascending field offsets.
1382+
oop obj = _found_oop_fields.pop();
1383+
_stack->push(PendingOop(obj, _referencing_obj, _level + 1));
1384+
}
13771385
}
1378-
void do_oop(narrowOop *p) { WalkOopAndArchiveClosure::do_oop_work(p); }
1379-
void do_oop( oop *p) { WalkOopAndArchiveClosure::do_oop_work(p); }
13801386

13811387
protected:
13821388
template <class T> void do_oop_work(T *p) {
@@ -1396,20 +1402,15 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
13961402
}
13971403
}
13981404

1399-
bool success = HeapShared::archive_reachable_objects_from(
1400-
_level + 1, _subgraph_info, obj);
1401-
assert(success, "VM should have exited with unarchivable objects for _level > 1");
1405+
_found_oop_fields.push(obj);
14021406
}
14031407
}
14041408

14051409
public:
1406-
static WalkOopAndArchiveClosure* current() { return _current; }
14071410
oop referencing_obj() { return _referencing_obj; }
14081411
KlassSubGraphInfo* subgraph_info() { return _subgraph_info; }
14091412
};
14101413

1411-
WalkOopAndArchiveClosure* WalkOopAndArchiveClosure::_current = nullptr;
1412-
14131414
// Checks if an oop has any non-null oop fields
14141415
class PointsToOopsChecker : public BasicOopIterateClosure {
14151416
bool _result;
@@ -1425,9 +1426,7 @@ class PointsToOopsChecker : public BasicOopIterateClosure {
14251426
bool result() { return _result; }
14261427
};
14271428

1428-
HeapShared::CachedOopInfo HeapShared::make_cached_oop_info(oop obj) {
1429-
WalkOopAndArchiveClosure* walker = WalkOopAndArchiveClosure::current();
1430-
oop referrer = (walker == nullptr) ? nullptr : walker->referencing_obj();
1429+
HeapShared::CachedOopInfo HeapShared::make_cached_oop_info(oop obj, oop referrer) {
14311430
PointsToOopsChecker points_to_oops_checker;
14321431
obj->oop_iterate(&points_to_oops_checker);
14331432
return CachedOopInfo(referrer, points_to_oops_checker.result());
@@ -1450,12 +1449,35 @@ void HeapShared::init_box_classes(TRAPS) {
14501449
// (1) If orig_obj has not been archived yet, archive it.
14511450
// (2) If orig_obj has not been seen yet (since start_recording_subgraph() was called),
14521451
// trace all objects that are reachable from it, and make sure these objects are archived.
1453-
// (3) Record the klasses of all orig_obj and all reachable objects.
1452+
// (3) Record the klasses of all objects that are reachable from orig_obj (including those that
1453+
// were already archived when this function is called)
14541454
bool HeapShared::archive_reachable_objects_from(int level,
14551455
KlassSubGraphInfo* subgraph_info,
14561456
oop orig_obj) {
14571457
assert(orig_obj != nullptr, "must be");
1458+
PendingOopStack stack;
1459+
stack.push(PendingOop(orig_obj, nullptr, level));
1460+
1461+
while (stack.length() > 0) {
1462+
PendingOop po = stack.pop();
1463+
_object_being_archived = po;
1464+
bool status = walk_one_object(&stack, po.level(), subgraph_info, po.obj(), po.referrer());
1465+
_object_being_archived = PendingOop();
1466+
1467+
if (!status) {
1468+
// Don't archive a subgraph root that's too big. For archives static fields, that's OK
1469+
// as the Java code will take care of initializing this field dynamically.
1470+
assert(level == 1, "VM should have exited with unarchivable objects for _level > 1");
1471+
return false;
1472+
}
1473+
}
14581474

1475+
return true;
1476+
}
1477+
1478+
bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGraphInfo* subgraph_info,
1479+
oop orig_obj, oop referrer) {
1480+
assert(orig_obj != nullptr, "must be");
14591481
if (!JavaClasses::is_supported_for_archiving(orig_obj)) {
14601482
// This object has injected fields that cannot be supported easily, so we disallow them for now.
14611483
// If you get an error here, you probably made a change in the JDK library that has added
@@ -1524,7 +1546,7 @@ bool HeapShared::archive_reachable_objects_from(int level,
15241546
bool record_klasses_only = already_archived;
15251547
if (!already_archived) {
15261548
++_num_new_archived_objs;
1527-
if (!archive_object(orig_obj, subgraph_info)) {
1549+
if (!archive_object(orig_obj, referrer, subgraph_info)) {
15281550
// Skip archiving the sub-graph referenced from the current entry field.
15291551
ResourceMark rm;
15301552
log_error(cds, heap)(
@@ -1547,8 +1569,13 @@ bool HeapShared::archive_reachable_objects_from(int level,
15471569
Klass *orig_k = orig_obj->klass();
15481570
subgraph_info->add_subgraph_object_klass(orig_k);
15491571

1550-
WalkOopAndArchiveClosure walker(level, record_klasses_only, subgraph_info, orig_obj);
1551-
orig_obj->oop_iterate(&walker);
1572+
{
1573+
// Find all the oops that are referenced by orig_obj, push them onto the stack
1574+
// so we can work on them next.
1575+
ResourceMark rm;
1576+
ReferentPusher pusher(stack, level, record_klasses_only, subgraph_info, orig_obj);
1577+
orig_obj->oop_iterate(&pusher);
1578+
}
15521579

15531580
if (CDSConfig::is_initing_classes_at_dump_time()) {
15541581
// The enum klasses are archived with aot-initialized mirror.
@@ -1573,8 +1600,7 @@ bool HeapShared::archive_reachable_objects_from(int level,
15731600
// - No java.lang.Class instance (java mirror) can be included inside
15741601
// an archived sub-graph. Mirror can only be the sub-graph entry object.
15751602
//
1576-
// The Java heap object sub-graph archiving process (see
1577-
// WalkOopAndArchiveClosure):
1603+
// The Java heap object sub-graph archiving process (see ReferentPusher):
15781604
//
15791605
// 1) Java object sub-graph archiving starts from a given static field
15801606
// within a Class instance (java mirror). If the static field is a
@@ -1722,6 +1748,7 @@ void HeapShared::check_special_subgraph_classes() {
17221748
}
17231749

17241750
HeapShared::SeenObjectsTable* HeapShared::_seen_objects_table = nullptr;
1751+
HeapShared::PendingOop HeapShared::_object_being_archived;
17251752
int HeapShared::_num_new_walked_objs;
17261753
int HeapShared::_num_new_archived_objs;
17271754
int HeapShared::_num_old_recorded_klasses;
@@ -2043,10 +2070,11 @@ bool HeapShared::is_dumped_interned_string(oop o) {
20432070

20442071
void HeapShared::debug_trace() {
20452072
ResourceMark rm;
2046-
WalkOopAndArchiveClosure* walker = WalkOopAndArchiveClosure::current();
2047-
if (walker != nullptr) {
2073+
oop referrer = _object_being_archived.referrer();
2074+
if (referrer != nullptr) {
20482075
LogStream ls(Log(cds, heap)::error());
2049-
CDSHeapVerifier::trace_to_root(&ls, walker->referencing_obj());
2076+
ls.print_cr("Reference trace");
2077+
CDSHeapVerifier::trace_to_root(&ls, referrer);
20502078
}
20512079
}
20522080

src/hotspot/share/cds/heapShared.hpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ class HeapShared: AllStatic {
234234
static DumpTimeKlassSubGraphInfoTable* _dump_time_subgraph_info_table;
235235
static RunTimeKlassSubGraphInfoTable _run_time_subgraph_info_table;
236236

237-
static CachedOopInfo make_cached_oop_info(oop obj);
237+
static CachedOopInfo make_cached_oop_info(oop obj, oop referrer);
238238
static ArchivedKlassSubGraphInfoRecord* archive_subgraph_info(KlassSubGraphInfo* info);
239239
static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[],
240240
bool is_full_module_graph);
@@ -314,7 +314,7 @@ class HeapShared: AllStatic {
314314

315315
static bool has_been_seen_during_subgraph_recording(oop obj);
316316
static void set_has_been_seen_during_subgraph_recording(oop obj);
317-
static bool archive_object(oop obj, KlassSubGraphInfo* subgraph_info);
317+
static bool archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgraph_info);
318318

319319
static void resolve_classes_for_subgraphs(JavaThread* current, ArchivableStaticFieldInfo fields[]);
320320
static void resolve_classes_for_subgraph_of(JavaThread* current, Klass* k);
@@ -340,6 +340,30 @@ class HeapShared: AllStatic {
340340
static void archive_strings();
341341
static void archive_subgraphs();
342342

343+
// PendingOop and PendingOopStack are used for recursively discovering all cacheable
344+
// heap objects. The recursion is done using PendingOopStack so we won't overflow the
345+
// C stack with deep reference chains.
346+
class PendingOop {
347+
oop _obj;
348+
oop _referrer;
349+
int _level;
350+
351+
public:
352+
PendingOop() : _obj(nullptr), _referrer(nullptr), _level(-1) {}
353+
PendingOop(oop obj, oop referrer, int level) : _obj(obj), _referrer(referrer), _level(level) {}
354+
355+
oop obj() const { return _obj; }
356+
oop referrer() const { return _referrer; }
357+
int level() const { return _level; }
358+
};
359+
360+
class ReferentPusher;
361+
using PendingOopStack = GrowableArrayCHeap<PendingOop, mtClassShared>;
362+
363+
static PendingOop _object_being_archived;
364+
static bool walk_one_object(PendingOopStack* stack, int level, KlassSubGraphInfo* subgraph_info,
365+
oop orig_obj, oop referrer);
366+
343367
public:
344368
static void reset_archived_object_states(TRAPS);
345369
static void create_archived_object_cache() {

0 commit comments

Comments
 (0)