Skip to content

Commit b8244b6

Browse files
committed
8236926: Concurrently uncommit memory in G1
Reviewed-by: ayang, tschatzl
1 parent defdd12 commit b8244b6

25 files changed

+1409
-177
lines changed

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include "gc/g1/g1Trace.hpp"
6565
#include "gc/g1/g1YCTypes.hpp"
6666
#include "gc/g1/g1ServiceThread.hpp"
67+
#include "gc/g1/g1UncommitRegionTask.hpp"
6768
#include "gc/g1/g1VMOperations.hpp"
6869
#include "gc/g1/heapRegion.inline.hpp"
6970
#include "gc/g1/heapRegionRemSet.hpp"
@@ -743,7 +744,7 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) {
743744
HeapWord* prev_last_addr = NULL;
744745
HeapRegion* prev_last_region = NULL;
745746
size_t size_used = 0;
746-
size_t uncommitted_regions = 0;
747+
uint shrink_count = 0;
747748

748749
// For each Memregion, free the G1 regions that constitute it, and
749750
// notify mark-sweep that the range is no longer to be considered 'archive.'
@@ -792,14 +793,17 @@ void G1CollectedHeap::dealloc_archive_regions(MemRegion* ranges, size_t count) {
792793
} else {
793794
curr_region = NULL;
794795
}
796+
795797
_hrm.shrink_at(curr_index, 1);
796-
uncommitted_regions++;
798+
shrink_count++;
797799
}
798800
}
799801

