Skip to content

Commit 558c015

Browse files
author
Thomas Schatzl
committed
8351921: G1: Pinned regions with pinned objects only reachable by native code crash VM
Reviewed-by: ayang, iwalulya
1 parent f8c2122 commit 558c015

File tree

4 files changed

+116
-15
lines changed

4 files changed

+116
-15
lines changed

src/hotspot/share/gc/g1/g1Policy.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -660,13 +660,6 @@ void G1Policy::record_concurrent_refinement_stats(size_t pending_cards,
660660

661661
bool G1Policy::should_retain_evac_failed_region(uint index) const {
662662
size_t live_bytes = _g1h->region_at(index)->live_bytes();
663-
664-
#ifdef ASSERT
665-
G1HeapRegion* r = _g1h->region_at(index);
666-
assert(live_bytes != 0,
667-
"live bytes not set for %u used %zu garbage %zu cm-live %zu pinned %d",
668-
index, r->used(), r->garbage_bytes(), live_bytes, r->has_pinned_objects());
669-
#endif
670663
size_t threshold = G1RetainRegionLiveThresholdPercent * G1HeapRegion::GrainBytes / 100;
671664
return live_bytes < threshold;
672665
}

src/hotspot/share/gc/g1/g1YoungCollector.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ class G1PrepareEvacuationTask : public WorkerTask {
287287
class G1PrepareRegionsClosure : public G1HeapRegionClosure {
288288
G1CollectedHeap* _g1h;
289289
G1PrepareEvacuationTask* _parent_task;
290+
G1EvacFailureRegions* _evac_failure_regions;
291+
uint _worker_id;
290292
uint _worker_humongous_total;
291293
uint _worker_humongous_candidates;
292294

@@ -361,9 +363,14 @@ class G1PrepareEvacuationTask : public WorkerTask {
361363
}
362364

363365
public:
364-
G1PrepareRegionsClosure(G1CollectedHeap* g1h, G1PrepareEvacuationTask* parent_task) :
366+
G1PrepareRegionsClosure(G1CollectedHeap* g1h,
367+
G1PrepareEvacuationTask* parent_task,
368+
G1EvacFailureRegions* evac_failure_regions,
369+
uint worker_id) :
365370
_g1h(g1h),
366371
_parent_task(parent_task),
372+
_evac_failure_regions(evac_failure_regions),
373+
_worker_id(worker_id),
367374
_worker_humongous_total(0),
368375
_worker_humongous_candidates(0) { }
369376

@@ -373,6 +380,13 @@ class G1PrepareEvacuationTask : public WorkerTask {
373380
}
374381

375382
virtual bool do_heap_region(G1HeapRegion* hr) {
383+
// All pinned regions in the collection set must be registered as failed
384+
// regions here as there is no guarantee that there is a reference
385+
// reachable by Java code (i.e. only by native code).
386+
if (hr->in_collection_set() && hr->has_pinned_objects()) {
387+
_evac_failure_regions->record(_worker_id, hr->hrm_index(), true /* cause_pinned */);
388+
}
389+
376390
// First prepare the region for scanning
377391
_g1h->rem_set()->prepare_region_for_scan(hr);
378392

@@ -415,22 +429,25 @@ class G1PrepareEvacuationTask : public WorkerTask {
415429
};
416430

417431
G1CollectedHeap* _g1h;
432+
G1EvacFailureRegions* _evac_failure_regions;
418433
G1HeapRegionClaimer _claimer;
419434
volatile uint _humongous_total;
420435
volatile uint _humongous_candidates;
421436

422437
G1MonotonicArenaMemoryStats _all_card_set_stats;
423438

424439
public:
425-
G1PrepareEvacuationTask(G1CollectedHeap* g1h) :
440+
G1PrepareEvacuationTask(G1CollectedHeap* g1h, G1EvacFailureRegions* evac_failure_regions) :
426441
WorkerTask("Prepare Evacuation"),
427442
_g1h(g1h),
443+
_evac_failure_regions(evac_failure_regions),
428444
_claimer(_g1h->workers()->active_workers()),
429445
_humongous_total(0),
430-
_humongous_candidates(0) { }
446+
_humongous_candidates(0),
447+
_all_card_set_stats() { }
431448

432449
void work(uint worker_id) {
433-
G1PrepareRegionsClosure cl(_g1h, this);
450+
G1PrepareRegionsClosure cl(_g1h, this, _evac_failure_regions, worker_id);
434451
_g1h->heap_region_par_iterate_from_worker_offset(&cl, &_claimer, worker_id);
435452

436453
MutexLocker x(G1RareEvent_lock, Mutex::_no_safepoint_check_flag);
@@ -472,7 +489,8 @@ void G1YoungCollector::set_young_collection_default_active_worker_threads(){
472489
log_info(gc,task)("Using %u workers of %u for evacuation", active_workers, workers()->max_workers());
473490
}
474491

475-
void G1YoungCollector::pre_evacuate_collection_set(G1EvacInfo* evacuation_info) {
492+
void G1YoungCollector::pre_evacuate_collection_set(G1EvacInfo* evacuation_info,
493+
G1EvacFailureRegions* evac_failure_regions) {
476494
// Flush various data in thread-local buffers to be able to determine the collection
477495
// set
478496
{
@@ -511,7 +529,7 @@ void G1YoungCollector::pre_evacuate_collection_set(G1EvacInfo* evacuation_info)
511529
}
512530

513531
{
514-
G1PrepareEvacuationTask g1_prep_task(_g1h);
532+
G1PrepareEvacuationTask g1_prep_task(_g1h, evac_failure_regions);
515533
Tickspan task_time = run_task_timed(&g1_prep_task);
516534

517535
G1MonotonicArenaMemoryStats sampled_card_set_stats = g1_prep_task.all_card_set_stats();
@@ -1104,7 +1122,7 @@ void G1YoungCollector::collect() {
11041122
// other trivial setup above).
11051123
policy()->record_young_collection_start();
11061124

1107-
pre_evacuate_collection_set(jtm.evacuation_info());
1125+
pre_evacuate_collection_set(jtm.evacuation_info(), &_evac_failure_regions);
11081126

11091127
G1ParScanThreadStateSet per_thread_states(_g1h,
11101128
workers()->active_workers(),

src/hotspot/share/gc/g1/g1YoungCollector.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ class G1YoungCollector {
9595

9696
void set_young_collection_default_active_worker_threads();
9797

98-
void pre_evacuate_collection_set(G1EvacInfo* evacuation_info);
98+
void pre_evacuate_collection_set(G1EvacInfo* evacuation_info,
99+
G1EvacFailureRegions* evac_failure_regions);
99100
// Actually do the work of evacuating the parts of the collection set.
100101
// The has_optional_evacuation_work flag for the initial collection set
101102
// evacuation indicates whether one or more optional evacuation steps may
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/* @test
25+
* @summary Test that pinned regions with no Java references into them
26+
* do not make the garbage collector reclaim that region.
27+
* This test simulates this behavior using Whitebox/Unsafe methods
28+
* to pin a Java object in a region with no other pinnable objects and
29+
* lose the reference to it before the garbage collection.
30+
* @requires vm.gc.G1
31+
* @requires vm.debug
32+
* @library /test/lib
33+
* @modules java.base/jdk.internal.misc:+open
34+
* java.management
35+
* @build jdk.test.whitebox.WhiteBox
36+
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
37+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
38+
* -Xbootclasspath/a:. -Xlog:gc=debug,gc+ergo+cset=trace,gc+phases=debug -XX:G1HeapRegionSize=1m -Xms30m gc.g1.pinnedobjs.TestPinnedEvacEmpty
39+
*/
40+
41+
package gc.g1.pinnedobjs;
42+
43+
import jdk.internal.misc.Unsafe;
44+
45+
import jdk.test.lib.Asserts;
46+
import jdk.test.lib.process.OutputAnalyzer;
47+
import jdk.test.lib.process.ProcessTools;
48+
import jdk.test.whitebox.WhiteBox;
49+
50+
public class TestPinnedEvacEmpty {
51+
52+
private static final jdk.internal.misc.Unsafe unsafe = Unsafe.getUnsafe();
53+
private static final WhiteBox wb = WhiteBox.getWhiteBox();
54+
55+
private static final long objSize = wb.getObjectSize(new Object());
56+
57+
// How many j.l.Object should we allocate when creating garbage.
58+
private static final long numAllocations = 1024 * 1024 * 3 / objSize;
59+
60+
public static void main(String[] args) throws Exception {
61+
// Remove garbage from VM initialization.
62+
wb.fullGC();
63+
64+
// Allocate garbage so that the target object will be in a new region.
65+
for (int i = 0; i < numAllocations; i++) {
66+
Object z = new Object();
67+
}
68+
int[] o = new int[100]; // The target object to pin.
69+
// Further allocate garbage so that any additional allocations of potentially
70+
// pinned objects can not be allocated in the same region as the target object.
71+
for (int i = 0; i < numAllocations; i++) {
72+
Object z = new Object();
73+
}
74+
75+
Asserts.assertTrue(!wb.isObjectInOldGen(o), "should not be in old gen already");
76+
77+
// Pin the object.
78+
wb.pinObject(o);
79+
80+
// And forget the (Java) reference to the int array. After this, the garbage
81+
// collection should find a completely empty pinned region. The collector
82+
// must not collect/free it.
83+
o = null;
84+
85+
// Do garbage collection to zap the data in the pinned region.
86+
wb.youngGC();
87+
}
88+
}
89+

0 commit comments

Comments
 (0)