Skip to content

Commit 0a4c5a8

Browse files
committed
8347804: GenShen: Crash with small GCCardSizeInBytes and small Java heap
Reviewed-by: wkemper
1 parent f1398ec commit 0a4c5a8

File tree

5 files changed

+82
-23
lines changed

5 files changed

+82
-23
lines changed

src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "gc/shared/tlab_globals.hpp"
3030
#include "gc/shared/workerPolicy.hpp"
3131
#include "gc/shenandoah/shenandoahArguments.hpp"
32+
#include "gc/shenandoah/shenandoahCardTable.hpp"
3233
#include "gc/shenandoah/shenandoahCollectorPolicy.hpp"
3334
#include "gc/shenandoah/shenandoahGenerationalHeap.hpp"
3435
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
@@ -187,6 +188,11 @@ void ShenandoahArguments::initialize() {
187188
FLAG_SET_DEFAULT(TLABAllocationWeight, 90);
188189
}
189190

191+
if (GCCardSizeInBytes < ShenandoahMinCardSizeInBytes) {
192+
vm_exit_during_initialization(
193+
err_msg("GCCardSizeInBytes ( %u ) must be >= %u\n", GCCardSizeInBytes, (unsigned int) ShenandoahMinCardSizeInBytes));
194+
}
195+
190196
FullGCForwarding::initialize_flags(MaxHeapSize);
191197
}
192198

src/hotspot/share/gc/shenandoah/shenandoahCardTable.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "oops/oopsHierarchy.hpp"
3131
#include "utilities/macros.hpp"
3232

33+
#define ShenandoahMinCardSizeInBytes 128
34+
3335
class ShenandoahCardTable: public CardTable {
3436
friend class VMStructs;
3537

src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.cpp

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,6 @@ size_t ShenandoahRegionChunkIterator::calc_regular_group_size() {
727727
// half of the remaining heap, the third processes half of what remains and so on. The smallest chunk size
728728
// is represented by _smallest_chunk_size_words. We do not divide work any smaller than this.
729729
//
730-
731730
size_t group_size = _heap->num_regions() / 2;
732731
return group_size;
733732
}
@@ -773,6 +772,7 @@ size_t ShenandoahRegionChunkIterator::calc_num_groups() {
773772
// Any remaining regions will be treated as if they are part of the most recently created group. This group will
774773
// have more than _regular_group_size chunks within it.
775774
}
775+
assert (num_groups <= _maximum_groups, "Cannot have more than %zu groups", _maximum_groups);
776776
return num_groups;
777777
}
778778