800-
if (uncommitted_regions != 0) {
801-
log_debug(gc, ergo, heap)("Attempt heap shrinking (uncommitted archive regions). Total size: " SIZE_FORMAT "B",
802-
HeapRegion::GrainWords * HeapWordSize * uncommitted_regions);
802+
if (shrink_count != 0) {
803+
log_debug(gc, ergo, heap)("Attempt heap shrinking (archive regions). Total size: " SIZE_FORMAT "B",
804+
HeapRegion::GrainWords * HeapWordSize * shrink_count);
805+
// Explicit uncommit.
806+
uncommit_regions(shrink_count);
803807
}
804808
decrease_used(size_used);
805809
}
@@ -1015,6 +1019,7 @@ void G1CollectedHeap::prepare_heap_for_mutators() {
10151019
rebuild_region_sets(false /* free_list_only */);
10161020
abort_refinement();
10171021
resize_heap_if_necessary();
1022+
uncommit_regions_if_necessary();
10181023

10191024
// Rebuild the strong code root lists for each region
10201025
rebuild_strong_code_roots();
@@ -1294,6 +1299,7 @@ void G1CollectedHeap::shrink_helper(size_t shrink_bytes) {
12941299
log_debug(gc, ergo, heap)("Shrink the heap. requested shrinking amount: " SIZE_FORMAT "B aligned shrinking amount: " SIZE_FORMAT "B attempted shrinking amount: " SIZE_FORMAT "B",
12951300
shrink_bytes, aligned_shrink_bytes, shrunk_bytes);
12961301
if (num_regions_removed > 0) {
1302+
log_debug(gc, heap)("Uncommittable regions after shrink: %u", num_regions_removed);
12971303
policy()->record_new_heap_size(num_regions());
12981304
} else {
12991305
log_debug(gc, ergo, heap)("Did not expand the heap (heap shrinking operation failed)");
@@ -2611,6 +2617,20 @@ void G1CollectedHeap::gc_epilogue(bool full) {
26112617
_collection_pause_end = Ticks::now();
26122618
}
26132619

2620+
uint G1CollectedHeap::uncommit_regions(uint region_limit) {
2621+
return _hrm.uncommit_inactive_regions(region_limit);
2622+
}
2623+
2624+
bool G1CollectedHeap::has_uncommittable_regions() {
2625+
return _hrm.has_inactive_regions();
2626+
}
2627+
2628+
void G1CollectedHeap::uncommit_regions_if_necessary() {
2629+
if (has_uncommittable_regions()) {
2630+
G1UncommitRegionTask::enqueue();
2631+
}
2632+
}
2633+
26142634
void G1CollectedHeap::verify_numa_regions(const char* desc) {
26152635
LogTarget(Trace, gc, heap, verify) lt;
26162636

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,12 @@ class G1CollectedHeap : public CollectedHeap {
563563

564564
void resize_heap_if_necessary();
565565

566+
// Check if there is memory to uncommit and if so schedule a task to do it.
567+
void uncommit_regions_if_necessary();
568+
// Immediately uncommit uncommittable regions.
569+
uint uncommit_regions(uint region_limit);
570+
bool has_uncommittable_regions();
571+
566572
G1NUMA* numa() const { return _numa; }
567573

568574
// Expand the garbage-first heap by at least the given size (in bytes!).
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/*
2+
* Copyright (c) 2020, 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+
25+
#include "precompiled.hpp"
26+
#include "gc/g1/g1CommittedRegionMap.inline.hpp"
27+
#include "logging/log.hpp"
28+
#include "memory/universe.hpp"
29+
#include "runtime/mutexLocker.hpp"
30+
#include "runtime/safepoint.hpp"
31+
#include "utilities/debug.hpp"
32+
33+
HeapRegionRange::HeapRegionRange(uint start, uint end) :
34+
_start(start),
35+
_end(end) {
36+
assert(start <= end, "Invariant");
37+
}
38+
39+
G1CommittedRegionMap::G1CommittedRegionMap() :
40+
_active(mtGC),
41+
_inactive(mtGC),
42+
_num_active(0),
43+
_num_inactive(0) { }
44+
45+
void G1CommittedRegionMap::initialize(uint num_regions) {
46+
_active.initialize(num_regions);
47+
_inactive.initialize(num_regions);
48+
}
49+
50+
uint G1CommittedRegionMap::num_active() const {
51+
return _num_active;
52+
}
53+
54+
uint G1CommittedRegionMap::num_inactive() const {
55+
return _num_inactive;
56+
}
57+
58+
uint G1CommittedRegionMap::max_length() const {
59+
return (uint) _active.size();
60+
}
61+
62+
void G1CommittedRegionMap::activate(uint start, uint end) {
63+
verify_active_count(start, end, 0);
64+
verify_inactive_count(start, end, 0);
65+
66+
log_debug(gc, heap, region)("Activate regions [%u, %u)", start, end);
67+
68+
active_set_range(start, end);
69+
}
70+
71+
void G1CommittedRegionMap::reactivate(uint start, uint end) {
72+
verify_active_count(start, end, 0);
73+
verify_inactive_count(start, end, (end - start));
74+
75+
log_debug(gc, heap, region)("Reactivate regions [%u, %u)", start, end);
76+
77+
active_set_range(start, end);
78+
inactive_clear_range(start, end);
79+
}
80+
81+
void G1CommittedRegionMap::deactivate(uint start, uint end) {
82+
verify_active_count(start, end, (end - start));
83+
verify_inactive_count(start, end, 0);
84+
85+
log_debug(gc, heap, region)("Deactivate regions [%u, %u)", start, end);
86+
87+
active_clear_range(start, end);
88+
inactive_set_range(start, end);
89+
}
90+
91+
void G1CommittedRegionMap::uncommit(uint start, uint end) {
92+
verify_active_count(start, end, 0);
93+
verify_inactive_count(start, end, (end-start));
94+
95+
log_debug(gc, heap, region)("Uncommit regions [%u, %u)", start, end);
96+
97+
inactive_clear_range(start, end);
98+
}
99+
100+
HeapRegionRange G1CommittedRegionMap::next_active_range(uint offset) const {
101+
// Find first active index from offset.
102+
uint start = (uint) _active.get_next_one_offset(offset);
103+
if (start == max_length()) {
104+
// Early out when no active regions are found.
105+
return HeapRegionRange(max_length(), max_length());
106+
}
107+
108+
uint end = (uint) _active.get_next_zero_offset(start);
109+
verify_active_range(start, end);
110+
111+
return HeapRegionRange(start, end);
112+
}
113+
114+
HeapRegionRange G1CommittedRegionMap::next_committable_range(uint offset) const {
115+
// We should only call this function when there are no inactive regions.
116+
verify_no_inactive_regons();
117+
118+
// Find first free region from offset.
119+
uint start = (uint) _active.get_next_zero_offset(offset);
120+
if (start == max_length()) {
121+
// Early out when no free regions are found.
122+
return HeapRegionRange(max_length(), max_length());
123+
}
124+
125+
uint end = (uint) _active.get_next_one_offset(start);
126+
verify_free_range(start, end);
127+
128+
return HeapRegionRange(start, end);
129+
}
130+
131+
HeapRegionRange G1CommittedRegionMap::next_inactive_range(uint offset) const {
132+
// Find first inactive region from offset.
133+
uint start = (uint) _inactive.get_next_one_offset(offset);
134+
135+
if (start == max_length()) {
136+
// Early when no inactive regions are found.
137+
return HeapRegionRange(max_length(), max_length());
138+
}
139+
140+
uint end = (uint) _inactive.get_next_zero_offset(start);
141+
verify_inactive_range(start, end);
142+
143+
return HeapRegionRange(start, end);
144+
}
145+
146+
void G1CommittedRegionMap::active_set_range(uint start, uint end) {
147+
guarantee_mt_safety_active();
148+
149+
_active.par_set_range(start, end, BitMap::unknown_range);
150+
_num_active += (end - start);
151+
}
152+
153+
void G1CommittedRegionMap::active_clear_range(uint start, uint end) {
154+
guarantee_mt_safety_active();
155+
156+
_active.par_clear_range(start, end, BitMap::unknown_range);
157+
_num_active -= (end - start);
158+
}
159+
160+
void G1CommittedRegionMap::inactive_set_range(uint start, uint end) {
161+
guarantee_mt_safety_inactive();
162+
163+
_inactive.par_set_range(start, end, BitMap::unknown_range);
164+
_num_inactive += (end - start);
165+
}
166+
167+
void G1CommittedRegionMap::inactive_clear_range(uint start, uint end) {
168+
guarantee_mt_safety_inactive();
169+
170+
_inactive.par_clear_range(start, end, BitMap::unknown_range);
171+
_num_inactive -= (end - start);
172+
}
173+
174+
void G1CommittedRegionMap::guarantee_mt_safety_active() const {
175+
// G1CommittedRegionMap _active-map MT safety protocol:
176+
// (a) If we're at a safepoint, the caller must either be the VM thread or
177+
// hold the FreeList_lock.
178+
// (b) If we're not at a safepoint, the caller must hold the Heap_lock.
179+
// Protocol only applies after initialization is complete.
180+
181+
if (!Universe::is_fully_initialized()) {
182+
return;
183+
}
184+
185+
if (SafepointSynchronize::is_at_safepoint()) {
186+
guarantee(Thread::current()->is_VM_thread() ||
187+
FreeList_lock->owned_by_self(),
188+
"G1CommittedRegionMap _active-map MT safety protocol at a safepoint");
189+
} else {
190+
guarantee(Heap_lock->owned_by_self(),
191+
"G1CommittedRegionMap _active-map MT safety protocol outside a safepoint");
192+
}
193+
}
194+
195+
void G1CommittedRegionMap::guarantee_mt_safety_inactive() const {
196+
// G1CommittedRegionMap _inactive-map MT safety protocol:
197+
// (a) If we're at a safepoint, the caller must either be the VM thread or
198+
// hold the FreeList_lock.
199+
// (b) If we're not at a safepoint, the caller must hold the Uncommit_lock.
200+
// Protocol only applies after initialization is complete.
201+
202+
if (!Universe::is_fully_initialized()) {
203+
return;
204+
}
205+
206+
if (SafepointSynchronize::is_at_safepoint()) {
207+
guarantee(Thread::current()->is_VM_thread() ||
208+
FreeList_lock->owned_by_self(),
209+
"G1CommittedRegionMap MT safety protocol at a safepoint");
210+
} else {
211+
guarantee(Uncommit_lock->owned_by_self(),
212+
"G1CommittedRegionMap MT safety protocol outside a safepoint");
213+
}
214+
}
215+
216+
#ifdef ASSERT
217+
void G1CommittedRegionMap::verify_active_range(uint start, uint end) const {
218+
assert(active(start), "First region (%u) is not active", start);
219+
assert(active(end - 1), "Last region (%u) is not active", end - 1);
220+
assert(end == _active.size() || !active(end), "Region (%u) is active but not included in range", end);
221+
}
222+
223+
void G1CommittedRegionMap::verify_inactive_range(uint start, uint end) const {
224+
assert(inactive(start), "First region (%u) is not inactive", start);
225+
assert(inactive(end - 1), "Last region (%u) in range is not inactive", end - 1);
226+
assert(end == _inactive.size() || !inactive(end), "Region (%u) is inactive but not included in range", end);
227+
}
228+
229+
void G1CommittedRegionMap::verify_free_range(uint start, uint end) const {
230+
assert(!active(start), "First region (%u) is active", start);
231+
assert(!active(end - 1), "Last region (%u) in range is active", end - 1);
232+
}
233+
234+
void G1CommittedRegionMap::verify_no_inactive_regons() const {
235+
BitMap::idx_t first_inactive = _inactive.get_next_one_offset(0);
236+
assert(first_inactive == _inactive.size(), "Should be no inactive regions, but was at index: " SIZE_FORMAT, first_inactive);
237+
}
238+
239+
void G1CommittedRegionMap::verify_active_count(uint start, uint end, uint expected) const {
240+
uint found = (uint) _active.count_one_bits(start, end);
241+
assert(found == expected, "Unexpected number of active regions, found: %u, expected: %u", found, expected);
242+
}
243+
244+
void G1CommittedRegionMap::verify_inactive_count(uint start, uint end, uint expected) const {
245+
uint found = (uint) _inactive.count_one_bits(start, end);
246+
assert(found == expected, "Unexpected number of inactive regions, found: %u, expected: %u", found, expected);
247+
}
248+
249+
#endif //ASSERT

0 commit comments

Comments
 (0)