mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-23 21:30:26 +00:00
8379531: Shenandoah: Allow safepoint preemption during allocation of very large arrays
Reviewed-by: wkemper, kdnilsen
This commit is contained in:
parent
7c4c04486f
commit
1536c8233f
@ -300,7 +300,11 @@ void ShenandoahConcurrentMark::finish_mark_work() {
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
if (!generation()->is_old() && heap->is_concurrent_young_mark_in_progress()) {
|
||||
// Lastly, ensure all the invisible roots are marked.
|
||||
ShenandoahInvisibleRootsMarkClosure cl;
|
||||
Threads::java_threads_do(&cl);
|
||||
}
|
||||
|
||||
assert(task_queues()->is_empty(), "Should be empty");
|
||||
}
|
||||
|
||||
@ -284,15 +284,6 @@ void ShenandoahGeneration::prepare_regions_and_collection_set(bool concurrent) {
|
||||
// along with the census done during marking, and compute the tenuring threshold.
|
||||
ShenandoahAgeCensus* census = ShenandoahGenerationalHeap::heap()->age_census();
|
||||
census->update_census(age0_pop);
|
||||
#ifndef PRODUCT
|
||||
size_t total_pop = age0_cl.get_total_population();
|
||||
size_t total_census = census->get_total();
|
||||
// Usually total_pop > total_census, but not by too much.
|
||||
// We use integer division so anything up to just less than 2 is considered
|
||||
// reasonable, and the "+1" is to avoid divide-by-zero.
|
||||
assert((total_pop+1)/(total_census+1) == 1, "Extreme divergence: "
|
||||
"%zu/%zu", total_pop, total_census);
|
||||
#endif
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@ -62,6 +62,7 @@
|
||||
#include "gc/shenandoah/shenandoahMarkingContext.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahMemoryPool.hpp"
|
||||
#include "gc/shenandoah/shenandoahMonitoringSupport.hpp"
|
||||
#include "gc/shenandoah/shenandoahObjArrayAllocator.hpp"
|
||||
#include "gc/shenandoah/shenandoahOldGeneration.hpp"
|
||||
#include "gc/shenandoah/shenandoahPadding.hpp"
|
||||
#include "gc/shenandoah/shenandoahParallelCleaning.inline.hpp"
|
||||
@ -1073,6 +1074,11 @@ HeapWord* ShenandoahHeap::mem_allocate(size_t size) {
|
||||
return allocate_memory(req);
|
||||
}
|
||||
|
||||
oop ShenandoahHeap::array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS) {
|
||||
ShenandoahObjArrayAllocator allocator(klass, size, length, do_zero, THREAD);
|
||||
return allocator.allocate();
|
||||
}
|
||||
|
||||
MetaWord* ShenandoahHeap::satisfy_failed_metadata_allocation(ClassLoaderData* loader_data,
|
||||
size_t size,
|
||||
Metaspace::MetadataType mdtype) {
|
||||
|
||||
@ -709,6 +709,7 @@ private:
|
||||
public:
|
||||
HeapWord* allocate_memory(ShenandoahAllocRequest& request);
|
||||
HeapWord* mem_allocate(size_t size) override;
|
||||
oop array_allocate(Klass* klass, size_t size, int length, bool do_zero, TRAPS) override;
|
||||
MetaWord* satisfy_failed_metadata_allocation(ClassLoaderData* loader_data,
|
||||
size_t size,
|
||||
Metaspace::MetadataType mdtype) override;
|
||||
|
||||
127
src/hotspot/share/gc/shenandoah/shenandoahObjArrayAllocator.cpp
Normal file
127
src/hotspot/share/gc/shenandoah/shenandoahObjArrayAllocator.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gc/shared/memAllocator.hpp"
|
||||
#include "gc/shenandoah/shenandoahBarrierSet.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
|
||||
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
|
||||
#include "gc/shenandoah/shenandoahObjArrayAllocator.hpp"
|
||||
#include "gc/shenandoah/shenandoahThreadLocalData.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
#include "oops/arrayKlass.hpp"
|
||||
#include "oops/arrayOop.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "utilities/copy.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
|
||||
ShenandoahObjArrayAllocator::ShenandoahObjArrayAllocator(
|
||||
Klass* klass, size_t word_size, int length, bool do_zero, Thread* thread)
|
||||
: ObjArrayAllocator(klass, word_size, length, do_zero, thread) {
|
||||
assert(_length >= 0, "length should be non-negative");
|
||||
}
|
||||
|
||||
oop ShenandoahObjArrayAllocator::initialize(HeapWord* mem) const {
|
||||
// threshold of object size, while above this size current mutator will yield to safepoint
|
||||
// when it clears the array content.
|
||||
constexpr size_t THRESHOLD = 64 * K / BytesPerWord;
|
||||
|
||||
ShenandoahHeap* const heap = ShenandoahHeap::heap();
|
||||
|
||||
// Fast path: delegate to base class for small arrays or no-zero case.
|
||||
// In no-zero case(_do_zero is false), the content of the array won't be zeroed, therefore no need to fall into slow-path.
|
||||
if (!_do_zero || _word_size <= THRESHOLD) {
|
||||
return ObjArrayAllocator::initialize(mem);
|
||||
}
|
||||
|
||||
// Slow path: yield to safepoint when clearing for large arrays
|
||||
|
||||
// Compute clearing bounds
|
||||
const BasicType element_type = ArrayKlass::cast(_klass)->element_type();
|
||||
const size_t base_offset_in_bytes = (size_t)arrayOopDesc::base_offset_in_bytes(element_type);
|
||||
const size_t process_start_offset_in_bytes = align_up(base_offset_in_bytes, (size_t)BytesPerWord);
|
||||
|
||||
const size_t process_start = process_start_offset_in_bytes / BytesPerWord;
|
||||
const size_t process_size = _word_size - process_start;
|
||||
|
||||
// Pin the region before clearing to avoid moving the object until it is done
|
||||
ShenandoahHeapRegion* region = heap->heap_region_containing(mem);
|
||||
region->record_pin();
|
||||
|
||||
// Always initialize the mem with primitive array first so GC won't look into the elements in the array.
|
||||
// For obj array, the header will be corrected to object array after clearing the memory.
|
||||
Klass* filling_klass = _klass;
|
||||
int filling_array_length = _length;
|
||||
const bool is_ref_type = is_reference_type(element_type, true);
|
||||
|
||||
if (is_ref_type) {
|
||||
const bool is_narrow_oop = element_type == T_NARROWOOP;
|
||||
size_t filling_element_byte_size = is_narrow_oop ? T_INT_aelem_bytes : T_LONG_aelem_bytes;
|
||||
filling_klass = is_narrow_oop ? Universe::intArrayKlass() : Universe::longArrayKlass();
|
||||
filling_array_length = (int) ((process_size << LogBytesPerWord) / filling_element_byte_size);
|
||||
}
|
||||
ObjArrayAllocator filling_array_allocator(filling_klass, _word_size, filling_array_length , /* do_zero */ false);
|
||||
filling_array_allocator.initialize(mem);
|
||||
|
||||
// Invisible roots will be scanned and marked at the end of marking.
|
||||
ShenandoahThreadLocalData::set_invisible_root(_thread, mem, _word_size);
|
||||
|
||||
{
|
||||
// The mem has been initialized as primitive array, the entire clearing work is safe for safepoint
|
||||
ThreadBlockInVM tbivm(JavaThread::cast(_thread)); // Allow safepoint to proceed.
|
||||
// Handle potential 4-byte alignment gap before array data
|
||||
if (process_start_offset_in_bytes != base_offset_in_bytes) {
|
||||
assert(process_start_offset_in_bytes - base_offset_in_bytes == 4, "Must be 4-byte aligned");
|
||||
*reinterpret_cast<int*>(reinterpret_cast<char*>(mem) + base_offset_in_bytes) = 0;
|
||||
}
|
||||
|
||||
Copy::zero_to_words(mem + process_start, process_size);
|
||||
|
||||
if (!is_ref_type) {
|
||||
// zap paddings
|
||||
mem_zap_start_padding(mem);
|
||||
mem_zap_end_padding(mem);
|
||||
}
|
||||
}
|
||||
|
||||
// reference array, header need to be overridden to its own.
|
||||
if (is_ref_type) {
|
||||
arrayOopDesc::set_length(mem, _length);
|
||||
finish(mem);
|
||||
// zap paddings after setting correct klass
|
||||
mem_zap_start_padding(mem);
|
||||
mem_zap_end_padding(mem);
|
||||
}
|
||||
|
||||
oop arrayObj = cast_to_oop(mem);
|
||||
if (heap->is_concurrent_young_mark_in_progress() && !heap->marking_context()->allocated_after_mark_start(arrayObj)) {
|
||||
// Keep the obj alive because we don't know the progress of marking,
|
||||
// current concurrent marking could have done and VM is calling safepoint for final mark.
|
||||
heap->keep_alive(arrayObj);
|
||||
}
|
||||
ShenandoahThreadLocalData::clear_invisible_root(_thread);
|
||||
|
||||
region->record_unpin();
|
||||
|
||||
return arrayObj;
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHOBJARRAYALLOCATOR_HPP
|
||||
#define SHARE_GC_SHENANDOAH_SHENANDOAHOBJARRAYALLOCATOR_HPP
|
||||
|
||||
#include "gc/shared/memAllocator.hpp"
|
||||
|
||||
class ShenandoahObjArrayAllocator : public ObjArrayAllocator {
|
||||
private:
|
||||
// Override: clearing with safepoint yields for large arrays
|
||||
oop initialize(HeapWord* mem) const override;
|
||||
|
||||
public:
|
||||
ShenandoahObjArrayAllocator(Klass* klass, size_t word_size, int length,
|
||||
bool do_zero, Thread* thread);
|
||||
};
|
||||
|
||||
#endif // SHARE_GC_SHENANDOAH_SHENANDOAHOBJARRAYALLOCATOR_HPP
|
||||
@ -141,6 +141,49 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class ShenandoahInvisibleRootsMarkClosure : public ThreadClosure {
|
||||
public:
|
||||
void do_thread(Thread* t) {
|
||||
assert_at_safepoint();
|
||||
|
||||
HeapWord* invisible_root = ShenandoahThreadLocalData::get_invisible_root(t);
|
||||
if (invisible_root == nullptr) {
|
||||
return;
|
||||
}
|
||||
size_t invisible_root_word_size = ShenandoahThreadLocalData::get_invisible_root_word_size(t);
|
||||
|
||||
ShenandoahHeap* const heap = ShenandoahHeap::heap();
|
||||
ShenandoahMarkingContext* const marking_context = heap->marking_context();
|
||||
// Mark the invisible root if it is not marked.
|
||||
if (!marking_context->is_marked(invisible_root)) {
|
||||
bool was_upgraded = false;
|
||||
if (!marking_context->mark_strong(cast_to_oop(invisible_root), was_upgraded)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update region liveness data
|
||||
ShenandoahHeapRegion* region = heap->heap_region_containing(invisible_root);
|
||||
if (region->is_regular() || region->is_regular_pinned()) {
|
||||
assert(!ShenandoahHeapRegion::requires_humongous(invisible_root_word_size), "Must not be humongous.");
|
||||
region->increase_live_data_alloc_words(invisible_root_word_size);
|
||||
} else if (region->is_humongous_start()) {
|
||||
DEBUG_ONLY(size_t total_live_words = 0;)
|
||||
do {
|
||||
size_t current = region->get_live_data_words();
|
||||
size_t region_used_words = region->used() >> LogHeapWordSize;
|
||||
DEBUG_ONLY(total_live_words += region_used_words;)
|
||||
assert(current == 0 || current == region_used_words, "Must be");
|
||||
if (current == 0) {
|
||||
region->increase_live_data_alloc_words(region_used_words);
|
||||
}
|
||||
region = heap->get_region(region->index() + 1);
|
||||
} while (region != nullptr && region->is_humongous_continuation());
|
||||
assert(total_live_words == invisible_root_word_size, "Must be");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The rationale for selecting the roots to scan is as follows:
|
||||
// a. With unload_classes = true, we only want to scan the actual strong roots from the
|
||||
// code cache. This will allow us to identify the dead classes, unload them, *and*
|
||||
|
||||
@ -104,6 +104,12 @@ void ShenandoahSTWMark::mark() {
|
||||
heap->workers()->run_task(&task);
|
||||
|
||||
assert(task_queues()->is_empty(), "Should be empty");
|
||||
|
||||
if (!generation()->is_old()) {
|
||||
// Lastly, ensure all the invisible roots are marked.
|
||||
ShenandoahInvisibleRootsMarkClosure cl;
|
||||
Threads::java_threads_do(&cl);
|
||||
}
|
||||
}
|
||||
|
||||
_generation->set_mark_complete();
|
||||
|
||||
@ -38,7 +38,9 @@ ShenandoahThreadLocalData::ShenandoahThreadLocalData() :
|
||||
_gclab(nullptr),
|
||||
_gclab_size(0),
|
||||
_shenandoah_plab(nullptr),
|
||||
_evacuation_stats(new ShenandoahEvacuationStats()) {
|
||||
_evacuation_stats(new ShenandoahEvacuationStats()),
|
||||
_invisible_root(nullptr),
|
||||
_invisible_root_word_size(0) {
|
||||
}
|
||||
|
||||
ShenandoahThreadLocalData::~ShenandoahThreadLocalData() {
|
||||
|
||||
@ -67,6 +67,9 @@ private:
|
||||
|
||||
ShenandoahEvacuationStats* _evacuation_stats;
|
||||
|
||||
Atomic<HeapWord*> _invisible_root;
|
||||
Atomic<size_t> _invisible_root_word_size;
|
||||
|
||||
ShenandoahThreadLocalData();
|
||||
~ShenandoahThreadLocalData();
|
||||
|
||||
@ -206,6 +209,25 @@ public:
|
||||
static ByteSize card_table_offset() {
|
||||
return Thread::gc_data_offset() + byte_offset_of(ShenandoahThreadLocalData, _card_table);
|
||||
}
|
||||
|
||||
// invisible root are the partially initialized obj array set by ShenandoahObjArrayAllocator
|
||||
static void set_invisible_root(Thread* thread, HeapWord* invisible_root, size_t word_size) {
|
||||
data(thread)->_invisible_root.store_relaxed(invisible_root);
|
||||
data(thread)->_invisible_root_word_size.store_relaxed(word_size);
|
||||
}
|
||||
|
||||
static void clear_invisible_root(Thread* thread) {
|
||||
data(thread)->_invisible_root.store_relaxed(nullptr);
|
||||
data(thread)->_invisible_root_word_size.store_relaxed(0);
|
||||
}
|
||||
|
||||
static HeapWord* get_invisible_root(Thread* thread) {
|
||||
return data(thread)->_invisible_root.load_relaxed();
|
||||
}
|
||||
|
||||
static size_t get_invisible_root_word_size(Thread* thread) {
|
||||
return data(thread)->_invisible_root_word_size.load_relaxed();
|
||||
}
|
||||
};
|
||||
|
||||
STATIC_ASSERT(sizeof(ShenandoahThreadLocalData) <= sizeof(GCThreadLocalData));
|
||||
|
||||
128
test/hotspot/jtreg/gc/shenandoah/TestLargeArrayInit.java
Normal file
128
test/hotspot/jtreg/gc/shenandoah/TestLargeArrayInit.java
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=adaptive
|
||||
* @summary Verify zero-initialization completeness for large arrays under Shenandoah adaptive mode
|
||||
* @requires vm.gc.Shenandoah
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx512m -Xms512m
|
||||
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive
|
||||
* TestLargeArrayInit
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=generational
|
||||
* @summary Verify zero-initialization completeness for large arrays under Shenandoah generational mode
|
||||
* @requires vm.gc.Shenandoah
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx512m -Xms512m
|
||||
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational
|
||||
* TestLargeArrayInit
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=compact-on
|
||||
* @summary Verify zero-initialization completeness for large arrays with compact object headers enabled
|
||||
* @requires vm.gc.Shenandoah
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx512m -Xms512m
|
||||
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive
|
||||
* -XX:+UseCompactObjectHeaders
|
||||
* TestLargeArrayInit
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=compact-off
|
||||
* @summary Verify zero-initialization completeness for large arrays with compact object headers disabled
|
||||
* @requires vm.gc.Shenandoah
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx512m -Xms512m
|
||||
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive
|
||||
* -XX:-UseCompactObjectHeaders
|
||||
* TestLargeArrayInit
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* Allocates large byte[], int[], long[], and Object[] arrays whose sizes span
|
||||
* multiple 64K-word segments (~100MB each), then verifies every element is
|
||||
* zero (or null for reference arrays).
|
||||
*/
|
||||
public class TestLargeArrayInit {
|
||||
|
||||
// ~100MB for each array type
|
||||
static final int BYTE_LEN = 100 * 1024 * 1024; // 100M elements
|
||||
static final int INT_LEN = 25 * 1024 * 1024; // 25M elements * 4 bytes = 100MB
|
||||
static final int LONG_LEN = 12 * 1024 * 1024 + 512*1024; // ~100MB in longs
|
||||
static final int OBJ_LEN = 12 * 1024 * 1024 + 512*1024; // ~100MB in refs (8 bytes each on 64-bit)
|
||||
|
||||
public static void main(String[] args) {
|
||||
testByteArray();
|
||||
testIntArray();
|
||||
testLongArray();
|
||||
testObjectArray();
|
||||
System.out.println("TestLargeArrayInit PASSED");
|
||||
}
|
||||
|
||||
static void testByteArray() {
|
||||
byte[] arr = new byte[BYTE_LEN];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != 0) {
|
||||
throw new RuntimeException("byte[] not zero at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void testIntArray() {
|
||||
int[] arr = new int[INT_LEN];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != 0) {
|
||||
throw new RuntimeException("int[] not zero at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void testLongArray() {
|
||||
long[] arr = new long[LONG_LEN];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != 0L) {
|
||||
throw new RuntimeException("long[] not zero at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void testObjectArray() {
|
||||
Object[] arr = new Object[OBJ_LEN];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != null) {
|
||||
throw new RuntimeException("Object[] not null at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
190
test/hotspot/jtreg/gc/shenandoah/TestLargeArrayInitGCStress.java
Normal file
190
test/hotspot/jtreg/gc/shenandoah/TestLargeArrayInitGCStress.java
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=aggressive
|
||||
* @summary Verify correct object metadata for large arrays under Shenandoah GC stress (aggressive heuristics)
|
||||
* @requires vm.gc.Shenandoah
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx256m -Xms256m
|
||||
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
|
||||
* TestLargeArrayInitGCStress
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=generational-aggressive
|
||||
* @summary Verify correct object metadata for large arrays under Shenandoah generational mode with aggressive heuristics
|
||||
* @requires vm.gc.Shenandoah
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx256m -Xms256m
|
||||
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive -XX:ShenandoahGCMode=generational
|
||||
* TestLargeArrayInitGCStress
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=compact-on
|
||||
* @summary Verify correct object metadata for large arrays with compact object headers enabled
|
||||
* @requires vm.gc.Shenandoah
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx256m -Xms256m
|
||||
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
|
||||
|
||||
* -XX:+UseCompactObjectHeaders
|
||||
* TestLargeArrayInitGCStress
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=compact-off
|
||||
* @summary Verify correct object metadata for large arrays with compact object headers disabled
|
||||
* @requires vm.gc.Shenandoah
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx256m -Xms256m
|
||||
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
|
||||
* -XX:-UseCompactObjectHeaders
|
||||
* TestLargeArrayInitGCStress
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* Allocates large arrays of various types under GC stress (aggressive heuristics,
|
||||
* tight 256MB heap) and verifies that after each allocation:
|
||||
* - array.length matches the requested length
|
||||
* - array.getClass() matches the expected array type
|
||||
* - All elements are zero/null
|
||||
*
|
||||
* Arrays are sized to span multiple 64K-word segments to exercise the segmented
|
||||
* clearing path in ShenandoahObjArrayAllocator. The loop creates sustained GC
|
||||
* pressure so that safepoints are likely to occur during array initialization.
|
||||
*/
|
||||
public class TestLargeArrayInitGCStress {
|
||||
|
||||
static final int ITERATIONS = 50;
|
||||
|
||||
// Array sizes: small (within one segment), medium (a few segments), large (many segments)
|
||||
static final int[] BYTE_SIZES = {1024, 256 * 1024, 2 * 1024 * 1024, 16 * 1024 * 1024};
|
||||
static final int[] INT_SIZES = {1024, 64 * 1024, 512 * 1024, 4 * 1024 * 1024};
|
||||
static final int[] LONG_SIZES = {1024, 32 * 1024, 256 * 1024, 2 * 1024 * 1024};
|
||||
static final int[] OBJ_SIZES = {1024, 32 * 1024, 256 * 1024, 2 * 1024 * 1024};
|
||||
|
||||
// Volatile sink to prevent dead code elimination
|
||||
static volatile Object sink;
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (int iter = 0; iter < ITERATIONS; iter++) {
|
||||
testByteArrays();
|
||||
testIntArrays();
|
||||
testLongArrays();
|
||||
testObjectArrays();
|
||||
}
|
||||
System.out.println("TestLargeArrayInitGCStress PASSED");
|
||||
}
|
||||
|
||||
static void testByteArrays() {
|
||||
for (int len : BYTE_SIZES) {
|
||||
byte[] arr = new byte[len];
|
||||
sink = arr;
|
||||
verifyLength(arr.length, len, "byte[]");
|
||||
verifyClass(arr.getClass(), byte[].class, "byte[]");
|
||||
verifyByteZeros(arr, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void testIntArrays() {
|
||||
for (int len : INT_SIZES) {
|
||||
int[] arr = new int[len];
|
||||
sink = arr;
|
||||
verifyLength(arr.length, len, "int[]");
|
||||
verifyClass(arr.getClass(), int[].class, "int[]");
|
||||
verifyIntZeros(arr, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void testLongArrays() {
|
||||
for (int len : LONG_SIZES) {
|
||||
long[] arr = new long[len];
|
||||
sink = arr;
|
||||
verifyLength(arr.length, len, "long[]");
|
||||
verifyClass(arr.getClass(), long[].class, "long[]");
|
||||
verifyLongZeros(arr, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void testObjectArrays() {
|
||||
for (int len : OBJ_SIZES) {
|
||||
Object[] arr = new Object[len];
|
||||
sink = arr;
|
||||
verifyLength(arr.length, len, "Object[]");
|
||||
verifyClass(arr.getClass(), Object[].class, "Object[]");
|
||||
verifyObjectNulls(arr, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyLength(int actual, int expected, String type) {
|
||||
if (actual != expected) {
|
||||
throw new RuntimeException(type + " length mismatch: expected " + expected + ", got " + actual);
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyClass(Class<?> actual, Class<?> expected, String type) {
|
||||
if (actual != expected) {
|
||||
throw new RuntimeException(type + " class mismatch: expected " + expected.getName() + ", got " + actual.getName());
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyByteZeros(byte[] arr, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (arr[i] != 0) {
|
||||
throw new RuntimeException("byte[] not zero at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyIntZeros(int[] arr, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (arr[i] != 0) {
|
||||
throw new RuntimeException("int[] not zero at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyLongZeros(long[] arr, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (arr[i] != 0L) {
|
||||
throw new RuntimeException("long[] not zero at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyObjectNulls(Object[] arr, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (arr[i] != null) {
|
||||
throw new RuntimeException("Object[] not null at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
141
test/hotspot/jtreg/gc/shenandoah/TestSmallArrayInit.java
Normal file
141
test/hotspot/jtreg/gc/shenandoah/TestSmallArrayInit.java
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=adaptive
|
||||
* @summary Verify behavioral equivalence for small arrays under Shenandoah adaptive mode
|
||||
* @requires vm.gc.Shenandoah
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx256m -Xms256m
|
||||
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive
|
||||
* TestSmallArrayInit
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=generational
|
||||
* @summary Verify behavioral equivalence for small arrays under Shenandoah generational mode
|
||||
* @requires vm.gc.Shenandoah
|
||||
* @library /test/lib
|
||||
*
|
||||
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx256m -Xms256m
|
||||
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahGCMode=generational
|
||||
* TestSmallArrayInit
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* For arrays with word_size <= 64K words (the segment size threshold),
|
||||
* ShenandoahObjArrayAllocator delegates to ObjArrayAllocator::initialize().
|
||||
* This test verifies that small arrays of various types and sizes are correctly
|
||||
* zero-initialized, confirming no regression vs. the default allocator behavior.
|
||||
*
|
||||
* Test sizes:
|
||||
* - Tiny: 10 elements
|
||||
* - Small: 1000 elements
|
||||
* - Near boundary: just under 64K words (65536 words = 524288 bytes on 64-bit)
|
||||
* byte[] -> 524288 elements (524288 bytes = 64K words)
|
||||
* int[] -> 131072 elements (524288 bytes = 64K words)
|
||||
* long[] -> 65536 elements (524288 bytes = 64K words)
|
||||
* Object[] -> 65536 elements (524288 bytes = 64K words, 8 bytes/ref)
|
||||
*/
|
||||
public class TestSmallArrayInit {
|
||||
|
||||
// Tiny sizes
|
||||
static final int TINY = 10;
|
||||
|
||||
// Small sizes
|
||||
static final int SMALL = 1000;
|
||||
|
||||
// Near 64K-word boundary sizes (just under 524288 bytes of element data)
|
||||
// 64K words = 65536 words = 524288 bytes on 64-bit
|
||||
static final int NEAR_BOUNDARY_BYTE = 524288; // 524288 bytes
|
||||
static final int NEAR_BOUNDARY_INT = 131072; // 131072 * 4 = 524288 bytes
|
||||
static final int NEAR_BOUNDARY_LONG = 65536; // 65536 * 8 = 524288 bytes
|
||||
static final int NEAR_BOUNDARY_OBJ = 65536; // 65536 * 8 = 524288 bytes (8 bytes/ref on 64-bit)
|
||||
|
||||
public static void main(String[] args) {
|
||||
testByteArrays();
|
||||
testIntArrays();
|
||||
testLongArrays();
|
||||
testObjectArrays();
|
||||
System.out.println("TestSmallArrayInit PASSED");
|
||||
}
|
||||
|
||||
static void testByteArrays() {
|
||||
verifyByteArray(new byte[TINY], "tiny");
|
||||
verifyByteArray(new byte[SMALL], "small");
|
||||
verifyByteArray(new byte[NEAR_BOUNDARY_BYTE], "near-boundary");
|
||||
}
|
||||
|
||||
static void testIntArrays() {
|
||||
verifyIntArray(new int[TINY], "tiny");
|
||||
verifyIntArray(new int[SMALL], "small");
|
||||
verifyIntArray(new int[NEAR_BOUNDARY_INT], "near-boundary");
|
||||
}
|
||||
|
||||
static void testLongArrays() {
|
||||
verifyLongArray(new long[TINY], "tiny");
|
||||
verifyLongArray(new long[SMALL], "small");
|
||||
verifyLongArray(new long[NEAR_BOUNDARY_LONG], "near-boundary");
|
||||
}
|
||||
|
||||
static void testObjectArrays() {
|
||||
verifyObjectArray(new Object[TINY], "tiny");
|
||||
verifyObjectArray(new Object[SMALL], "small");
|
||||
verifyObjectArray(new Object[NEAR_BOUNDARY_OBJ], "near-boundary");
|
||||
}
|
||||
|
||||
static void verifyByteArray(byte[] arr, String label) {
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != 0) {
|
||||
throw new RuntimeException("byte[" + label + "] not zero at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyIntArray(int[] arr, String label) {
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != 0) {
|
||||
throw new RuntimeException("int[" + label + "] not zero at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyLongArray(long[] arr, String label) {
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != 0L) {
|
||||
throw new RuntimeException("long[" + label + "] not zero at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyObjectArray(Object[] arr, String label) {
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != null) {
|
||||
throw new RuntimeException("Object[" + label + "] not null at index " + i + ": " + arr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user