@@ -784,21 +784,31 @@ size_t ShenandoahRegionChunkIterator::calc_total_chunks() {
784784
size_t current_group_span = _first_group_chunk_size_b4_rebalance * _regular_group_size;
785785
size_t smallest_group_span = smallest_chunk_size_words() * _regular_group_size;
786786

787-
// The first group gets special handling because the first chunk size can be no larger than _largest_chunk_size_words
787+
// The first group gets special handling because the first chunk size can be no larger than _maximum_chunk_size_words
788788
if (region_size_words > _maximum_chunk_size_words) {
789789
// In the case that we shrink the first group's chunk size, certain other groups will also be subsumed within the first group
790790
size_t effective_chunk_size = _first_group_chunk_size_b4_rebalance;
791+
uint coalesced_groups = 0;
791792
while (effective_chunk_size >= _maximum_chunk_size_words) {
793+
// Each iteration of this loop subsumes one original group into a new rebalanced initial group.
792794
num_chunks += current_group_span / _maximum_chunk_size_words;
793795
unspanned_heap_size -= current_group_span;
794796
effective_chunk_size /= 2;
795797
current_group_span /= 2;
798+
coalesced_groups++;
796799
}
800+
assert(effective_chunk_size * 2 == _maximum_chunk_size_words,
801+
"We assume _first_group_chunk_size_b4_rebalance is _maximum_chunk_size_words * a power of two");
802+
_largest_chunk_size_words = _maximum_chunk_size_words;
803+
_adjusted_num_groups = _num_groups - (coalesced_groups - 1);
797804
} else {
798805
num_chunks = _regular_group_size;
799806
unspanned_heap_size -= current_group_span;
807+
_largest_chunk_size_words = current_group_span / num_chunks;
808+
_adjusted_num_groups = _num_groups;
800809
current_group_span /= 2;
801810
}
811+
802812
size_t spanned_groups = 1;
803813
while (unspanned_heap_size > 0) {
804814
if (current_group_span <= unspanned_heap_size) {
@@ -856,11 +866,12 @@ ShenandoahRegionChunkIterator::ShenandoahRegionChunkIterator(ShenandoahHeap* hea
856866
size_t expected_chunk_size_words = _clusters_in_smallest_chunk * CardTable::card_size_in_words() * ShenandoahCardCluster::CardsPerCluster;
857867
assert(smallest_chunk_size_words() == expected_chunk_size_words, "_smallest_chunk_size (%zu) is not valid because it does not equal (%zu)",
858868
smallest_chunk_size_words(), expected_chunk_size_words);
859-
#endif
860869
assert(_num_groups <= _maximum_groups,
861870
"The number of remembered set scanning groups must be less than or equal to maximum groups");
862-
assert(smallest_chunk_size_words() << (_maximum_groups - 1) == _maximum_chunk_size_words,
863-
"Maximum number of groups needs to span maximum chunk size to smallest chunk size");
871+
assert(smallest_chunk_size_words() << (_adjusted_num_groups - 1) == _largest_chunk_size_words,
872+
"The number of groups (%zu) needs to span smallest chunk size (%zu) to largest chunk size (%zu)",
873+
_adjusted_num_groups, smallest_chunk_size_words(), _largest_chunk_size_words);
874+
#endif
864875

865876
size_t words_in_region = ShenandoahHeapRegion::region_size_words();
866877
_region_index[0] = 0;
@@ -883,7 +894,7 @@ ShenandoahRegionChunkIterator::ShenandoahRegionChunkIterator(ShenandoahHeap* hea
883894
}
884895

885896
size_t previous_group_span = _group_entries[0] * _group_chunk_size[0];
886-
for (size_t i = 1; i < _num_groups; i++) {
897+
for (size_t i = 1; i < _adjusted_num_groups; i++) {
887898
_group_chunk_size[i] = _group_chunk_size[i-1] / 2;
888899
size_t chunks_in_group = _regular_group_size;
889900
size_t this_group_span = _group_chunk_size[i] * chunks_in_group;
@@ -893,19 +904,19 @@ ShenandoahRegionChunkIterator::ShenandoahRegionChunkIterator(ShenandoahHeap* hea
893904
_group_entries[i] = _group_entries[i-1] + _regular_group_size;
894905
previous_group_span = total_span_of_groups;
895906
}
896-
if (_group_entries[_num_groups-1] < _total_chunks) {
897-
assert((_total_chunks - _group_entries[_num_groups-1]) * _group_chunk_size[_num_groups-1] + previous_group_span ==
907+
if (_group_entries[_adjusted_num_groups-1] < _total_chunks) {
908+
assert((_total_chunks - _group_entries[_adjusted_num_groups-1]) * _group_chunk_size[_adjusted_num_groups-1] + previous_group_span ==
898909
heap->num_regions() * words_in_region, "Total region chunks (%zu"
899910
") do not span total heap regions (%zu)", _total_chunks, _heap->num_regions());
900-
previous_group_span += (_total_chunks - _group_entries[_num_groups-1]) * _group_chunk_size[_num_groups-1];
901-
_group_entries[_num_groups-1] = _total_chunks;
911+
previous_group_span += (_total_chunks - _group_entries[_adjusted_num_groups-1]) * _group_chunk_size[_adjusted_num_groups-1];
912+
_group_entries[_adjusted_num_groups-1] = _total_chunks;
902913
}
903914
assert(previous_group_span == heap->num_regions() * words_in_region, "Total region chunks (%zu"
904915
") do not span total heap regions (%zu): %zu does not equal %zu",
905916
_total_chunks, _heap->num_regions(), previous_group_span, heap->num_regions() * words_in_region);
906917

907918
// Not necessary, but keeps things tidy
908-
for (size_t i = _num_groups; i < _maximum_groups; i++) {
919+
for (size_t i = _adjusted_num_groups; i < _maximum_groups; i++) {
909920
_region_index[i] = 0;
910921
_group_offset[i] = 0;
911922
_group_entries[i] = _group_entries[i-1];

src/hotspot/share/gc/shenandoah/shenandoahScanRemembered.hpp

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,14 @@ class ShenandoahDirectCardMarkRememberedSet: public CHeapObj<mtGC> {
196196

197197
// Use symbolic constants defined in cardTable.hpp
198198
// CardTable::card_shift = 9;
199-
// CardTable::card_size = 512;
200-
// CardTable::card_size_in_words = 64;
199+
// CardTable::card_size = 512; (default value 512, a power of 2 >= 128)
200+
// CardTable::card_size_in_words = 64; (default value 64, a power of 2 >= 16)
201201
// CardTable::clean_card_val()
202202
// CardTable::dirty_card_val()
203203

204+
// See shenandoahCardTable.hpp
205+
// ShenandoahMinCardSizeInBytes 128
206+
204207
const size_t LogCardValsPerIntPtr; // the number of card values (entries) in an intptr_t
205208
const size_t LogCardSizeInWords; // the size of a card in heap word units
206209

@@ -888,14 +891,14 @@ class ShenandoahRegionChunkIterator : public StackObj {
888891
// The largest chunk size is 4 MiB, measured in words. Otherwise, remembered set scanning may become too unbalanced.
889892
// If the largest chunk size is too small, there is too much overhead sifting out assignments to individual worker threads.
890893
static const size_t _maximum_chunk_size_words = (4 * 1024 * 1024) / HeapWordSize;
891-
892894
static const size_t _clusters_in_smallest_chunk = 4;
893895

896+
size_t _largest_chunk_size_words;
897+
894898
// smallest_chunk_size is 4 clusters. Each cluster spans 128 KiB.
895899
// This is computed from CardTable::card_size_in_words() * ShenandoahCardCluster::CardsPerCluster;
896900
static size_t smallest_chunk_size_words() {
897-
return _clusters_in_smallest_chunk * CardTable::card_size_in_words() *
898-
ShenandoahCardCluster::CardsPerCluster;
901+
return _clusters_in_smallest_chunk * CardTable::card_size_in_words() * ShenandoahCardCluster::CardsPerCluster;
899902
}
900903

901904
// The total remembered set scanning effort is divided into chunks of work that are assigned to individual worker tasks.
@@ -910,19 +913,30 @@ class ShenandoahRegionChunkIterator : public StackObj {
910913
// The first group "effectively" processes chunks of size 1 MiB (or smaller for smaller region sizes).
911914
// The last group processes chunks of size 128 KiB. There are four groups total.
912915

913-
// group[0] is 4 MiB chunk size (_maximum_chunk_size_words)
914-
// group[1] is 2 MiB chunk size
915-
// group[2] is 1 MiB chunk size
916-
// group[3] is 512 KiB chunk size
917-
// group[4] is 256 KiB chunk size
918-
// group[5] is 128 Kib shunk size (_smallest_chunk_size_words = 4 * 64 * 64
919-
static const size_t _maximum_groups = 6;
916+
// group[ 0] is 4 MiB chunk size (_maximum_chunk_size_words)
917+
// group[ 1] is 2 MiB chunk size
918+
// group[ 2] is 1 MiB chunk size
919+
// group[ 3] is 512 KiB chunk size
920+
// group[ 4] is 256 KiB chunk size
921+
// group[ 5] is 128 KiB chunk size
922+
// group[ 6] is 64 KiB chunk size
923+
// group[ 7] is 32 KiB chunk size
924+
// group[ 8] is 16 KiB chunk size
925+
// group[ 9] is 8 KiB chunk size
926+
// group[10] is 4 KiB chunk size
927+
// Note: 4 KiB is smallest possible chunk_size, computed from:
928+
// _clusters_in_smallest_chunk * MinimumCardSizeInWords * ShenandoahCardCluster::CardsPerCluster, which is
929+
// 4 * 16 * 64 = 4096
930+
931+
// We set aside arrays to represent the maximum number of groups that may be required for any heap configuration
932+
static const size_t _maximum_groups = 11;
920933

921934
const ShenandoahHeap* _heap;
922935

923936
const size_t _regular_group_size; // Number of chunks in each group
924937
const size_t _first_group_chunk_size_b4_rebalance;
925938
const size_t _num_groups; // Number of groups in this configuration
939+
size_t _adjusted_num_groups; // Rebalancing may coalesce groups
926940
const size_t _total_chunks;
927941

928942
shenandoah_padding(0);

test/hotspot/jtreg/gc/shenandoah/compiler/TestClone.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,32 @@
232232
* TestClone
233233
*/
234234

235+
/*
236+
* @test id=generational-small-card-size
237+
* @summary Test clone barriers work correctly
238+
* @requires vm.gc.Shenandoah
239+
*
240+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g
241+
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:GCCardSizeInBytes=128
242+
* TestClone
243+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g
244+
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:GCCardSizeInBytes=128
245+
* -Xint
246+
* TestClone
247+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g
248+
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:GCCardSizeInBytes=128
249+
* -XX:-TieredCompilation
250+
* TestClone
251+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g
252+
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:GCCardSizeInBytes=128
253+
* -XX:TieredStopAtLevel=1
254+
* TestClone
255+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xms1g -Xmx1g
256+
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:GCCardSizeInBytes=128
257+
* -XX:TieredStopAtLevel=4
258+
* TestClone
259+
*/
260+
235261
/*
236262
* @test id=generational-verify
237263
* @summary Test clone barriers work correctly

0 commit comments

Comments
 (0)