--- a/hotspot/src/share/vm/classfile/moduleEntry.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/classfile/moduleEntry.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -36,6 +36,7 @@
#include "utilities/events.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp"
+#include "utilities/ostream.hpp"
ModuleEntry* ModuleEntryTable::_javabase_module = NULL;
@@ -359,31 +360,29 @@
java_lang_Class::set_fixup_module_field_list(NULL);
}
-#ifndef PRODUCT
-void ModuleEntryTable::print() {
- tty->print_cr("Module Entry Table (table_size=%d, entries=%d)",
- table_size(), number_of_entries());
+void ModuleEntryTable::print(outputStream* st) {
+ st->print_cr("Module Entry Table (table_size=%d, entries=%d)",
+ table_size(), number_of_entries());
for (int i = 0; i < table_size(); i++) {
for (ModuleEntry* probe = bucket(i);
probe != NULL;
probe = probe->next()) {
- probe->print();
+ probe->print(st);
}
}
}
-void ModuleEntry::print() {
+void ModuleEntry::print(outputStream* st) {
ResourceMark rm;
- tty->print_cr("entry "PTR_FORMAT" name %s module "PTR_FORMAT" loader %s version %s location %s strict %s next "PTR_FORMAT,
- p2i(this),
- name() == NULL ? UNNAMED_MODULE : name()->as_C_string(),
- p2i(module()),
- loader()->loader_name(),
- version() != NULL ? version()->as_C_string() : "NULL",
- location() != NULL ? location()->as_C_string() : "NULL",
- BOOL_TO_STR(!can_read_all_unnamed()), p2i(next()));
+ st->print_cr("entry "PTR_FORMAT" name %s module "PTR_FORMAT" loader %s version %s location %s strict %s next "PTR_FORMAT,
+ p2i(this),
+ name() == NULL ? UNNAMED_MODULE : name()->as_C_string(),
+ p2i(module()),
+ loader()->loader_name(),
+ version() != NULL ? version()->as_C_string() : "NULL",
+ location() != NULL ? location()->as_C_string() : "NULL",
+ BOOL_TO_STR(!can_read_all_unnamed()), p2i(next()));
}
-#endif
void ModuleEntryTable::verify() {
int element_count = 0;
--- a/hotspot/src/share/vm/classfile/moduleEntry.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/classfile/moduleEntry.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -33,6 +33,7 @@
#include "trace/traceMacros.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.hpp"
+#include "utilities/ostream.hpp"
#define UNNAMED_MODULE "Unnamed Module"
@@ -141,7 +142,7 @@
void purge_reads();
void delete_reads();
- void print() PRODUCT_RETURN;
+ void print(outputStream* st = tty);
void verify();
};
@@ -223,7 +224,7 @@
static void finalize_javabase(Handle module_handle, Symbol* version, Symbol* location);
static void patch_javabase_entries(Handle module_handle);
- void print() PRODUCT_RETURN;
+ void print(outputStream* st = tty);
void verify();
};
--- a/hotspot/src/share/vm/classfile/packageEntry.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/classfile/packageEntry.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -32,6 +32,7 @@
#include "utilities/events.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp"
+#include "utilities/ostream.hpp"
// Return true if this package is exported to m.
bool PackageEntry::is_qexported_to(ModuleEntry* m) const {
@@ -265,28 +266,26 @@
}
}
-#ifndef PRODUCT
-void PackageEntryTable::print() {
- tty->print_cr("Package Entry Table (table_size=%d, entries=%d)",
- table_size(), number_of_entries());
+void PackageEntryTable::print(outputStream* st) {
+ st->print_cr("Package Entry Table (table_size=%d, entries=%d)",
+ table_size(), number_of_entries());
for (int i = 0; i < table_size(); i++) {
for (PackageEntry* probe = bucket(i);
probe != NULL;
probe = probe->next()) {
- probe->print();
+ probe->print(st);
}
}
}
-void PackageEntry::print() {
+void PackageEntry::print(outputStream* st) {
ResourceMark rm;
- tty->print_cr("package entry "PTR_FORMAT" name %s module %s classpath_index "
- INT32_FORMAT " is_exported %d is_exported_allUnnamed %d " "next "PTR_FORMAT,
- p2i(this), name()->as_C_string(),
- (module()->is_named() ? module()->name()->as_C_string() : UNNAMED_MODULE),
- _classpath_index, _is_exported, _is_exported_allUnnamed, p2i(next()));
+ st->print_cr("package entry "PTR_FORMAT" name %s module %s classpath_index "
+ INT32_FORMAT " is_exported %d is_exported_allUnnamed %d " "next "PTR_FORMAT,
+ p2i(this), name()->as_C_string(),
+ (module()->is_named() ? module()->name()->as_C_string() : UNNAMED_MODULE),
+ _classpath_index, _is_exported, _is_exported_allUnnamed, p2i(next()));
}
-#endif
void PackageEntryTable::verify() {
int element_count = 0;
--- a/hotspot/src/share/vm/classfile/packageEntry.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/classfile/packageEntry.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -29,6 +29,7 @@
#include "oops/symbol.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.hpp"
+#include "utilities/ostream.hpp"
// A PackageEntry basically represents a Java package. It contains:
// - Symbol* containing the package's name.
@@ -144,7 +145,7 @@
void purge_qualified_exports();
void delete_qualified_exports();
- void print() PRODUCT_RETURN;
+ void print(outputStream* st = tty);
void verify();
};
@@ -195,7 +196,7 @@
// purge dead weak references out of exported list
void purge_all_package_exports();
- void print() PRODUCT_RETURN;
+ void print(outputStream* st = tty);
void verify();
};
--- a/hotspot/src/share/vm/gc/cms/compactibleFreeListSpace.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/cms/compactibleFreeListSpace.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -82,6 +82,8 @@
template <typename SpaceType>
friend void CompactibleSpace::scan_and_compact(SpaceType* space);
template <typename SpaceType>
+ friend void CompactibleSpace::verify_up_to_first_dead(SpaceType* space);
+ template <typename SpaceType>
friend void CompactibleSpace::scan_and_forward(SpaceType* space, CompactPoint* cp);
// "Size" of chunks of work (executed during parallel remark phases
--- a/hotspot/src/share/vm/gc/g1/concurrentG1Refine.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/g1/concurrentG1Refine.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -29,42 +29,174 @@
#include "gc/g1/g1HotCardCache.hpp"
#include "gc/g1/g1Predictions.hpp"
#include "runtime/java.hpp"
+#include "utilities/debug.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/pair.hpp"
+#include <math.h>
-ConcurrentG1Refine::ConcurrentG1Refine(G1CollectedHeap* g1h, const G1Predictions* predictor) :
+// Arbitrary but large limits, to simplify some of the zone calculations.
+// The general idea is to allow expressions like
+// MIN2(x OP y, max_XXX_zone)
+// without needing to check for overflow in "x OP y", because the
+// ranges for x and y have been restricted.
+STATIC_ASSERT(sizeof(LP64_ONLY(jint) NOT_LP64(jshort)) <= (sizeof(size_t)/2));
+const size_t max_yellow_zone = LP64_ONLY(max_jint) NOT_LP64(max_jshort);
+const size_t max_green_zone = max_yellow_zone / 2;
+const size_t max_red_zone = INT_MAX; // For dcqs.set_max_completed_queue.
+STATIC_ASSERT(max_yellow_zone <= max_red_zone);
+
+// Range check assertions for green zone values.
+#define assert_zone_constraints_g(green) \
+ do { \
+ size_t azc_g_green = (green); \
+ assert(azc_g_green <= max_green_zone, \
+ "green exceeds max: " SIZE_FORMAT, azc_g_green); \
+ } while (0)
+
+// Range check assertions for green and yellow zone values.
+#define assert_zone_constraints_gy(green, yellow) \
+ do { \
+ size_t azc_gy_green = (green); \
+ size_t azc_gy_yellow = (yellow); \
+ assert_zone_constraints_g(azc_gy_green); \
+ assert(azc_gy_yellow <= max_yellow_zone, \
+ "yellow exceeds max: " SIZE_FORMAT, azc_gy_yellow); \
+ assert(azc_gy_green <= azc_gy_yellow, \
+ "green (" SIZE_FORMAT ") exceeds yellow (" SIZE_FORMAT ")", \
+ azc_gy_green, azc_gy_yellow); \
+ } while (0)
+
+// Range check assertions for green, yellow, and red zone values.
+#define assert_zone_constraints_gyr(green, yellow, red) \
+ do { \
+ size_t azc_gyr_green = (green); \
+ size_t azc_gyr_yellow = (yellow); \
+ size_t azc_gyr_red = (red); \
+ assert_zone_constraints_gy(azc_gyr_green, azc_gyr_yellow); \
+ assert(azc_gyr_red <= max_red_zone, \
+ "red exceeds max: " SIZE_FORMAT, azc_gyr_red); \
+ assert(azc_gyr_yellow <= azc_gyr_red, \
+ "yellow (" SIZE_FORMAT ") exceeds red (" SIZE_FORMAT ")", \
+ azc_gyr_yellow, azc_gyr_red); \
+ } while (0)
+
+// Logging tag sequence for refinement control updates.
+#define CTRL_TAGS gc, ergo, refine
+
+// For logging zone values, ensuring consistency of level and tags.
+#define LOG_ZONES(...) log_debug( CTRL_TAGS )(__VA_ARGS__)
+
+// Package for pair of refinement thread activation and deactivation
+// thresholds. The activation and deactivation levels are resp. the first
+// and second values of the pair.
+typedef Pair<size_t, size_t> Thresholds;
+inline size_t activation_level(const Thresholds& t) { return t.first; }
+inline size_t deactivation_level(const Thresholds& t) { return t.second; }
+
+static Thresholds calc_thresholds(size_t green_zone,
+ size_t yellow_zone,
+ uint worker_i) {
+ double yellow_size = yellow_zone - green_zone;
+ double step = yellow_size / ConcurrentG1Refine::thread_num();
+ if (worker_i == 0) {
+ // Potentially activate worker 0 more aggressively, to keep
+ // available buffers near green_zone value. When yellow_size is
+ // large we don't want to allow a full step to accumulate before
+ // doing any processing, as that might lead to significantly more
+ // than green_zone buffers to be processed by update_rs.
+ step = MIN2(step, ParallelGCThreads / 2.0);
+ }
+ size_t activate_offset = static_cast<size_t>(ceil(step * (worker_i + 1)));
+ size_t deactivate_offset = static_cast<size_t>(floor(step * worker_i));
+ return Thresholds(green_zone + activate_offset,
+ green_zone + deactivate_offset);
+}
+
+ConcurrentG1Refine::ConcurrentG1Refine(G1CollectedHeap* g1h,
+ size_t green_zone,
+ size_t yellow_zone,
+ size_t red_zone,
+ size_t min_yellow_zone_size) :
_threads(NULL),
_sample_thread(NULL),
- _predictor_sigma(predictor->sigma()),
+ _n_worker_threads(thread_num()),
+ _green_zone(green_zone),
+ _yellow_zone(yellow_zone),
+ _red_zone(red_zone),
+ _min_yellow_zone_size(min_yellow_zone_size),
_hot_card_cache(g1h)
{
- // Ergonomically select initial concurrent refinement parameters
- if (FLAG_IS_DEFAULT(G1ConcRefinementGreenZone)) {
- FLAG_SET_DEFAULT(G1ConcRefinementGreenZone, ParallelGCThreads);
- }
- set_green_zone(G1ConcRefinementGreenZone);
+ assert_zone_constraints_gyr(green_zone, yellow_zone, red_zone);
+}
- if (FLAG_IS_DEFAULT(G1ConcRefinementYellowZone)) {
- FLAG_SET_DEFAULT(G1ConcRefinementYellowZone, green_zone() * 3);
+static size_t calc_min_yellow_zone_size() {
+ size_t step = G1ConcRefinementThresholdStep;
+ uint n_workers = ConcurrentG1Refine::thread_num();
+ if ((max_yellow_zone / step) < n_workers) {
+ return max_yellow_zone;
+ } else {
+ return step * n_workers;
}
- set_yellow_zone(MAX2(G1ConcRefinementYellowZone, green_zone()));
+}
- if (FLAG_IS_DEFAULT(G1ConcRefinementRedZone)) {
- FLAG_SET_DEFAULT(G1ConcRefinementRedZone, yellow_zone() * 2);
+static size_t calc_init_green_zone() {
+ size_t green = G1ConcRefinementGreenZone;
+ if (FLAG_IS_DEFAULT(G1ConcRefinementGreenZone)) {
+ green = ParallelGCThreads;
}
- set_red_zone(MAX2(G1ConcRefinementRedZone, yellow_zone()));
-
+ return MIN2(green, max_green_zone);
}
-ConcurrentG1Refine* ConcurrentG1Refine::create(G1CollectedHeap* g1h, CardTableEntryClosure* refine_closure, jint* ecode) {
- G1CollectorPolicy* policy = g1h->g1_policy();
- ConcurrentG1Refine* cg1r = new ConcurrentG1Refine(g1h, &policy->predictor());
+static size_t calc_init_yellow_zone(size_t green, size_t min_size) {
+ size_t config = G1ConcRefinementYellowZone;
+ size_t size = 0;
+ if (FLAG_IS_DEFAULT(G1ConcRefinementYellowZone)) {
+ size = green * 2;
+ } else if (green < config) {
+ size = config - green;
+ }
+ size = MAX2(size, min_size);
+ size = MIN2(size, max_yellow_zone);
+ return MIN2(green + size, max_yellow_zone);
+}
+
+static size_t calc_init_red_zone(size_t green, size_t yellow) {
+ size_t size = yellow - green;
+ if (!FLAG_IS_DEFAULT(G1ConcRefinementRedZone)) {
+ size_t config = G1ConcRefinementRedZone;
+ if (yellow < config) {
+ size = MAX2(size, config - yellow);
+ }
+ }
+ return MIN2(yellow + size, max_red_zone);
+}
+
+ConcurrentG1Refine* ConcurrentG1Refine::create(G1CollectedHeap* g1h,
+ CardTableEntryClosure* refine_closure,
+ jint* ecode) {
+ size_t min_yellow_zone_size = calc_min_yellow_zone_size();
+ size_t green_zone = calc_init_green_zone();
+ size_t yellow_zone = calc_init_yellow_zone(green_zone, min_yellow_zone_size);
+ size_t red_zone = calc_init_red_zone(green_zone, yellow_zone);
+
+ LOG_ZONES("Initial Refinement Zones: "
+ "green: " SIZE_FORMAT ", "
+ "yellow: " SIZE_FORMAT ", "
+ "red: " SIZE_FORMAT ", "
+ "min yellow size: " SIZE_FORMAT,
+ green_zone, yellow_zone, red_zone, min_yellow_zone_size);
+
+ ConcurrentG1Refine* cg1r = new ConcurrentG1Refine(g1h,
+ green_zone,
+ yellow_zone,
+ red_zone,
+ min_yellow_zone_size);
+
if (cg1r == NULL) {
*ecode = JNI_ENOMEM;
vm_shutdown_during_initialization("Could not create ConcurrentG1Refine");
return NULL;
}
- cg1r->_n_worker_threads = thread_num();
-
- cg1r->reset_threshold_step();
cg1r->_threads = NEW_C_HEAP_ARRAY_RETURN_NULL(ConcurrentG1RefineThread*, cg1r->_n_worker_threads, mtGC);
if (cg1r->_threads == NULL) {
@@ -77,7 +209,15 @@
ConcurrentG1RefineThread *next = NULL;
for (uint i = cg1r->_n_worker_threads - 1; i != UINT_MAX; i--) {
- ConcurrentG1RefineThread* t = new ConcurrentG1RefineThread(cg1r, next, refine_closure, worker_id_offset, i);
+ Thresholds thresholds = calc_thresholds(green_zone, yellow_zone, i);
+ ConcurrentG1RefineThread* t =
+ new ConcurrentG1RefineThread(cg1r,
+ next,
+ refine_closure,
+ worker_id_offset,
+ i,
+ activation_level(thresholds),
+ deactivation_level(thresholds));
assert(t != NULL, "Conc refine should have been created");
if (t->osthread() == NULL) {
*ecode = JNI_ENOMEM;
@@ -101,14 +241,6 @@
return cg1r;
}
-void ConcurrentG1Refine::reset_threshold_step() {
- if (FLAG_IS_DEFAULT(G1ConcRefinementThresholdStep)) {
- _thread_threshold_step = (yellow_zone() - green_zone()) / (worker_thread_num() + 1);
- } else {
- _thread_threshold_step = G1ConcRefinementThresholdStep;
- }
-}
-
void ConcurrentG1Refine::init(G1RegionToSpaceMapper* card_counts_storage) {
_hot_card_cache.initialize(card_counts_storage);
}
@@ -120,10 +252,11 @@
_sample_thread->stop();
}
-void ConcurrentG1Refine::reinitialize_threads() {
- reset_threshold_step();
+void ConcurrentG1Refine::update_thread_thresholds() {
for (uint i = 0; i < _n_worker_threads; i++) {
- _threads[i]->initialize();
+ Thresholds thresholds = calc_thresholds(_green_zone, _yellow_zone, i);
+ _threads[i]->update_thresholds(activation_level(thresholds),
+ deactivation_level(thresholds));
}
}
@@ -142,7 +275,7 @@
}
void ConcurrentG1Refine::worker_threads_do(ThreadClosure * tc) {
- for (uint i = 0; i < worker_thread_num(); i++) {
+ for (uint i = 0; i < _n_worker_threads; i++) {
tc->do_thread(_threads[i]);
}
}
@@ -160,34 +293,80 @@
st->cr();
}
+static size_t calc_new_green_zone(size_t green,
+ double update_rs_time,
+ size_t update_rs_processed_buffers,
+ double goal_ms) {
+ // Adjust green zone based on whether we're meeting the time goal.
+ // Limit to max_green_zone.
+ const double inc_k = 1.1, dec_k = 0.9;
+ if (update_rs_time > goal_ms) {
+ if (green > 0) {
+ green = static_cast<size_t>(green * dec_k);
+ }
+ } else if (update_rs_time < goal_ms &&
+ update_rs_processed_buffers > green) {
+ green = static_cast<size_t>(MAX2(green * inc_k, green + 1.0));
+ green = MIN2(green, max_green_zone);
+ }
+ return green;
+}
+
+static size_t calc_new_yellow_zone(size_t green, size_t min_yellow_size) {
+ size_t size = green * 2;
+ size = MAX2(size, min_yellow_size);
+ return MIN2(green + size, max_yellow_zone);
+}
+
+static size_t calc_new_red_zone(size_t green, size_t yellow) {
+ return MIN2(yellow + (yellow - green), max_red_zone);
+}
+
+void ConcurrentG1Refine::update_zones(double update_rs_time,
+ size_t update_rs_processed_buffers,
+ double goal_ms) {
+ log_trace( CTRL_TAGS )("Updating Refinement Zones: "
+ "update_rs time: %.3fms, "
+ "update_rs buffers: " SIZE_FORMAT ", "
+ "update_rs goal time: %.3fms",
+ update_rs_time,
+ update_rs_processed_buffers,
+ goal_ms);
+
+ _green_zone = calc_new_green_zone(_green_zone,
+ update_rs_time,
+ update_rs_processed_buffers,
+ goal_ms);
+ _yellow_zone = calc_new_yellow_zone(_green_zone, _min_yellow_zone_size);
+ _red_zone = calc_new_red_zone(_green_zone, _yellow_zone);
+
+ assert_zone_constraints_gyr(_green_zone, _yellow_zone, _red_zone);
+ LOG_ZONES("Updated Refinement Zones: "
+ "green: " SIZE_FORMAT ", "
+ "yellow: " SIZE_FORMAT ", "
+ "red: " SIZE_FORMAT,
+ _green_zone, _yellow_zone, _red_zone);
+}
+
void ConcurrentG1Refine::adjust(double update_rs_time,
- double update_rs_processed_buffers,
+ size_t update_rs_processed_buffers,
double goal_ms) {
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
if (G1UseAdaptiveConcRefinement) {
- const int k_gy = 3, k_gr = 6;
- const double inc_k = 1.1, dec_k = 0.9;
+ update_zones(update_rs_time, update_rs_processed_buffers, goal_ms);
+ update_thread_thresholds();
- size_t g = green_zone();
- if (update_rs_time > goal_ms) {
- g = (size_t)(g * dec_k); // Can become 0, that's OK. That would mean a mutator-only processing.
+ // Change the barrier params
+ if (_n_worker_threads == 0) {
+ // Disable dcqs notification when there are no threads to notify.
+ dcqs.set_process_completed_threshold(INT_MAX);
} else {
- if (update_rs_time < goal_ms && update_rs_processed_buffers > g) {
- g = (size_t)MAX2(g * inc_k, g + 1.0);
- }
+ // Worker 0 is the primary; wakeup is via dcqs notification.
+ STATIC_ASSERT(max_yellow_zone <= INT_MAX);
+ size_t activate = _threads[0]->activation_threshold();
+ dcqs.set_process_completed_threshold((int)activate);
}
- // Change the refinement threads params
- set_green_zone(g);
- set_yellow_zone(g * k_gy);
- set_red_zone(g * k_gr);
- reinitialize_threads();
-
- size_t processing_threshold_delta = MAX2<size_t>(green_zone() * _predictor_sigma, 1);
- size_t processing_threshold = MIN2(green_zone() + processing_threshold_delta,
- yellow_zone());
- // Change the barrier params
- dcqs.set_process_completed_threshold((int)processing_threshold);
dcqs.set_max_completed_queue((int)red_zone());
}
--- a/hotspot/src/share/vm/gc/g1/concurrentG1Refine.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/g1/concurrentG1Refine.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -65,18 +65,24 @@
size_t _green_zone;
size_t _yellow_zone;
size_t _red_zone;
-
- size_t _thread_threshold_step;
-
- double _predictor_sigma;
+ size_t _min_yellow_zone_size;
// We delay the refinement of 'hot' cards using the hot card cache.
G1HotCardCache _hot_card_cache;
- // Reset the threshold step value based of the current zone boundaries.
- void reset_threshold_step();
+ ConcurrentG1Refine(G1CollectedHeap* g1h,
+ size_t green_zone,
+ size_t yellow_zone,
+ size_t red_zone,
+ size_t min_yellow_zone_size);
- ConcurrentG1Refine(G1CollectedHeap* g1h, const G1Predictions* predictions);
+ // Update green/yellow/red zone values based on how well goals are being met.
+ void update_zones(double update_rs_time,
+ size_t update_rs_processed_buffers,
+ double goal_ms);
+
+ // Update thread thresholds to account for updated zone values.
+ void update_thread_thresholds();
public:
~ConcurrentG1Refine();
@@ -88,9 +94,7 @@
void init(G1RegionToSpaceMapper* card_counts_storage);
void stop();
- void adjust(double update_rs_time, double update_rs_processed_buffers, double goal_ms);
-
- void reinitialize_threads();
+ void adjust(double update_rs_time, size_t update_rs_processed_buffers, double goal_ms);
// Iterate over all concurrent refinement threads
void threads_do(ThreadClosure *tc);
@@ -105,18 +109,10 @@
void print_worker_threads_on(outputStream* st) const;
- void set_green_zone(size_t x) { _green_zone = x; }
- void set_yellow_zone(size_t x) { _yellow_zone = x; }
- void set_red_zone(size_t x) { _red_zone = x; }
-
size_t green_zone() const { return _green_zone; }
size_t yellow_zone() const { return _yellow_zone; }
size_t red_zone() const { return _red_zone; }
- uint worker_thread_num() const { return _n_worker_threads; }
-
- size_t thread_threshold_step() const { return _thread_threshold_step; }
-
G1HotCardCache* hot_card_cache() { return &_hot_card_cache; }
static bool hot_card_cache_enabled() { return G1HotCardCache::default_use_cache(); }
--- a/hotspot/src/share/vm/gc/g1/concurrentG1RefineThread.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/g1/concurrentG1RefineThread.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -36,7 +36,8 @@
ConcurrentG1RefineThread::
ConcurrentG1RefineThread(ConcurrentG1Refine* cg1r, ConcurrentG1RefineThread *next,
CardTableEntryClosure* refine_closure,
- uint worker_id_offset, uint worker_id) :
+ uint worker_id_offset, uint worker_id,
+ size_t activate, size_t deactivate) :
ConcurrentGCThread(),
_refine_closure(refine_closure),
_worker_id_offset(worker_id_offset),
@@ -45,7 +46,9 @@
_next(next),
_monitor(NULL),
_cg1r(cg1r),
- _vtime_accum(0.0)
+ _vtime_accum(0.0),
+ _activation_threshold(activate),
+ _deactivation_threshold(deactivate)
{
// Each thread has its own monitor. The i-th thread is responsible for signaling
@@ -58,21 +61,17 @@
} else {
_monitor = DirtyCardQ_CBL_mon;
}
- initialize();
// set name
set_name("G1 Refine#%d", worker_id);
create_and_start();
}
-void ConcurrentG1RefineThread::initialize() {
- // Current thread activation threshold
- _threshold = MIN2(cg1r()->thread_threshold_step() * (_worker_id + 1) + cg1r()->green_zone(),
- cg1r()->yellow_zone());
- // A thread deactivates once the number of buffer reached a deactivation threshold
- _deactivation_threshold =
- MAX2(_threshold - MIN2(_threshold, cg1r()->thread_threshold_step()),
- cg1r()->green_zone());
+void ConcurrentG1RefineThread::update_thresholds(size_t activate,
+ size_t deactivate) {
+ assert(deactivate < activate, "precondition");
+ _activation_threshold = activate;
+ _deactivation_threshold = deactivate;
}
void ConcurrentG1RefineThread::wait_for_completed_buffers() {
@@ -118,9 +117,10 @@
break;
}
+ size_t buffers_processed = 0;
DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
log_debug(gc, refine)("Activated %d, on threshold: " SIZE_FORMAT ", current: " SIZE_FORMAT,
- _worker_id, _threshold, dcqs.completed_buffers_num());
+ _worker_id, _activation_threshold, dcqs.completed_buffers_num());
{
SuspendibleThreadSetJoiner sts_join;
@@ -139,7 +139,9 @@
}
// Check if we need to activate the next thread.
- if (_next != NULL && !_next->is_active() && curr_buffer_num > _next->_threshold) {
+ if ((_next != NULL) &&
+ !_next->is_active() &&
+ (curr_buffer_num > _next->_activation_threshold)) {
_next->activate();
}
@@ -150,14 +152,16 @@
false /* during_pause */)) {
break; // Deactivate, number of buffers fell below threshold.
}
+ ++buffers_processed;
}
}
deactivate();
log_debug(gc, refine)("Deactivated %d, off threshold: " SIZE_FORMAT
- ", current: " SIZE_FORMAT,
+ ", current: " SIZE_FORMAT ", processed: " SIZE_FORMAT,
_worker_id, _deactivation_threshold,
- dcqs.completed_buffers_num());
+ dcqs.completed_buffers_num(),
+ buffers_processed);
if (os::supports_vtime()) {
_vtime_accum = (os::elapsedVTime() - _vtime_start);
--- a/hotspot/src/share/vm/gc/g1/concurrentG1RefineThread.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/g1/concurrentG1RefineThread.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -53,10 +53,8 @@
// The closure applied to completed log buffers.
CardTableEntryClosure* _refine_closure;
- size_t _thread_threshold_step;
- // This thread activation threshold
- size_t _threshold;
- // This thread deactivation threshold
+ // This thread's activation/deactivation thresholds
+ size_t _activation_threshold;
size_t _deactivation_threshold;
void wait_for_completed_buffers();
@@ -75,9 +73,11 @@
// Constructor
ConcurrentG1RefineThread(ConcurrentG1Refine* cg1r, ConcurrentG1RefineThread* next,
CardTableEntryClosure* refine_closure,
- uint worker_id_offset, uint worker_id);
+ uint worker_id_offset, uint worker_id,
+ size_t activate, size_t deactivate);
- void initialize();
+ void update_thresholds(size_t activate, size_t deactivate);
+ size_t activation_threshold() const { return _activation_threshold; }
// Total virtual time so far.
double vtime_accum() { return _vtime_accum; }
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -4452,7 +4452,6 @@
}
void G1CollectedHeap::preserve_cm_referents(G1ParScanThreadStateSet* per_thread_states) {
- double preserve_cm_referents_start = os::elapsedTime();
// Any reference objects, in the collection set, that were 'discovered'
// by the CM ref processor should have already been copied (either by
// applying the external root copy closure to the discovered lists, or
@@ -4473,16 +4472,24 @@
// objects discovered by the STW ref processor in case one of these
// referents points to another object which is also referenced by an
// object discovered by the STW ref processor.
-
- uint no_of_gc_workers = workers()->active_workers();
-
- G1ParPreserveCMReferentsTask keep_cm_referents(this,
- per_thread_states,
- no_of_gc_workers,
- _task_queues);
- workers()->run_task(&keep_cm_referents);
-
- g1_policy()->phase_times()->record_preserve_cm_referents_time_ms((os::elapsedTime() - preserve_cm_referents_start) * 1000.0);
+ double preserve_cm_referents_time = 0.0;
+
+ // To avoid spawning task when there is no work to do, check that
+ // a concurrent cycle is active and that some references have been
+ // discovered.
+ if (concurrent_mark()->cmThread()->during_cycle() &&
+ ref_processor_cm()->has_discovered_references()) {
+ double preserve_cm_referents_start = os::elapsedTime();
+ uint no_of_gc_workers = workers()->active_workers();
+ G1ParPreserveCMReferentsTask keep_cm_referents(this,
+ per_thread_states,
+ no_of_gc_workers,
+ _task_queues);
+ workers()->run_task(&keep_cm_referents);
+ preserve_cm_referents_time = os::elapsedTime() - preserve_cm_referents_start;
+ }
+
+ g1_policy()->phase_times()->record_preserve_cm_referents_time_ms(preserve_cm_referents_time * 1000.0);
}
// Weak Reference processing during an evacuation pause (part 1).
--- a/hotspot/src/share/vm/gc/g1/g1YoungGenSizer.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/g1/g1YoungGenSizer.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -25,12 +25,13 @@
#include "precompiled.hpp"
#include "gc/g1/g1YoungGenSizer.hpp"
#include "gc/g1/heapRegion.hpp"
+#include "logging/log.hpp"
G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), _adaptive_size(true),
_min_desired_young_length(0), _max_desired_young_length(0) {
if (FLAG_IS_CMDLINE(NewRatio)) {
if (FLAG_IS_CMDLINE(NewSize) || FLAG_IS_CMDLINE(MaxNewSize)) {
- warning("-XX:NewSize and -XX:MaxNewSize override -XX:NewRatio");
+ log_warning(gc, ergo)("-XX:NewSize and -XX:MaxNewSize override -XX:NewRatio");
} else {
_sizer_kind = SizerNewRatio;
_adaptive_size = false;
@@ -40,9 +41,9 @@
if (NewSize > MaxNewSize) {
if (FLAG_IS_CMDLINE(MaxNewSize)) {
- warning("NewSize (" SIZE_FORMAT "k) is greater than the MaxNewSize (" SIZE_FORMAT "k). "
- "A new max generation size of " SIZE_FORMAT "k will be used.",
- NewSize/K, MaxNewSize/K, NewSize/K);
+ log_warning(gc, ergo)("NewSize (" SIZE_FORMAT "k) is greater than the MaxNewSize (" SIZE_FORMAT "k). "
+ "A new max generation size of " SIZE_FORMAT "k will be used.",
+ NewSize/K, MaxNewSize/K, NewSize/K);
}
MaxNewSize = NewSize;
}
--- a/hotspot/src/share/vm/gc/g1/g1_globals.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/g1/g1_globals.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -112,8 +112,7 @@
product(size_t, G1ConcRefinementRedZone, 0, \
"Maximum number of enqueued update buffers before mutator " \
"threads start processing new ones instead of enqueueing them. " \
- "Will be selected ergonomically by default. Zero will disable " \
- "concurrent processing.") \
+ "Will be selected ergonomically by default.") \
range(0, max_intx) \
\
product(size_t, G1ConcRefinementGreenZone, 0, \
@@ -127,11 +126,12 @@
"specified number of milliseconds to do miscellaneous work.") \
range(0, max_jint) \
\
- product(size_t, G1ConcRefinementThresholdStep, 0, \
+ product(size_t, G1ConcRefinementThresholdStep, 2, \
"Each time the rset update queue increases by this amount " \
"activate the next refinement thread if available. " \
- "Will be selected ergonomically by default.") \
- range(0, SIZE_MAX) \
+ "The actual step size will be selected ergonomically by " \
+ "default, with this value used to determine a lower bound.") \
+ range(1, SIZE_MAX) \
\
product(intx, G1RSetUpdatingPauseTimePercent, 10, \
"A target percentage of time that is allowed to be spend on " \
@@ -201,9 +201,9 @@
range(0, 32*M) \
constraint(G1HeapRegionSizeConstraintFunc,AfterMemoryInit) \
\
- product(uintx, G1ConcRefinementThreads, 0, \
- "If non-0 is the number of parallel rem set update threads, " \
- "otherwise the value is determined ergonomically.") \
+ product(uint, G1ConcRefinementThreads, 0, \
+ "The number of parallel rem set update threads. " \
+ "Will be set ergonomically by default.") \
range(0, (max_jint-1)/wordSize) \
\
develop(bool, G1VerifyCTCleanup, false, \
--- a/hotspot/src/share/vm/gc/shared/referenceProcessor.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/shared/referenceProcessor.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -1090,6 +1090,15 @@
return true;
}
+bool ReferenceProcessor::has_discovered_references() {
+ for (uint i = 0; i < _max_num_q * number_of_subclasses_of_ref(); i++) {
+ if (!_discovered_refs[i].is_empty()) {
+ return true;
+ }
+ }
+ return false;
+}
+
// Preclean the discovered references by removing those
// whose referents are alive, and by marking from those that
// are not active. These lists can be handled here
--- a/hotspot/src/share/vm/gc/shared/referenceProcessor.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/shared/referenceProcessor.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -412,6 +412,9 @@
// Discover a Reference object, using appropriate discovery criteria
bool discover_reference(oop obj, ReferenceType rt);
+ // Has discovered references that need handling
+ bool has_discovered_references();
+
// Process references found during GC (called by the garbage collector)
ReferenceProcessorStats
process_discovered_references(BoolObjectClosure* is_alive,
--- a/hotspot/src/share/vm/gc/shared/space.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/shared/space.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -411,22 +411,6 @@
return compact_top;
}
-
-bool CompactibleSpace::insert_deadspace(size_t& allowed_deadspace_words,
- HeapWord* q, size_t deadlength) {
- if (allowed_deadspace_words >= deadlength) {
- allowed_deadspace_words -= deadlength;
- CollectedHeap::fill_with_object(q, deadlength);
- oop(q)->set_mark(oop(q)->mark()->set_marked());
- assert((int) deadlength == oop(q)->size(), "bad filler object size");
- // Recall that we required "q == compaction_top".
- return true;
- } else {
- allowed_deadspace_words = 0;
- return false;
- }
-}
-
void ContiguousSpace::prepare_for_compaction(CompactPoint* cp) {
scan_and_forward(this, cp);
}
--- a/hotspot/src/share/vm/gc/shared/space.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/shared/space.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -362,6 +362,12 @@
inline size_t obj_size(const HeapWord* addr) const;
+ template <class SpaceType>
+ static inline void verify_up_to_first_dead(SpaceType* space) NOT_DEBUG_RETURN;
+
+ template <class SpaceType>
+ static inline void clear_empty_region(SpaceType* space);
+
public:
CompactibleSpace() :
_compaction_top(NULL), _next_compaction_space(NULL) {}
@@ -455,16 +461,6 @@
return end();
}
- // Requires "allowed_deadspace_words > 0", that "q" is the start of a
- // free block of the given "word_len", and that "q", were it an object,
- // would not move if forwarded. If the size allows, fill the free
- // block with an object, to prevent excessive compaction. Returns "true"
- // iff the free region was made deadspace, and modifies
- // "allowed_deadspace_words" to reflect the number of available deadspace
- // words remaining after this operation.
- bool insert_deadspace(size_t& allowed_deadspace_words, HeapWord* q,
- size_t word_len);
-
// Below are template functions for scan_and_* algorithms (avoiding virtual calls).
// The space argument should be a subclass of CompactibleSpace, implementing
// scan_limit(), scanned_block_is_obj(), and scanned_block_size(),
--- a/hotspot/src/share/vm/gc/shared/space.inline.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/gc/shared/space.inline.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -31,6 +31,7 @@
#include "gc/shared/space.hpp"
#include "gc/shared/spaceDecorator.hpp"
#include "memory/universe.hpp"
+#include "oops/oopsHierarchy.hpp"
#include "runtime/prefetch.inline.hpp"
#include "runtime/safepoint.hpp"
@@ -75,11 +76,61 @@
return oop(addr)->size();
}
+class DeadSpacer : StackObj {
+ size_t _allowed_deadspace_words;
+ bool _active;
+ CompactibleSpace* _space;
+
+public:
+ DeadSpacer(CompactibleSpace* space) : _space(space), _allowed_deadspace_words(0) {
+ size_t ratio = _space->allowed_dead_ratio();
+ _active = ratio > 0;
+
+ if (_active) {
+ assert(!UseG1GC, "G1 should not be using dead space");
+
+ // We allow some amount of garbage towards the bottom of the space, so
+ // we don't start compacting before there is a significant gain to be made.
+ // Occasionally, we want to ensure a full compaction, which is determined
+ // by the MarkSweepAlwaysCompactCount parameter.
+ if ((MarkSweep::total_invocations() % MarkSweepAlwaysCompactCount) != 0) {
+ _allowed_deadspace_words = (space->capacity() * ratio / 100) / HeapWordSize;
+ } else {
+ _active = false;
+ }
+ }
+ }
+
+
+ bool insert_deadspace(HeapWord* dead_start, HeapWord* dead_end) {
+ if (!_active) {
+ return false;
+ }
+
+ size_t dead_length = pointer_delta(dead_end, dead_start);
+ if (_allowed_deadspace_words >= dead_length) {
+ _allowed_deadspace_words -= dead_length;
+ CollectedHeap::fill_with_object(dead_start, dead_length);
+ oop obj = oop(dead_start);
+ obj->set_mark(obj->mark()->set_marked());
+
+ assert(dead_length == (size_t)obj->size(), "bad filler object size");
+ log_develop_trace(gc, compaction)("Inserting object to dead space: " PTR_FORMAT ", " PTR_FORMAT ", " SIZE_FORMAT "b",
+ p2i(dead_start), p2i(dead_end), dead_length * HeapWordSize);
+
+ return true;
+ } else {
+ _active = false;
+ return false;
+ }
+ }
+
+};
+
template <class SpaceType>
inline void CompactibleSpace::scan_and_forward(SpaceType* space, CompactPoint* cp) {
// Compute the new addresses for the live objects and store it in the mark
// Used by universe::mark_sweep_phase2()
- HeapWord* compact_top; // This is where we are currently compacting to.
// We're sure to be here before any objects are compacted into this
// space, so this is a good time to initialize this:
@@ -90,89 +141,73 @@
assert(cp->threshold == NULL, "just checking");
assert(cp->gen->first_compaction_space() == space, "just checking");
cp->space = cp->gen->first_compaction_space();
- compact_top = cp->space->bottom();
- cp->space->set_compaction_top(compact_top);
cp->threshold = cp->space->initialize_threshold();
- } else {
- compact_top = cp->space->compaction_top();
+ cp->space->set_compaction_top(cp->space->bottom());
}
- // We allow some amount of garbage towards the bottom of the space, so
- // we don't start compacting before there is a significant gain to be made.
- // Occasionally, we want to ensure a full compaction, which is determined
- // by the MarkSweepAlwaysCompactCount parameter.
- uint invocations = MarkSweep::total_invocations();
- bool skip_dead = ((invocations % MarkSweepAlwaysCompactCount) != 0);
+ HeapWord* compact_top = cp->space->compaction_top(); // This is where we are currently compacting to.
- size_t allowed_deadspace = 0;
- if (skip_dead) {
- const size_t ratio = space->allowed_dead_ratio();
- allowed_deadspace = (space->capacity() * ratio / 100) / HeapWordSize;
- }
+ DeadSpacer dead_spacer(space);
- HeapWord* q = space->bottom();
- HeapWord* t = space->scan_limit();
-
- HeapWord* end_of_live= q; // One byte beyond the last byte of the last
- // live object.
- HeapWord* first_dead = space->end(); // The first dead object.
+ HeapWord* end_of_live = space->bottom(); // One byte beyond the last byte of the last live object.
+ HeapWord* first_dead = NULL; // The first dead object.
const intx interval = PrefetchScanIntervalInBytes;
- while (q < t) {
- assert(!space->scanned_block_is_obj(q) ||
- oop(q)->mark()->is_marked() || oop(q)->mark()->is_unlocked() ||
- oop(q)->mark()->has_bias_pattern(),
+ HeapWord* cur_obj = space->bottom();
+ HeapWord* scan_limit = space->scan_limit();
+
+ while (cur_obj < scan_limit) {
+ assert(!space->scanned_block_is_obj(cur_obj) ||
+ oop(cur_obj)->mark()->is_marked() || oop(cur_obj)->mark()->is_unlocked() ||
+ oop(cur_obj)->mark()->has_bias_pattern(),
"these are the only valid states during a mark sweep");
- if (space->scanned_block_is_obj(q) && oop(q)->is_gc_marked()) {
- // prefetch beyond q
- Prefetch::write(q, interval);
- size_t size = space->scanned_block_size(q);
- compact_top = cp->space->forward(oop(q), size, cp, compact_top);
- q += size;
- end_of_live = q;
+ if (space->scanned_block_is_obj(cur_obj) && oop(cur_obj)->is_gc_marked()) {
+ // prefetch beyond cur_obj
+ Prefetch::write(cur_obj, interval);
+ size_t size = space->scanned_block_size(cur_obj);
+ compact_top = cp->space->forward(oop(cur_obj), size, cp, compact_top);
+ cur_obj += size;
+ end_of_live = cur_obj;
} else {
// run over all the contiguous dead objects
- HeapWord* end = q;
+ HeapWord* end = cur_obj;
do {
// prefetch beyond end
Prefetch::write(end, interval);
end += space->scanned_block_size(end);
- } while (end < t && (!space->scanned_block_is_obj(end) || !oop(end)->is_gc_marked()));
+ } while (end < scan_limit && (!space->scanned_block_is_obj(end) || !oop(end)->is_gc_marked()));
// see if we might want to pretend this object is alive so that
// we don't have to compact quite as often.
- if (allowed_deadspace > 0 && q == compact_top) {
- size_t sz = pointer_delta(end, q);
- if (space->insert_deadspace(allowed_deadspace, q, sz)) {
- compact_top = cp->space->forward(oop(q), sz, cp, compact_top);
- q = end;
- end_of_live = end;
- continue;
+ if (cur_obj == compact_top && dead_spacer.insert_deadspace(cur_obj, end)) {
+ oop obj = oop(cur_obj);
+ compact_top = cp->space->forward(obj, obj->size(), cp, compact_top);
+ end_of_live = end;
+ } else {
+ // otherwise, it really is a free region.
+
+ // cur_obj is a pointer to a dead object. Use this dead memory to store a pointer to the next live object.
+ *(HeapWord**)cur_obj = end;
+
+ // see if this is the first dead region.
+ if (first_dead == NULL) {
+ first_dead = cur_obj;
}
}
- // otherwise, it really is a free region.
-
- // q is a pointer to a dead object. Use this dead memory to store a pointer to the next live object.
- (*(HeapWord**)q) = end;
-
- // see if this is the first dead region.
- if (q < first_dead) {
- first_dead = q;
- }
-
// move on to the next object
- q = end;
+ cur_obj = end;
}
}
- assert(q == t, "just checking");
+ assert(cur_obj == scan_limit, "just checking");
space->_end_of_live = end_of_live;
- if (end_of_live < first_dead) {
- first_dead = end_of_live;
+ if (first_dead != NULL) {
+ space->_first_dead = first_dead;
+ } else {
+ space->_first_dead = end_of_live;
}
- space->_first_dead = first_dead;
// save the compaction_top of the compaction space.
cp->space->set_compaction_top(compact_top);
@@ -183,127 +218,58 @@
// adjust all the interior pointers to point at the new locations of objects
// Used by MarkSweep::mark_sweep_phase3()
- HeapWord* q = space->bottom();
- HeapWord* t = space->_end_of_live; // Established by "prepare_for_compaction".
-
- assert(space->_first_dead <= space->_end_of_live, "Stands to reason, no?");
-
- if (q < t && space->_first_dead > q && !oop(q)->is_gc_marked()) {
- // we have a chunk of the space which hasn't moved and we've
- // reinitialized the mark word during the previous pass, so we can't
- // use is_gc_marked for the traversal.
- HeapWord* end = space->_first_dead;
+ HeapWord* cur_obj = space->bottom();
+ HeapWord* const end_of_live = space->_end_of_live; // Established by "scan_and_forward".
+ HeapWord* const first_dead = space->_first_dead; // Established by "scan_and_forward".
- while (q < end) {
- // I originally tried to conjoin "block_start(q) == q" to the
- // assertion below, but that doesn't work, because you can't
- // accurately traverse previous objects to get to the current one
- // after their pointers have been
- // updated, until the actual compaction is done. dld, 4/00
- assert(space->block_is_obj(q), "should be at block boundaries, and should be looking at objs");
-
- // point all the oops to the new location
- size_t size = MarkSweep::adjust_pointers(oop(q));
- size = space->adjust_obj_size(size);
-
- q += size;
- }
-
- if (space->_first_dead == t) {
- q = t;
- } else {
- // The first dead object is no longer an object. At that memory address,
- // there is a pointer to the first live object that the previous phase found.
- q = *((HeapWord**)(space->_first_dead));
- }
- }
+ assert(first_dead <= end_of_live, "Stands to reason, no?");
const intx interval = PrefetchScanIntervalInBytes;
- debug_only(HeapWord* prev_q = NULL);
- while (q < t) {
- // prefetch beyond q
- Prefetch::write(q, interval);
- if (oop(q)->is_gc_marked()) {
- // q is alive
+ debug_only(HeapWord* prev_obj = NULL);
+ while (cur_obj < end_of_live) {
+ Prefetch::write(cur_obj, interval);
+ if (cur_obj < first_dead || oop(cur_obj)->is_gc_marked()) {
+ // cur_obj is alive
// point all the oops to the new location
- size_t size = MarkSweep::adjust_pointers(oop(q));
+ size_t size = MarkSweep::adjust_pointers(oop(cur_obj));
size = space->adjust_obj_size(size);
- debug_only(prev_q = q);
- q += size;
+ debug_only(prev_obj = cur_obj);
+ cur_obj += size;
} else {
- debug_only(prev_q = q);
- // q is not a live object, instead it points at the next live object
- q = *(HeapWord**)q;
- assert(q > prev_q, "we should be moving forward through memory, q: " PTR_FORMAT ", prev_q: " PTR_FORMAT, p2i(q), p2i(prev_q));
+ debug_only(prev_obj = cur_obj);
+ // cur_obj is not a live object, instead it points at the next live object
+ cur_obj = *(HeapWord**)cur_obj;
+ assert(cur_obj > prev_obj, "we should be moving forward through memory, cur_obj: " PTR_FORMAT ", prev_obj: " PTR_FORMAT, p2i(cur_obj), p2i(prev_obj));
}
}
- assert(q == t, "just checking");
+ assert(cur_obj == end_of_live, "just checking");
}
+#ifdef ASSERT
+template <class SpaceType>
+inline void CompactibleSpace::verify_up_to_first_dead(SpaceType* space) {
+ HeapWord* cur_obj = space->bottom();
+
+ if (cur_obj < space->_end_of_live && space->_first_dead > cur_obj && !oop(cur_obj)->is_gc_marked()) {
+ // we have a chunk of the space which hasn't moved and we've reinitialized
+ // the mark word during the previous pass, so we can't use is_gc_marked for
+ // the traversal.
+ HeapWord* prev_obj = NULL;
+
+ while (cur_obj < space->_first_dead) {
+ size_t size = space->obj_size(cur_obj);
+ assert(!oop(cur_obj)->is_gc_marked(), "should be unmarked (special dense prefix handling)");
+ prev_obj = cur_obj;
+ cur_obj += size;
+ }
+ }
+}
+#endif
+
template <class SpaceType>
-inline void CompactibleSpace::scan_and_compact(SpaceType* space) {
- // Copy all live objects to their new location
- // Used by MarkSweep::mark_sweep_phase4()
-
- HeapWord* q = space->bottom();
- HeapWord* const t = space->_end_of_live;
- debug_only(HeapWord* prev_q = NULL);
-
- if (q < t && space->_first_dead > q && !oop(q)->is_gc_marked()) {
- #ifdef ASSERT // Debug only
- // we have a chunk of the space which hasn't moved and we've reinitialized
- // the mark word during the previous pass, so we can't use is_gc_marked for
- // the traversal.
- HeapWord* const end = space->_first_dead;
-
- while (q < end) {
- size_t size = space->obj_size(q);
- assert(!oop(q)->is_gc_marked(), "should be unmarked (special dense prefix handling)");
- prev_q = q;
- q += size;
- }
- #endif
-
- if (space->_first_dead == t) {
- q = t;
- } else {
- // $$$ Funky
- q = (HeapWord*) oop(space->_first_dead)->mark()->decode_pointer();
- }
- }
-
- const intx scan_interval = PrefetchScanIntervalInBytes;
- const intx copy_interval = PrefetchCopyIntervalInBytes;
- while (q < t) {
- if (!oop(q)->is_gc_marked()) {
- // mark is pointer to next marked oop
- debug_only(prev_q = q);
- q = (HeapWord*) oop(q)->mark()->decode_pointer();
- assert(q > prev_q, "we should be moving forward through memory");
- } else {
- // prefetch beyond q
- Prefetch::read(q, scan_interval);
-
- // size and destination
- size_t size = space->obj_size(q);
- HeapWord* compaction_top = (HeapWord*)oop(q)->forwardee();
-
- // prefetch beyond compaction_top
- Prefetch::write(compaction_top, copy_interval);
-
- // copy object and reinit its mark
- assert(q != compaction_top, "everything in this pass should be moving");
- Copy::aligned_conjoint_words(q, compaction_top, size);
- oop(compaction_top)->init_mark();
- assert(oop(compaction_top)->klass() != NULL, "should have a class");
-
- debug_only(prev_q = q);
- q += size;
- }
- }
-
+inline void CompactibleSpace::clear_empty_region(SpaceType* space) {
// Let's remember if we were empty before we did the compaction.
bool was_empty = space->used_region().is_empty();
// Reset space after compaction is complete
@@ -320,6 +286,65 @@
}
}
+template <class SpaceType>
+inline void CompactibleSpace::scan_and_compact(SpaceType* space) {
+ // Copy all live objects to their new location
+ // Used by MarkSweep::mark_sweep_phase4()
+
+ verify_up_to_first_dead(space);
+
+ HeapWord* const end_of_live = space->_end_of_live;
+
+ assert(space->_first_dead <= end_of_live, "Invariant. _first_dead: " PTR_FORMAT " <= end_of_live: " PTR_FORMAT, p2i(space->_first_dead), p2i(end_of_live));
+ if (space->_first_dead == end_of_live && !oop(space->bottom())->is_gc_marked()) {
+ // Nothing to compact. The space is either empty or all live object should be left in place.
+ clear_empty_region(space);
+ return;
+ }
+
+ const intx scan_interval = PrefetchScanIntervalInBytes;
+ const intx copy_interval = PrefetchCopyIntervalInBytes;
+
+ assert(space->bottom() < end_of_live, "bottom: " PTR_FORMAT " should be < end_of_live: " PTR_FORMAT, p2i(space->bottom()), p2i(end_of_live));
+ HeapWord* cur_obj = space->bottom();
+ if (space->_first_dead > cur_obj && !oop(cur_obj)->is_gc_marked()) {
+ // All object before _first_dead can be skipped. They should not be moved.
+ // A pointer to the first live object is stored at the memory location for _first_dead.
+ cur_obj = *(HeapWord**)(space->_first_dead);
+ }
+
+ debug_only(HeapWord* prev_obj = NULL);
+ while (cur_obj < end_of_live) {
+ if (!oop(cur_obj)->is_gc_marked()) {
+ debug_only(prev_obj = cur_obj);
+ // The first word of the dead object contains a pointer to the next live object or end of space.
+ cur_obj = *(HeapWord**)cur_obj;
+ assert(cur_obj > prev_obj, "we should be moving forward through memory");
+ } else {
+ // prefetch beyond q
+ Prefetch::read(cur_obj, scan_interval);
+
+ // size and destination
+ size_t size = space->obj_size(cur_obj);
+ HeapWord* compaction_top = (HeapWord*)oop(cur_obj)->forwardee();
+
+ // prefetch beyond compaction_top
+ Prefetch::write(compaction_top, copy_interval);
+
+ // copy object and reinit its mark
+ assert(cur_obj != compaction_top, "everything in this pass should be moving");
+ Copy::aligned_conjoint_words(cur_obj, compaction_top, size);
+ oop(compaction_top)->init_mark();
+ assert(oop(compaction_top)->klass() != NULL, "should have a class");
+
+ debug_only(prev_obj = cur_obj);
+ cur_obj += size;
+ }
+ }
+
+ clear_empty_region(space);
+}
+
size_t ContiguousSpace::scanned_block_size(const HeapWord* addr) const {
return oop(addr)->size();
}
--- a/hotspot/src/share/vm/logging/logPrefix.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/logging/logPrefix.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -55,6 +55,7 @@
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, ergo, cset)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, ergo, heap)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, ergo, ihop)) \
+ LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, ergo, refine)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, heap)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, heap, region)) \
LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, freelist)) \
--- a/hotspot/src/share/vm/memory/allocation.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/memory/allocation.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -143,8 +143,9 @@
mtTest = 0x0D, // Test type for verifying NMT
mtTracing = 0x0E, // memory used for Tracing
mtLogging = 0x0F, // memory for logging
- mtNone = 0x10, // undefined
- mt_number_of_types = 0x11 // number of memory types (mtDontTrack
+ mtArguments = 0x10, // memory for argument processing
+ mtNone = 0x11, // undefined
+ mt_number_of_types = 0x12 // number of memory types (mtDontTrack
// is not included as validate type)
};
--- a/hotspot/src/share/vm/prims/jvmtiExport.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/prims/jvmtiExport.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -2260,7 +2260,7 @@
if (env->is_enabled(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
EVT_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("JVMTI [%s] Evt vmobject alloc sent %s",
JvmtiTrace::safe_get_thread_name(thread),
- object==NULL? "NULL" : java_lang_Class::as_Klass(object)->external_name()));
+ object==NULL? "NULL" : object->klass()->external_name()));
JvmtiVMObjectAllocEventMark jem(thread, h());
JvmtiJavaThreadEventTransition jet(thread);
--- a/hotspot/src/share/vm/runtime/arguments.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/runtime/arguments.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -698,7 +698,7 @@
assert(total_len > 0, "empty sysclasspath not allowed");
// Copy the _items to a single string.
- char* cp = NEW_C_HEAP_ARRAY(char, total_len, mtInternal);
+ char* cp = NEW_C_HEAP_ARRAY(char, total_len, mtArguments);
char* cp_tmp = cp;
for (i = 0; i < _bcp_nitems; ++i) {
if (_items[i] != NULL) {
@@ -719,7 +719,7 @@
assert(str != NULL, "just checking");
if (path == NULL) {
size_t len = strlen(str) + 1;
- cp = NEW_C_HEAP_ARRAY(char, len, mtInternal);
+ cp = NEW_C_HEAP_ARRAY(char, len, mtArguments);
memcpy(cp, str, len); // copy the trailing null
} else {
const char separator = *os::path_separator();
@@ -728,7 +728,7 @@
size_t len = old_len + str_len + 2;
if (prepend) {
- cp = NEW_C_HEAP_ARRAY(char, len, mtInternal);
+ cp = NEW_C_HEAP_ARRAY(char, len, mtArguments);
char* cp_tmp = cp;
memcpy(cp_tmp, str, str_len);
cp_tmp += str_len;
@@ -736,7 +736,7 @@
memcpy(++cp_tmp, path, old_len + 1); // copy the trailing null
FREE_C_HEAP_ARRAY(char, path);
} else {
- cp = REALLOC_C_HEAP_ARRAY(char, path, len, mtInternal);
+ cp = REALLOC_C_HEAP_ARRAY(char, path, len, mtArguments);
char* cp_tmp = cp + old_len;
*cp_tmp = separator;
memcpy(++cp_tmp, str, str_len + 1); // copy the trailing null
@@ -758,7 +758,7 @@
/* Scan the directory for jars/zips, appending them to path. */
struct dirent *entry;
- char *dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(directory), mtInternal);
+ char *dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(directory), mtArguments);
while ((entry = os::readdir(dir, (dirent *) dbuf)) != NULL) {
const char* name = entry->d_name;
const char* ext = name + strlen(name) - 4;
@@ -766,7 +766,7 @@
(os::file_name_strcmp(ext, ".jar") == 0 ||
os::file_name_strcmp(ext, ".zip") == 0);
if (isJarOrZip) {
- char* jarpath = NEW_C_HEAP_ARRAY(char, directory_len + 2 + strlen(name), mtInternal);
+ char* jarpath = NEW_C_HEAP_ARRAY(char, directory_len + 2 + strlen(name), mtArguments);
sprintf(jarpath, "%s%s%s", directory, dir_sep, name);
path = add_to_path(path, jarpath, false);
FREE_C_HEAP_ARRAY(char, jarpath);
@@ -943,7 +943,7 @@
} else if (new_len == 0) {
value = old_value;
} else {
- char* buf = NEW_C_HEAP_ARRAY(char, old_len + 1 + new_len + 1, mtInternal);
+ char* buf = NEW_C_HEAP_ARRAY(char, old_len + 1 + new_len + 1, mtArguments);
// each new setting adds another LINE to the switch:
sprintf(buf, "%s\n%s", old_value, new_value);
value = buf;
@@ -1134,9 +1134,9 @@
// expand the array and add arg to the last element
if (*bldarray == NULL) {
- *bldarray = NEW_C_HEAP_ARRAY(char*, new_count, mtInternal);
+ *bldarray = NEW_C_HEAP_ARRAY(char*, new_count, mtArguments);
} else {
- *bldarray = REALLOC_C_HEAP_ARRAY(char*, *bldarray, new_count, mtInternal);
+ *bldarray = REALLOC_C_HEAP_ARRAY(char*, *bldarray, new_count, mtArguments);
}
(*bldarray)[*count] = os::strdup_check_oom(arg);
*count = new_count;
@@ -1400,7 +1400,7 @@
// property have a value, thus extract it and save to the
// allocated string
size_t key_len = eq - prop;
- char* tmp_key = AllocateHeap(key_len + 1, mtInternal);
+ char* tmp_key = AllocateHeap(key_len + 1, mtArguments);
strncpy(tmp_key, prop, key_len);
tmp_key[key_len] = '\0';
@@ -1422,7 +1422,7 @@
} else {
if (strcmp(key, "sun.java.command") == 0) {
char *old_java_command = _java_command;
- _java_command = os::strdup_check_oom(value, mtInternal);
+ _java_command = os::strdup_check_oom(value, mtArguments);
if (old_java_command != NULL) {
os::free(old_java_command);
}
@@ -1430,7 +1430,7 @@
const char* old_java_vendor_url_bug = _java_vendor_url_bug;
// save it in _java_vendor_url_bug, so JVM fatal error handler can access
// its value without going through the property list or making a Java call.
- _java_vendor_url_bug = os::strdup_check_oom(value, mtInternal);
+ _java_vendor_url_bug = os::strdup_check_oom(value, mtArguments);
if (old_java_vendor_url_bug != DEFAULT_VENDOR_URL_BUG) {
assert(old_java_vendor_url_bug != NULL, "_java_vendor_url_bug is NULL");
os::free((void *)old_java_vendor_url_bug);
@@ -1458,7 +1458,7 @@
if (old_value != NULL) {
buf_len += strlen(old_value) + 1;
}
- char* new_value = AllocateHeap(buf_len, mtInternal);
+ char* new_value = AllocateHeap(buf_len, mtArguments);
if (new_value == NULL) {
return false;
}
@@ -2095,8 +2095,8 @@
}
#if INCLUDE_ALL_GCS
- if (G1ConcRefinementThreads == 0) {
- FLAG_SET_DEFAULT(G1ConcRefinementThreads, ParallelGCThreads);
+ if (FLAG_IS_DEFAULT(G1ConcRefinementThreads)) {
+ FLAG_SET_ERGO(uint, G1ConcRefinementThreads, ParallelGCThreads);
}
#endif
@@ -2852,13 +2852,13 @@
if (tail != NULL) {
const char* pos = strchr(tail, ':');
size_t len = (pos == NULL) ? strlen(tail) : pos - tail;
- char* name = (char*)memcpy(NEW_C_HEAP_ARRAY(char, len + 1, mtInternal), tail, len);
+ char* name = (char*)memcpy(NEW_C_HEAP_ARRAY(char, len + 1, mtArguments), tail, len);
name[len] = '\0';
char *options = NULL;
if(pos != NULL) {
size_t len2 = strlen(pos+1) + 1; // options start after ':'. Final zero must be copied.
- options = (char*)memcpy(NEW_C_HEAP_ARRAY(char, len2, mtInternal), pos+1, len2);
+ options = (char*)memcpy(NEW_C_HEAP_ARRAY(char, len2, mtArguments), pos+1, len2);
}
#if !INCLUDE_JVMTI
if (strcmp(name, "jdwp") == 0) {
@@ -2875,12 +2875,12 @@
if(tail != NULL) {
const char* pos = strchr(tail, '=');
size_t len = (pos == NULL) ? strlen(tail) : pos - tail;
- char* name = strncpy(NEW_C_HEAP_ARRAY(char, len + 1, mtInternal), tail, len);
+ char* name = strncpy(NEW_C_HEAP_ARRAY(char, len + 1, mtArguments), tail, len);
name[len] = '\0';
char *options = NULL;
if(pos != NULL) {
- options = os::strdup_check_oom(pos + 1, mtInternal);
+ options = os::strdup_check_oom(pos + 1, mtArguments);
}
#if !INCLUDE_JVMTI
if (valid_jdwp_agent(name, is_absolute_path)) {
@@ -2899,7 +2899,7 @@
return JNI_ERR;
#else
if (tail != NULL) {
- char *options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(tail) + 1, mtInternal), tail);
+ char *options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(tail) + 1, mtArguments), tail);
add_init_agent("instrument", options, false);
// java agents need module java.instrument. Also -addmods ALL-SYSTEM because
// the java agent is in the unmamed module of the application class loader
@@ -3201,7 +3201,7 @@
size_t len = strlen(patch_dirs[x]);
if (len != 0) { // Ignore empty strings.
len += 11; // file_sep + "java.base" + null terminator.
- char* dir = NEW_C_HEAP_ARRAY(char, len, mtInternal);
+ char* dir = NEW_C_HEAP_ARRAY(char, len, mtArguments);
jio_snprintf(dir, len, "%s%cjava.base", patch_dirs[x], file_sep);
// See if Xpatch module path exists.
@@ -3507,7 +3507,7 @@
src ++;
}
- char* copy = os::strdup_check_oom(src, mtInternal);
+ char* copy = os::strdup_check_oom(src, mtArguments);
// trim all trailing empty paths
for (char* tail = copy + strlen(copy) - 1; tail >= copy && *tail == separator; tail--) {
@@ -3531,7 +3531,7 @@
if (dir == NULL) return false;
struct dirent *entry;
- char *dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(directory), mtInternal);
+ char *dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(directory), mtArguments);
bool hasJarFile = false;
while (!hasJarFile && (entry = os::readdir(dir, (dirent *) dbuf)) != NULL) {
const char* name = entry->d_name;
@@ -3557,7 +3557,7 @@
}
path = end;
} else {
- char* dirpath = NEW_C_HEAP_ARRAY(char, tmp_end - path + 1, mtInternal);
+ char* dirpath = NEW_C_HEAP_ARRAY(char, tmp_end - path + 1, mtArguments);
memcpy(dirpath, path, tmp_end - path);
dirpath[tmp_end - path] = '\0';
if (has_jar_files(dirpath)) {
@@ -3729,7 +3729,7 @@
jint set_args(GrowableArray<JavaVMOption>* options) {
_is_set = true;
JavaVMOption* options_arr = NEW_C_HEAP_ARRAY_RETURN_NULL(
- JavaVMOption, options->length(), mtInternal);
+ JavaVMOption, options->length(), mtArguments);
if (options_arr == NULL) {
return JNI_ENOMEM;
}
@@ -3784,7 +3784,7 @@
assert(vm_options_file_pos != -1, "vm_options_file_pos should be set");
int length = args->nOptions + args_to_insert->nOptions - 1;
- GrowableArray<JavaVMOption> *options = new (ResourceObj::C_HEAP, mtInternal)
+ GrowableArray<JavaVMOption> *options = new (ResourceObj::C_HEAP, mtArguments)
GrowableArray<JavaVMOption>(length, true); // Construct new option array
for (int i = 0; i < args->nOptions; i++) {
if (i == vm_options_file_pos) {
@@ -3861,7 +3861,7 @@
// '+ 1' for NULL termination even with max bytes
size_t bytes_alloc = stbuf.st_size + 1;
- char *buf = NEW_C_HEAP_ARRAY_RETURN_NULL(char, bytes_alloc, mtInternal);
+ char *buf = NEW_C_HEAP_ARRAY_RETURN_NULL(char, bytes_alloc, mtArguments);
if (NULL == buf) {
jio_fprintf(defaultStream::error_stream(),
"Could not allocate read buffer for options file parse\n");
@@ -3898,7 +3898,7 @@
}
jint Arguments::parse_options_buffer(const char* name, char* buffer, const size_t buf_len, ScopedVMInitArgs* vm_args) {
- GrowableArray<JavaVMOption> *options = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<JavaVMOption>(2, true); // Construct option array
+ GrowableArray<JavaVMOption> *options = new (ResourceObj::C_HEAP, mtArguments) GrowableArray<JavaVMOption>(2, true); // Construct option array
// some pointers to help with parsing
char *buffer_end = buffer + buf_len;
@@ -4002,13 +4002,13 @@
size_t jvm_path_len = strlen(jvm_path);
size_t file_sep_len = strlen(os::file_separator());
const size_t len = jvm_path_len + file_sep_len + 20;
- shared_archive_path = NEW_C_HEAP_ARRAY(char, len, mtInternal);
+ shared_archive_path = NEW_C_HEAP_ARRAY(char, len, mtArguments);
if (shared_archive_path != NULL) {
jio_snprintf(shared_archive_path, len, "%s%sclasses.jsa",
jvm_path, os::file_separator());
}
} else {
- shared_archive_path = os::strdup_check_oom(SharedArchiveFile, mtInternal);
+ shared_archive_path = os::strdup_check_oom(SharedArchiveFile, mtArguments);
}
return shared_archive_path;
}
--- a/hotspot/src/share/vm/runtime/arguments.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/runtime/arguments.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -47,7 +47,7 @@
// PathString is used as the underlying value container for a
// SystemProperty and for the string that represents the system
// boot class path, Arguments::_system_boot_class_path.
-class PathString : public CHeapObj<mtInternal> {
+class PathString : public CHeapObj<mtArguments> {
protected:
char* _value;
public:
@@ -57,7 +57,7 @@
if (_value != NULL) {
FreeHeap(_value);
}
- _value = AllocateHeap(strlen(value)+1, mtInternal);
+ _value = AllocateHeap(strlen(value)+1, mtArguments);
assert(_value != NULL, "Unable to allocate space for new path value");
if (_value != NULL) {
strcpy(_value, value);
@@ -76,7 +76,7 @@
if (_value != NULL) {
len += strlen(_value);
}
- sp = AllocateHeap(len+2, mtInternal);
+ sp = AllocateHeap(len+2, mtArguments);
assert(sp != NULL, "Unable to allocate space for new append path value");
if (sp != NULL) {
if (_value != NULL) {
@@ -97,7 +97,7 @@
if (value == NULL) {
_value = NULL;
} else {
- _value = AllocateHeap(strlen(value)+1, mtInternal);
+ _value = AllocateHeap(strlen(value)+1, mtArguments);
strcpy(_value, value);
}
}
@@ -143,7 +143,7 @@
if (key == NULL) {
_key = NULL;
} else {
- _key = AllocateHeap(strlen(key)+1, mtInternal);
+ _key = AllocateHeap(strlen(key)+1, mtArguments);
strcpy(_key, key);
}
_next = NULL;
@@ -154,7 +154,7 @@
// For use by -agentlib, -agentpath and -Xrun
-class AgentLibrary : public CHeapObj<mtInternal> {
+class AgentLibrary : public CHeapObj<mtArguments> {
friend class AgentLibraryList;
public:
// Is this library valid or not. Don't rely on os_lib == NULL as statically
@@ -189,12 +189,12 @@
// Constructor
AgentLibrary(const char* name, const char* options, bool is_absolute_path, void* os_lib) {
- _name = AllocateHeap(strlen(name)+1, mtInternal);
+ _name = AllocateHeap(strlen(name)+1, mtArguments);
strcpy(_name, name);
if (options == NULL) {
_options = NULL;
} else {
- _options = AllocateHeap(strlen(options)+1, mtInternal);
+ _options = AllocateHeap(strlen(options)+1, mtArguments);
strcpy(_options, options);
}
_is_absolute_path = is_absolute_path;
--- a/hotspot/src/share/vm/runtime/commandLineFlagConstraintList.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/runtime/commandLineFlagConstraintList.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -226,7 +226,7 @@
// Check the ranges of all flags that have them or print them out and exit if requested
void CommandLineFlagConstraintList::init(void) {
- _constraints = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<CommandLineFlagConstraint*>(INITIAL_CONSTRAINTS_SIZE, true);
+ _constraints = new (ResourceObj::C_HEAP, mtArguments) GrowableArray<CommandLineFlagConstraint*>(INITIAL_CONSTRAINTS_SIZE, true);
emit_constraint_no(NULL RUNTIME_FLAGS(EMIT_CONSTRAINT_DEVELOPER_FLAG,
EMIT_CONSTRAINT_PD_DEVELOPER_FLAG,
--- a/hotspot/src/share/vm/runtime/commandLineFlagConstraintList.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/runtime/commandLineFlagConstraintList.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -48,7 +48,7 @@
typedef Flag::Error (*CommandLineFlagConstraintFunc_size_t)(size_t value, bool verbose);
typedef Flag::Error (*CommandLineFlagConstraintFunc_double)(double value, bool verbose);
-class CommandLineFlagConstraint : public CHeapObj<mtInternal> {
+class CommandLineFlagConstraint : public CHeapObj<mtArguments> {
public:
// During VM initialization, constraint validation will be done order of ConstraintType.
enum ConstraintType {
--- a/hotspot/src/share/vm/runtime/commandLineFlagRangeList.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/runtime/commandLineFlagRangeList.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -292,7 +292,7 @@
// Check the ranges of all flags that have them
void CommandLineFlagRangeList::init(void) {
- _ranges = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<CommandLineFlagRange*>(INITIAL_RANGES_SIZE, true);
+ _ranges = new (ResourceObj::C_HEAP, mtArguments) GrowableArray<CommandLineFlagRange*>(INITIAL_RANGES_SIZE, true);
emit_range_no(NULL RUNTIME_FLAGS(EMIT_RANGE_DEVELOPER_FLAG,
EMIT_RANGE_PD_DEVELOPER_FLAG,
--- a/hotspot/src/share/vm/runtime/commandLineFlagRangeList.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/runtime/commandLineFlagRangeList.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -44,7 +44,7 @@
static void print(bool verbose, const char* msg, ...);
};
-class CommandLineFlagRange : public CHeapObj<mtInternal> {
+class CommandLineFlagRange : public CHeapObj<mtArguments> {
private:
const char* _name;
public:
--- a/hotspot/src/share/vm/runtime/globals.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/runtime/globals.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -1292,7 +1292,7 @@
const size_t length = Flag::numFlags - 1;
// Sort
- Flag** array = NEW_C_HEAP_ARRAY(Flag*, length, mtInternal);
+ Flag** array = NEW_C_HEAP_ARRAY(Flag*, length, mtArguments);
for (size_t i = 0; i < length; i++) {
array[i] = &flagTable[i];
}
@@ -1326,7 +1326,7 @@
const size_t length = Flag::numFlags - 1;
// Sort
- Flag** array = NEW_C_HEAP_ARRAY(Flag*, length, mtInternal);
+ Flag** array = NEW_C_HEAP_ARRAY(Flag*, length, mtArguments);
for (size_t i = 0; i < length; i++) {
array[i] = &flagTable[i];
}
--- a/hotspot/src/share/vm/services/nmtCommon.cpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/services/nmtCommon.cpp Fri Apr 22 00:08:54 2016 -0700
@@ -41,6 +41,7 @@
"Test",
"Tracing",
"Logging",
+ "Arguments",
"Unknown"
};
--- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp Fri Apr 22 00:08:54 2016 -0700
@@ -199,9 +199,6 @@
const size_t G = M*K;
const size_t HWperKB = K / sizeof(HeapWord);
-const jint min_jint = (jint)1 << (sizeof(jint)*BitsPerByte-1); // 0x80000000 == smallest jint
-const jint max_jint = (juint)min_jint - 1; // 0x7FFFFFFF == largest jint
-
// Constants for converting from a base unit to milli-base units. For
// example from seconds to milliseconds and microseconds
@@ -381,6 +378,14 @@
typedef jint s4;
typedef jlong s8;
+const jbyte min_jbyte = -(1 << 7); // smallest jbyte
+const jbyte max_jbyte = (1 << 7) - 1; // largest jbyte
+const jshort min_jshort = -(1 << 15); // smallest jshort
+const jshort max_jshort = (1 << 15) - 1; // largest jshort
+
+const jint min_jint = (jint)1 << (sizeof(jint)*BitsPerByte-1); // 0x80000000 == smallest jint
+const jint max_jint = (juint)min_jint - 1; // 0x7FFFFFFF == largest jint
+
//----------------------------------------------------------------------------------------------------
// JVM spec restrictions
--- a/hotspot/test/TEST.groups Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/test/TEST.groups Fri Apr 22 00:08:54 2016 -0700
@@ -338,6 +338,7 @@
sanity/ExecuteInternalVMTests.java \
gc/ \
-gc/g1/ \
+ -gc/stress \
-gc/survivorAlignment/TestPromotionFromSurvivorToTenuredAfterMinorGC.java \
-gc/cms/TestMBeanCMS.java \
-gc/metaspace/CompressedClassSpaceSizeInJmapHeap.java
@@ -346,7 +347,7 @@
sanity/ExecuteInternalVMTests.java
hotspot_fast_gc_gcold = \
- stress/gc/TestGCOld.java
+ gc/stress/TestGCOld.java
hotspot_fast_runtime = \
runtime/ \
--- a/hotspot/test/gc/arguments/TestG1ConcRefinementThreads.java Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/test/gc/arguments/TestG1ConcRefinementThreads.java Fri Apr 22 00:08:54 2016 -0700
@@ -38,7 +38,7 @@
public class TestG1ConcRefinementThreads {
- static final int AUTO_SELECT_THREADS_COUNT = 0;
+ static final int AUTO_SELECT_THREADS_COUNT = -1;
static final int PASSED_THREADS_COUNT = 11;
public static void main(String args[]) throws Exception {
@@ -49,8 +49,8 @@
// zero setting case
runG1ConcRefinementThreadsTest(
- new String[]{"-XX:G1ConcRefinementThreads=0"}, // automatically selected
- AUTO_SELECT_THREADS_COUNT /* set to zero */);
+ new String[]{"-XX:G1ConcRefinementThreads=0"},
+ 0);
// non-zero sestting case
runG1ConcRefinementThreadsTest(
@@ -77,7 +77,7 @@
private static void checkG1ConcRefinementThreadsConsistency(String output, int expectedValue) {
int actualValue = getIntValue("G1ConcRefinementThreads", output);
- if (expectedValue == 0) {
+ if (expectedValue == AUTO_SELECT_THREADS_COUNT) {
// If expectedValue is automatically selected, set it same as ParallelGCThreads.
expectedValue = getIntValue("ParallelGCThreads", output);
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/stress/TestGCOld.java Fri Apr 22 00:08:54 2016 -0700
@@ -0,0 +1,417 @@
+/*
+* Copyright (c) 2015, Oracle and/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 TestGCOld
+ * @key gc
+ * @key stress
+ * @requires vm.gc=="null"
+ * @summary Stress the GC by trying to make old objects more likely to be garbage than young objects.
+ * @run main/othervm -Xmx384M -XX:+UseSerialGC TestGCOld 50 1 20 10 10000
+ * @run main/othervm -Xmx384M -XX:+UseParallelGC TestGCOld 50 1 20 10 10000
+ * @run main/othervm -Xmx384M -XX:+UseParallelGC -XX:-UseParallelOldGC TestGCOld 50 1 20 10 10000
+ * @run main/othervm -Xmx384M -XX:+UseConcMarkSweepGC TestGCOld 50 1 20 10 10000
+ * @run main/othervm -Xmx384M -XX:+UseG1GC TestGCOld 50 1 20 10 10000
+ */
+
+import java.text.*;
+import java.util.Random;
+
+class TreeNode {
+ public TreeNode left, right;
+ public int val; // will always be the height of the tree
+}
+
+
+/* Args:
+ live-data-size: in megabytes (approximate, will be rounded down).
+ work: units of mutator non-allocation work per byte allocated,
+ (in unspecified units. This will affect the promotion rate
+ printed at the end of the run: more mutator work per step implies
+ fewer steps per second implies fewer bytes promoted per second.)
+ short/long ratio: ratio of short-lived bytes allocated to long-lived
+ bytes allocated.
+ pointer mutation rate: number of pointer mutations per step.
+ steps: number of steps to do.
+*/
+
+public class TestGCOld {
+
+ // Command-line parameters.
+
+ private static int size, workUnits, promoteRate, ptrMutRate, steps;
+
+ // Constants.
+
+ private static final int MEG = 1000000;
+ private static final int INSIGNIFICANT = 999; // this many bytes don't matter
+ private static final int BYTES_PER_WORD = 4;
+ private static final int BYTES_PER_NODE = 20; // bytes per TreeNode
+ private static final int WORDS_DEAD = 100; // size of young garbage object
+
+ private final static int treeHeight = 14;
+ private final static long treeSize = heightToBytes(treeHeight);
+
+ private static final String msg1
+ = "Usage: java TestGCOld <size> <work> <ratio> <mutation> <steps>";
+ private static final String msg2
+ = " where <size> is the live storage in megabytes";
+ private static final String msg3
+ = " <work> is the mutator work per step (arbitrary units)";
+ private static final String msg4
+ = " <ratio> is the ratio of short-lived to long-lived allocation";
+ private static final String msg5
+ = " <mutation> is the mutations per step";
+ private static final String msg6
+ = " <steps> is the number of steps";
+
+ // Counters (and global variables that discourage optimization)
+
+ private static long youngBytes = 0; // total young bytes allocated
+ private static long nodes = 0; // total tree nodes allocated
+ private static long actuallyMut = 0; // pointer mutations in old trees
+ private static long mutatorSum = 0; // checksum to discourage optimization
+ public static int[] aexport; // exported array to discourage opt
+
+ // Global variables.
+
+ private static TreeNode[] trees;
+ private static int where = 0; // roving index into trees
+ private static Random rnd = new Random();
+
+ // Returns the height of the given tree.
+
+ private static int height (TreeNode t) {
+ if (t == null) {
+ return 0;
+ }
+ else {
+ return 1 + Math.max (height (t.left), height (t.right));
+ }
+ }
+
+ // Returns the length of the shortest path in the given tree.
+
+ private static int shortestPath (TreeNode t) {
+ if (t == null) {
+ return 0;
+ }
+ else {
+ return 1 + Math.min (shortestPath (t.left), shortestPath (t.right));
+ }
+ }
+
+ // Returns the number of nodes in a balanced tree of the given height.
+
+ private static long heightToNodes (int h) {
+ if (h == 0) {
+ return 0;
+ }
+ else {
+ long n = 1;
+ while (h > 1) {
+ n = n + n;
+ h = h - 1;
+ }
+ return n + n - 1;
+ }
+ }
+
+ // Returns the number of bytes in a balanced tree of the given height.
+
+ private static long heightToBytes (int h) {
+ return BYTES_PER_NODE * heightToNodes (h);
+ }
+
+ // Returns the height of the largest balanced tree
+ // that has no more than the given number of nodes.
+
+ private static int nodesToHeight (long nodes) {
+ int h = 1;
+ long n = 1;
+ while (n + n - 1 <= nodes) {
+ n = n + n;
+ h = h + 1;
+ }
+ return h - 1;
+ }
+
+ // Returns the height of the largest balanced tree
+ // that occupies no more than the given number of bytes.
+
+ private static int bytesToHeight (long bytes) {
+ return nodesToHeight (bytes / BYTES_PER_NODE);
+ }
+
+ // Returns a newly allocated balanced binary tree of height h.
+
+ private static TreeNode makeTree(int h) {
+ if (h == 0) return null;
+ else {
+ TreeNode res = new TreeNode();
+ nodes++;
+ res.left = makeTree(h-1);
+ res.right = makeTree(h-1);
+ res.val = h;
+ return res;
+ }
+ }
+
+ // Allocates approximately size megabytes of trees and stores
+ // them into a global array.
+
+ private static void init() {
+ int ntrees = (int) ((size * MEG) / treeSize);
+ trees = new TreeNode[ntrees];
+
+ System.err.println("Allocating " + ntrees + " trees.");
+ System.err.println(" (" + (ntrees * treeSize) + " bytes)");
+ for (int i = 0; i < ntrees; i++) {
+ trees[i] = makeTree(treeHeight);
+ // doYoungGenAlloc(promoteRate*ntrees*treeSize, WORDS_DEAD);
+ }
+ System.err.println(" (" + nodes + " nodes)");
+
+ /* Allow any in-progress GC to catch up... */
+ // try { Thread.sleep(20000); } catch (InterruptedException x) {}
+ }
+
+ // Confirms that all trees are balanced and have the correct height.
+
+ private static void checkTrees() {
+ int ntrees = trees.length;
+ for (int i = 0; i < ntrees; i++) {
+ TreeNode t = trees[i];
+ int h1 = height(t);
+ int h2 = shortestPath(t);
+ if ((h1 != treeHeight) || (h2 != treeHeight)) {
+ System.err.println("*****BUG: " + h1 + " " + h2);
+ }
+ }
+ }
+
+ // Called only by replaceTree (below) and by itself.
+
+ private static void replaceTreeWork(TreeNode full, TreeNode partial, boolean dir) {
+ boolean canGoLeft = full.left != null && full.left.val > partial.val;
+ boolean canGoRight = full.right != null && full.right.val > partial.val;
+ if (canGoLeft && canGoRight) {
+ if (dir)
+ replaceTreeWork(full.left, partial, !dir);
+ else
+ replaceTreeWork(full.right, partial, !dir);
+ } else if (!canGoLeft && !canGoRight) {
+ if (dir)
+ full.left = partial;
+ else
+ full.right = partial;
+ } else if (!canGoLeft) {
+ full.left = partial;
+ } else {
+ full.right = partial;
+ }
+ }
+
+ // Given a balanced tree full and a smaller balanced tree partial,
+ // replaces an appropriate subtree of full by partial, taking care
+ // to preserve the shape of the full tree.
+
+ private static void replaceTree(TreeNode full, TreeNode partial) {
+ boolean dir = (partial.val % 2) == 0;
+ actuallyMut++;
+ replaceTreeWork(full, partial, dir);
+ }
+
+ // Allocates approximately n bytes of long-lived storage,
+ // replacing oldest existing long-lived storage.
+
+ private static void oldGenAlloc(long n) {
+ int full = (int) (n / treeSize);
+ long partial = n % treeSize;
+ // System.out.println("In oldGenAlloc, doing " + full + " full trees "
+ // + "and one partial tree of size " + partial);
+ for (int i = 0; i < full; i++) {
+ trees[where++] = makeTree(treeHeight);
+ if (where == trees.length) where = 0;
+ }
+ while (partial > INSIGNIFICANT) {
+ int h = bytesToHeight(partial);
+ TreeNode newTree = makeTree(h);
+ replaceTree(trees[where++], newTree);
+ if (where == trees.length) where = 0;
+ partial = partial - heightToBytes(h);
+ }
+ }
+
+ // Interchanges two randomly selected subtrees (of same size and depth).
+
+ private static void oldGenSwapSubtrees() {
+ // Randomly pick:
+ // * two tree indices
+ // * A depth
+ // * A path to that depth.
+ int index1 = rnd.nextInt(trees.length);
+ int index2 = rnd.nextInt(trees.length);
+ int depth = rnd.nextInt(treeHeight);
+ int path = rnd.nextInt();
+ TreeNode tn1 = trees[index1];
+ TreeNode tn2 = trees[index2];
+ for (int i = 0; i < depth; i++) {
+ if ((path & 1) == 0) {
+ tn1 = tn1.left;
+ tn2 = tn2.left;
+ } else {
+ tn1 = tn1.right;
+ tn2 = tn2.right;
+ }
+ path >>= 1;
+ }
+ TreeNode tmp;
+ if ((path & 1) == 0) {
+ tmp = tn1.left;
+ tn1.left = tn2.left;
+ tn2.left = tmp;
+ } else {
+ tmp = tn1.right;
+ tn1.right = tn2.right;
+ tn2.right = tmp;
+ }
+ actuallyMut += 2;
+ }
+
+ // Update "n" old-generation pointers.
+
+ private static void oldGenMut(long n) {
+ for (int i = 0; i < n/2; i++) {
+ oldGenSwapSubtrees();
+ }
+ }
+
+ // Does the amount of mutator work appropriate for n bytes of young-gen
+ // garbage allocation.
+
+ private static void doMutWork(long n) {
+ int sum = 0;
+ long limit = workUnits*n/10;
+ for (long k = 0; k < limit; k++) sum++;
+ // We don't want dead code elimination to eliminate the loop above.
+ mutatorSum = mutatorSum + sum;
+ }
+
+ // Allocate n bytes of young-gen garbage, in units of "nwords"
+ // words.
+
+ private static void doYoungGenAlloc(long n, int nwords) {
+ final int nbytes = nwords*BYTES_PER_WORD;
+ int allocated = 0;
+ while (allocated < n) {
+ aexport = new int[nwords];
+ /* System.err.println("Step"); */
+ allocated += nbytes;
+ }
+ youngBytes = youngBytes + allocated;
+ }
+
+ // Allocate "n" bytes of young-gen data; and do the
+ // corresponding amount of old-gen allocation and pointer
+ // mutation.
+
+ // oldGenAlloc may perform some mutations, so this code
+ // takes those mutations into account.
+
+ private static void doStep(long n) {
+ long mutations = actuallyMut;
+
+ doYoungGenAlloc(n, WORDS_DEAD);
+ doMutWork(n);
+ oldGenAlloc(n / promoteRate);
+ oldGenMut(Math.max(0L, (mutations + ptrMutRate) - actuallyMut));
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 5) {
+ System.err.println(msg1);
+ System.err.println(msg2);
+ System.err.println(msg3);
+ System.err.println(msg4);
+ System.err.println(msg5);
+ System.err.println(msg6);
+ return;
+ }
+
+ size = Integer.parseInt(args[0]);
+ workUnits = Integer.parseInt(args[1]);
+ promoteRate = Integer.parseInt(args[2]);
+ ptrMutRate = Integer.parseInt(args[3]);
+ steps = Integer.parseInt(args[4]);
+
+ System.out.println(size + " megabytes of live storage");
+ System.out.println(workUnits + " work units per step");
+ System.out.println("promotion ratio is 1:" + promoteRate);
+ System.out.println("pointer mutation rate is " + ptrMutRate);
+ System.out.println(steps + " steps");
+
+ init();
+// checkTrees();
+ youngBytes = 0;
+ nodes = 0;
+
+ System.err.println("Initialization complete...");
+
+ long start = System.currentTimeMillis();
+
+ for (int step = 0; step < steps; step++) {
+ doStep(MEG);
+ }
+
+ long end = System.currentTimeMillis();
+ float secs = ((float)(end-start))/1000.0F;
+
+// checkTrees();
+
+ NumberFormat nf = NumberFormat.getInstance();
+ nf.setMaximumFractionDigits(1);
+ System.out.println("\nTook " + nf.format(secs) + " sec in steady state.");
+ nf.setMaximumFractionDigits(2);
+ System.out.println("Allocated " + steps + " Mb of young gen garbage"
+ + " (= " + nf.format(((float)steps)/secs) +
+ " Mb/sec)");
+ System.out.println(" (actually allocated " +
+ nf.format(((float) youngBytes)/MEG) + " megabytes)");
+ float promoted = ((float)steps) / (float)promoteRate;
+ System.out.println("Promoted " + promoted +
+ " Mb (= " + nf.format(promoted/secs) + " Mb/sec)");
+ System.out.println(" (actually promoted " +
+ nf.format(((float) (nodes * BYTES_PER_NODE))/MEG) +
+ " megabytes)");
+ if (ptrMutRate != 0) {
+ System.out.println("Mutated " + actuallyMut +
+ " pointers (= " +
+ nf.format(actuallyMut/secs) + " ptrs/sec)");
+
+ }
+ // This output serves mainly to discourage optimization.
+ System.out.println("Checksum = " + (mutatorSum + aexport.length));
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/stress/TestMultiThreadStressRSet.java Fri Apr 22 00:08:54 2016 -0700
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2016, Oracle and/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.
+ */
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import sun.hotspot.WhiteBox;
+
+/*
+ * @test TestMultiThreadStressRSet.java
+ * @key stress
+ * @requires vm.gc=="G1" | vm.gc=="null"
+ * @requires os.maxMemory > 2G
+ *
+ * @summary Stress G1 Remembered Set using multiple threads
+ * @library /test/lib /testlibrary
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=1 -Xlog:gc
+ * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestMultiThreadStressRSet 10 4
+ *
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc
+ * -Xmx1G -XX:G1HeapRegionSize=8m -XX:MaxGCPauseMillis=1000 TestMultiThreadStressRSet 60 16
+ *
+ * @run main/othervm/timeout=700 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc
+ * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestMultiThreadStressRSet 600 32
+ */
+public class TestMultiThreadStressRSet {
+
+ private static final Random RND = new Random(2015 * 2016);
+ private static final WhiteBox WB = WhiteBox.getWhiteBox();
+ private static final int REF_SIZE = WB.getHeapOopSize();
+ private static final int REGION_SIZE = WB.g1RegionSize();
+
+ // How many regions to use for the storage
+ private static final int STORAGE_REGIONS = 20;
+
+ // Size a single obj in the storage
+ private static final int OBJ_SIZE = 1024;
+
+ // How many regions of young/old gen to use in the BUFFER
+ private static final int BUFFER_YOUNG_REGIONS = 60;
+ private static final int BUFFER_OLD_REGIONS = 40;
+
+ // Total number of objects in the storage.
+ private final int N;
+
+ // The storage of byte[]
+ private final List<Object> STORAGE;
+
+ // Where references to the Storage will be stored
+ private final List<Object[]> BUFFER;
+
+ // The length of a buffer element.
+ // RSet deals with "cards" (areas of 512 bytes), not with single refs
+ // So, to affect the RSet the BUFFER refs should be allocated in different
+ // memory cards.
+ private final int BUF_ARR_LEN = 100 * (512 / REF_SIZE);
+
+ // Total number of objects in the young/old buffers
+ private final int YOUNG;
+ private final int OLD;
+
+ // To cause Remembered Sets change their coarse level the test uses a window
+ // within STORAGE. All the BUFFER elements refer to only STORAGE objects
+ // from the current window. The window is defined by a range.
+ // The first element has got the index: 'windowStart',
+ // the last one: 'windowStart + windowSize - 1'
+ // The window is shifting periodically.
+ private int windowStart;
+ private final int windowSize;
+
+ // Counter of created worker threads
+ private int counter = 0;
+
+ private volatile String errorMessage = null;
+ private volatile boolean isEnough = false;
+
+ public static void main(String args[]) {
+ if (args.length != 2) {
+ throw new IllegalArgumentException("TEST BUG: wrong arg count " + args.length);
+ }
+ long time = Long.parseLong(args[0]);
+ int threads = Integer.parseInt(args[1]);
+ new TestMultiThreadStressRSet().test(time * 1000, threads);
+ }
+
+ /**
+ * Initiates test parameters, fills out the STORAGE and BUFFER.
+ */
+ public TestMultiThreadStressRSet() {
+
+ N = (REGION_SIZE - 1) * STORAGE_REGIONS / OBJ_SIZE + 1;
+ STORAGE = new ArrayList<>(N);
+ int bytes = OBJ_SIZE - 20;
+ for (int i = 0; i < N - 1; i++) {
+ STORAGE.add(new byte[bytes]);
+ }
+ STORAGE.add(new byte[REGION_SIZE / 2 + 100]); // humongous
+ windowStart = 0;
+ windowSize = REGION_SIZE / OBJ_SIZE;
+
+ BUFFER = new ArrayList<>();
+ int sizeOfBufferObject = 20 + REF_SIZE * BUF_ARR_LEN;
+ OLD = REGION_SIZE * BUFFER_OLD_REGIONS / sizeOfBufferObject;
+ YOUNG = REGION_SIZE * BUFFER_YOUNG_REGIONS / sizeOfBufferObject;
+ for (int i = 0; i < OLD + YOUNG; i++) {
+ BUFFER.add(new Object[BUF_ARR_LEN]);
+ }
+ }
+
+ /**
+ * Does the testing. Steps:
+ * <ul>
+ * <li> starts the Shifter thread
+ * <li> during the given time starts new Worker threads, keeping the number
+ * of live thread under limit.
+ * <li> stops the Shifter thread
+ * </ul>
+ *
+ * @param timeInMillis how long to stress
+ * @param maxThreads the maximum number of Worker thread working together.
+ */
+ public void test(long timeInMillis, int maxThreads) {
+ if (timeInMillis <= 0 || maxThreads <= 0) {
+ throw new IllegalArgumentException("TEST BUG: be positive!");
+ }
+ System.out.println("%% Time to work: " + timeInMillis / 1000 + "s");
+ System.out.println("%% Number of threads: " + maxThreads);
+ long finish = System.currentTimeMillis() + timeInMillis;
+ Shifter shift = new Shifter(this, 1000, (int) (windowSize * 0.9));
+ shift.start();
+ for (int i = 0; i < maxThreads; i++) {
+ new Worker(this, 100).start();
+ }
+ try {
+ while (System.currentTimeMillis() < finish && errorMessage == null) {
+ Thread.sleep(100);
+ }
+ } catch (Throwable t) {
+ printAllStackTraces(System.err);
+ t.printStackTrace(System.err);
+ this.errorMessage = t.getMessage();
+ } finally {
+ isEnough = true;
+ }
+ System.out.println("%% Total work cycles: " + counter);
+ if (errorMessage != null) {
+ throw new RuntimeException(errorMessage);
+ }
+ }
+
+ /**
+ * Returns an element from from the BUFFER (an object array) to keep
+ * references to the storage.
+ *
+ * @return an Object[] from buffer.
+ */
+ private Object[] getFromBuffer() {
+ int index = counter % (OLD + YOUNG);
+ synchronized (BUFFER) {
+ if (index < OLD) {
+ if (counter % 100 == (counter / 100) % 100) {
+ // need to generate garbage in the old gen to provoke mixed GC
+ return replaceInBuffer(index);
+ } else {
+ return BUFFER.get(index);
+ }
+ } else {
+ return replaceInBuffer(index);
+ }
+ }
+ }
+
+ private Object[] replaceInBuffer(int index) {
+ Object[] objs = new Object[BUF_ARR_LEN];
+ BUFFER.set(index, objs);
+ return objs;
+ }
+
+ /**
+ * Returns a random object from the current window within the storage.
+ * A storage element with index from windowStart to windowStart+windowSize.
+ *
+ * @return a random element from the current window within the storage.
+ */
+ private Object getRandomObject() {
+ int index = (windowStart + RND.nextInt(windowSize)) % N;
+ return STORAGE.get(index);
+ }
+
+ private static void printAllStackTraces(PrintStream ps) {
+ Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
+ for (Thread t : traces.keySet()) {
+ ps.println(t.toString() + " " + t.getState());
+ for (StackTraceElement traceElement : traces.get(t)) {
+ ps.println("\tat " + traceElement);
+ }
+ }
+ }
+
+ /**
+ * Thread to create a number of references from BUFFER to STORAGE.
+ */
+ private static class Worker extends Thread {
+
+ final TestMultiThreadStressRSet boss;
+ final int refs; // number of refs to OldGen
+
+ /**
+ * @param boss the tests
+ * @param refsToOldGen how many references to the OldGen to create
+ */
+ Worker(TestMultiThreadStressRSet boss, int refsToOldGen) {
+ this.boss = boss;
+ this.refs = refsToOldGen;
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (!boss.isEnough) {
+ Object[] objs = boss.getFromBuffer();
+ int step = objs.length / refs;
+ for (int i = 0; i < refs; i += step) {
+ objs[i] = boss.getRandomObject();
+ }
+ boss.counter++;
+ }
+ } catch (Throwable t) {
+ t.printStackTrace(System.out);
+ boss.errorMessage = t.getMessage();
+ }
+ }
+ }
+
+ /**
+ * Periodically shifts the current STORAGE window, removing references
+ * in BUFFER that refer to objects outside the window.
+ */
+ private static class Shifter extends Thread {
+
+ final TestMultiThreadStressRSet boss;
+ final int sleepTime;
+ final int shift;
+
+ Shifter(TestMultiThreadStressRSet boss, int sleepTime, int shift) {
+ this.boss = boss;
+ this.sleepTime = sleepTime;
+ this.shift = shift;
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (!boss.isEnough) {
+ Thread.sleep(sleepTime);
+ boss.windowStart += shift;
+ for (int i = 0; i < boss.OLD; i++) {
+ Object[] objs = boss.BUFFER.get(i);
+ for (int j = 0; j < objs.length; j++) {
+ objs[j] = null;
+ }
+ }
+ if (!WB.g1InConcurrentMark()) {
+ System.out.println("%% start CMC");
+ WB.g1StartConcMarkCycle();
+ } else {
+ System.out.println("%% CMC is already in progress");
+ }
+ }
+ } catch (Throwable t) {
+ t.printStackTrace(System.out);
+ boss.errorMessage = t.getMessage();
+ }
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/stress/TestStressIHOPMultiThread.java Fri Apr 22 00:08:54 2016 -0700
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2016, Oracle and/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 TestStressIHOPMultiThread
+ * @bug 8148397
+ * @key stress
+ * @summary Stress test for IHOP
+ * @requires vm.gc=="G1" | vm.gc=="null"
+ * @run main/othervm/timeout=200 -Xmx128m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
+ * -XX:+UseG1GC -XX:G1HeapRegionSize=1m -XX:+G1UseAdaptiveIHOP
+ * -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread1.log
+ * -Dtimeout=2 -DheapUsageMinBound=30 -DheapUsageMaxBound=80
+ * -Dthreads=2 TestStressIHOPMultiThread
+ * @run main/othervm/timeout=200 -Xmx256m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
+ * -XX:+UseG1GC -XX:G1HeapRegionSize=2m -XX:+G1UseAdaptiveIHOP
+ * -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread2.log
+ * -Dtimeout=2 -DheapUsageMinBound=60 -DheapUsageMaxBound=90
+ * -Dthreads=3 TestStressIHOPMultiThread
+ * @run main/othervm/timeout=200 -Xmx256m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
+ * -XX:+UseG1GC -XX:G1HeapRegionSize=4m -XX:-G1UseAdaptiveIHOP
+ * -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread3.log
+ * -Dtimeout=2 -DheapUsageMinBound=40 -DheapUsageMaxBound=90
+ * -Dthreads=5 TestStressIHOPMultiThread
+ * @run main/othervm/timeout=200 -Xmx128m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
+ * -XX:+UseG1GC -XX:G1HeapRegionSize=8m -XX:+G1UseAdaptiveIHOP
+ * -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread4.log
+ * -Dtimeout=2 -DheapUsageMinBound=20 -DheapUsageMaxBound=90
+ * -Dthreads=10 TestStressIHOPMultiThread
+ * @run main/othervm/timeout=200 -Xmx512m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
+ * -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:+G1UseAdaptiveIHOP
+ * -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread5.log
+ * -Dtimeout=2 -DheapUsageMinBound=20 -DheapUsageMaxBound=90
+ * -Dthreads=17 TestStressIHOPMultiThread
+ */
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Stress test for Adaptive IHOP. Starts a number of threads that fill and free
+ * specified amount of memory. Tests work with enabled IHOP logging.
+ *
+ */
+public class TestStressIHOPMultiThread {
+
+ public final static List<Object> GARBAGE = new LinkedList<>();
+
+ private final long HEAP_SIZE;
+ // Amount of memory to be allocated before iterations start
+ private final long HEAP_PREALLOC_SIZE;
+ // Amount of memory to be allocated and freed during iterations
+ private final long HEAP_ALLOC_SIZE;
+ private final int CHUNK_SIZE = 100000;
+
+ private final int TIMEOUT;
+ private final int THREADS;
+ private final int HEAP_LOW_BOUND;
+ private final int HEAP_HIGH_BOUND;
+
+ private volatile boolean running = true;
+ private final List<AllocationThread> threads;
+
+ public static void main(String[] args) throws InterruptedException {
+ new TestStressIHOPMultiThread().start();
+
+ }
+
+ TestStressIHOPMultiThread() {
+
+ TIMEOUT = Integer.getInteger("timeout") * 60;
+ THREADS = Integer.getInteger("threads");
+ HEAP_LOW_BOUND = Integer.getInteger("heapUsageMinBound");
+ HEAP_HIGH_BOUND = Integer.getInteger("heapUsageMaxBound");
+ HEAP_SIZE = Runtime.getRuntime().maxMemory();
+
+ HEAP_PREALLOC_SIZE = HEAP_SIZE * HEAP_LOW_BOUND / 100;
+ HEAP_ALLOC_SIZE = HEAP_SIZE * (HEAP_HIGH_BOUND - HEAP_LOW_BOUND) / 100;
+
+ threads = new ArrayList<>(THREADS);
+ }
+
+ public void start() throws InterruptedException {
+ fill();
+ createThreads();
+ waitForStress();
+ stressDone();
+ waitForFinish();
+ }
+
+ /**
+ * Fills HEAP_PREALLOC_SIZE bytes of garbage.
+ */
+ private void fill() {
+ long allocated = 0;
+ while (allocated < HEAP_PREALLOC_SIZE) {
+ GARBAGE.add(new byte[CHUNK_SIZE]);
+ allocated += CHUNK_SIZE;
+ }
+ }
+
+ /**
+ * Creates a number of threads which will fill and free amount of memory.
+ */
+ private void createThreads() {
+ for (int i = 0; i < THREADS; ++i) {
+ System.out.println("Create thread " + i);
+ AllocationThread thread =new TestStressIHOPMultiThread.AllocationThread(i, HEAP_ALLOC_SIZE / THREADS);
+ // Put reference to thread garbage into common garbage for avoiding possible optimization.
+ GARBAGE.add(thread.getList());
+ threads.add(thread);
+ }
+ threads.forEach(t -> t.start());
+ }
+
+ /**
+ * Wait each thread for finishing
+ */
+ private void waitForFinish() {
+ threads.forEach(thread -> {
+ thread.silentJoin();
+ });
+ }
+
+ private boolean isRunning() {
+ return running;
+ }
+
+ private void stressDone() {
+ running = false;
+ }
+
+ private void waitForStress() throws InterruptedException {
+ Thread.sleep(TIMEOUT * 1000);
+ }
+
+ private class AllocationThread extends Thread {
+
+ private final List<Object> garbage;
+
+ private final long amountOfGarbage;
+ private final int threadId;
+
+ public AllocationThread(int id, long amount) {
+ super("Thread " + id);
+ threadId = id;
+ amountOfGarbage = amount;
+ garbage = new LinkedList<>();
+ }
+
+ /**
+ * Returns list of garbage.
+ * @return List with thread garbage.
+ */
+ public List<Object> getList(){
+ return garbage;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("Start the thread " + threadId);
+ while (TestStressIHOPMultiThread.this.isRunning()) {
+ allocate(amountOfGarbage);
+ free();
+ }
+ }
+
+ private void silentJoin() {
+ System.out.println("Join the thread " + threadId);
+ try {
+ join();
+ } catch (InterruptedException ie) {
+ throw new RuntimeException(ie);
+ }
+ }
+
+ /**
+ * Allocates thread local garbage
+ */
+ private void allocate(long amount) {
+ long allocated = 0;
+ while (allocated < amount && TestStressIHOPMultiThread.this.isRunning()) {
+ garbage.add(new byte[CHUNK_SIZE]);
+ allocated += CHUNK_SIZE;
+ }
+ }
+
+ /**
+ * Frees thread local garbage
+ */
+ private void free() {
+ garbage.clear();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/stress/TestStressRSetCoarsening.java Fri Apr 22 00:08:54 2016 -0700
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2016, Oracle and/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.
+ */
+
+import java.util.concurrent.TimeoutException;
+import sun.hotspot.WhiteBox;
+
+/*
+ * @test TestStressRSetCoarsening.java
+ * @key stress
+ * @bug 8146984 8147087
+ * @requires vm.gc=="G1" | vm.gc=="null"
+ * @requires os.maxMemory > 3G
+ *
+ * @summary Stress G1 Remembered Set by creating a lot of cross region links
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary /test/lib
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm/timeout=300
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000
+ * -Xmx500m -XX:G1HeapRegionSize=1m TestStressRSetCoarsening 1 0 300
+ * @run main/othervm/timeout=300
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000
+ * -Xmx500m -XX:G1HeapRegionSize=8m TestStressRSetCoarsening 1 10 300
+ * @run main/othervm/timeout=300
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000
+ * -Xmx500m -XX:G1HeapRegionSize=32m TestStressRSetCoarsening 42 10 300
+ * @run main/othervm/timeout=300
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000
+ * -Xmx500m -XX:G1HeapRegionSize=1m TestStressRSetCoarsening 2 0 300
+ * @run main/othervm/timeout=1800
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000
+ * -Xmx1G -XX:G1HeapRegionSize=1m TestStressRSetCoarsening 500 0 1800
+ * @run main/othervm/timeout=1800
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000
+ * -Xmx1G -XX:G1HeapRegionSize=1m TestStressRSetCoarsening 10 10 1800
+ */
+
+/**
+ * What the test does.
+ * Preparation stage:
+ * Fill out ~90% of the heap with objects, each object is an object array.
+ * If we want to allocate K objects per region, we calculate N to meet:
+ * sizeOf(Object[N]) ~= regionSize / K
+ * Stress stage:
+ * No more allocation, so no more GC.
+ * We will perform a number of iterations. On each iteration i,
+ * for each pair of regions Rx and Ry we will set c[i] references
+ * from Rx to Ry. If c[i] less than c[i-1] at the end of iteration
+ * concurrent mark cycle will be initiated (to recalculate remembered sets).
+ * As the result RSet will be growing up and down, up and down many times.
+ *
+ * The test expects: no crash and no timeouts.
+ *
+ * Test Parameters:
+ * args[0] - number of objects per Heap Region (1 - means humongous)
+ * args[1] - number of regions to refresh to provoke GC at the end of cycle.
+ * (0 - means no GC, i.e. no reading from RSet)
+ * args[2] - timeout in seconds (to stop execution to avoid jtreg timeout)
+ */
+public class TestStressRSetCoarsening {
+
+ public static void main(String... args) throws InterruptedException {
+ if (args.length != 3) {
+ throw new IllegalArgumentException("Wrong number of arguments " + args.length);
+ }
+ int objectsPerRegion = Integer.parseInt(args[0]); // 1 means humongous
+ int regsToRefresh = Integer.parseInt(args[1]); // 0 means no regions to refresh at the end of cycle
+ int timeout = Integer.parseInt(args[2]); // in seconds, test should stop working eariler
+ new TestStressRSetCoarsening(objectsPerRegion, regsToRefresh, timeout).go();
+ }
+
+ private static final long KB = 1024;
+ private static final long MB = 1024 * KB;
+
+ private static final WhiteBox WB = WhiteBox.getWhiteBox();
+
+ public final Object[][] storage;
+
+ /**
+ * Number of objects per region. This is a test parameter.
+ */
+ public final int K;
+
+ /**
+ * Length of object array: sizeOf(Object[N]) ~= regionSize / K
+ * N will be calculated as function of K.
+ */
+ public final int N;
+
+ /**
+ * How many regions involved into testing.
+ * Will be calculated as heapFractionToAllocate * freeRegionCount.
+ */
+ public final int regionCount;
+
+ /**
+ * How much heap to use.
+ */
+ public final float heapFractionToAllocate = 0.9f;
+
+ /**
+ * How many regions to be refreshed at the end of cycle.
+ * This is a test parameter.
+ */
+ public final int regsToRefresh;
+
+ /**
+ * Initial time.
+ */
+ public final long start;
+
+ /**
+ * Time when the test should stop working.
+ */
+ public final long finishAt;
+
+ /**
+ * Does pre-calculation and allocate necessary objects.
+ *
+ * @param objPerRegions how many objects per G1 heap region
+ */
+ TestStressRSetCoarsening(int objPerRegions, int regsToRefresh, int timeout) {
+ this.K = objPerRegions;
+ this.regsToRefresh = regsToRefresh;
+ this.start = System.currentTimeMillis();
+ this.finishAt = start + timeout * 900; // 10% ahead of jtreg timeout
+
+ long regionSize = WB.g1RegionSize();
+
+ // How many free regions
+ Runtime rt = Runtime.getRuntime();
+ long used = rt.totalMemory() - rt.freeMemory();
+ long totalFree = rt.maxMemory() - used;
+ regionCount = (int) ((totalFree / regionSize) * heapFractionToAllocate);
+ long toAllocate = regionCount * regionSize;
+ System.out.println("%% Test parameters");
+ System.out.println("%% Objects per region : " + K);
+ System.out.println("%% Heap fraction to allocate : " + (int) (heapFractionToAllocate * 100) + "%");
+ System.out.println("%% Regions to refresh to provoke GC: " + regsToRefresh);
+
+ System.out.println("%% Memory");
+ System.out.println("%% used : " + used / MB + "M");
+ System.out.println("%% available : " + totalFree / MB + "M");
+ System.out.println("%% to allocate : " + toAllocate / MB + "M");
+ System.out.println("%% (in regs) : " + regionCount);
+ System.out.println("%% G1 Region Size: " + regionSize / MB + "M");
+
+ int refSize = WB.getHeapOopSize();
+
+ // Calculate N: K*sizeOf(Object[N]) ~= regionSize
+ // sizeOf(Object[N]) ~= (N+4)*refSize
+ // ==>
+ // N = regionSize / K / refSize - 4;
+ int n = (int) ((regionSize / K) / refSize) - 5; // best guess
+ long objSize = WB.getObjectSize(new Object[n]);
+ while (K*objSize > regionSize) { // adjust to avoid OOME
+ n = n - 1;
+ objSize = WB.getObjectSize(new Object[n]);
+ }
+ N = n;
+
+ /*
+ * --------------
+ * region0 storage[0] = new Object[N]
+ * ...
+ * storage[K-1] = new Object[N]
+ * ---------------
+ * region1 storage[K] = new Object[N]
+ * ...
+ * storage[2*K - 1] = new Object[N]
+ * --------------
+ * ...
+ * --------------
+ * regionX storage[X*K] = new Object[N]
+ * ...
+ * storage[(X+1)*K -1] = new Object[N]
+ * where X = HeapFraction * TotalRegions
+ * -------------
+ */
+ System.out.println("%% Objects");
+ System.out.println("%% N (array length) : " + N);
+ System.out.println("%% K (objects in regions): " + K);
+ System.out.println("%% Object size : " + objSize +
+ " (sizeOf(new Object[" + N + "])");
+ System.out.println("%% Reference size : " + refSize);
+
+ storage = new Object[regionCount * K][];
+ for (int i = 0; i < storage.length; i++) {
+ storage[i] = new Object[N];
+ }
+ }
+
+ public void go() throws InterruptedException {
+ // threshold for sparce -> fine
+ final int FINE = WB.getIntxVMFlag("G1RSetSparseRegionEntries").intValue();
+
+ // threshold for fine -> coarse
+ final int COARSE = WB.getIntxVMFlag("G1RSetRegionEntries").intValue();
+
+ // regToRegRefCounts - array of reference counts from region to region
+ // at the the end of iteration.
+ // The number of test iterations is array length - 1.
+ // If c[i] > c[i-1] then during the iteration i more references will
+ // be created.
+ // If c[i] < c[i-1] then some referenes will be cleaned.
+ int[] regToRegRefCounts = {0, FINE / 2, 0, FINE, (FINE + COARSE) / 2, 0,
+ COARSE, COARSE + 10, FINE + 1, FINE / 2, 0};
+
+ // For progress tracking
+ int[] progress = new int[regToRegRefCounts.length];
+ progress[0] = 0;
+ for (int i = 1; i < regToRegRefCounts.length; i++) {
+ progress[i] = progress[i - 1] + Math.abs(regToRegRefCounts[i] - regToRegRefCounts[i - 1]);
+ }
+ try {
+ for (int i = 1; i < regToRegRefCounts.length; i++) {
+ int pre = regToRegRefCounts[i - 1];
+ int cur = regToRegRefCounts[i];
+ float prog = ((float) progress[i - 1] / progress[progress.length - 1]);
+
+ System.out.println("%% step " + i
+ + " out of " + (regToRegRefCounts.length - 1)
+ + " (~" + (int) (100 * prog) + "% done)");
+ System.out.println("%% " + pre + " --> " + cur);
+ for (int to = 0; to < regionCount; to++) {
+ // Select a celebrity object that we will install references to.
+ // The celebrity will be referred from all other regions.
+ // If the number of references after should be less than they
+ // were before, select NULL.
+ Object celebrity = cur > pre ? storage[to * K] : null;
+ for (int from = 0; from < regionCount; from++) {
+ if (to == from) {
+ continue; // no need to refer to itself
+ }
+
+ int step = cur > pre ? +1 : -1;
+ for (int rn = pre; rn != cur; rn += step) {
+ storage[getY(to, from, rn)][getX(to, from, rn)] = celebrity;
+ if (System.currentTimeMillis() > finishAt) {
+ throw new TimeoutException();
+ }
+ }
+ }
+ }
+ if (pre > cur) {
+ // Number of references went down.
+ // Need to provoke recalculation of RSet.
+ WB.g1StartConcMarkCycle();
+ while (WB.g1InConcurrentMark()) {
+ Thread.sleep(1);
+ }
+ }
+
+ // To force the use of rememebered set entries we need to provoke a GC.
+ // To induce some fragmentation, and some mixed GCs, we need
+ // to make a few objects unreachable.
+ for (int toClean = i * regsToRefresh; toClean < (i + 1) * regsToRefresh; toClean++) {
+ int to = toClean % regionCount;
+ // Need to remove all references from all regions to the region 'to'
+ for (int from = 0; from < regionCount; from++) {
+ if (to == from) {
+ continue; // no need to refer to itself
+ }
+ for (int rn = 0; rn <= cur; rn++) {
+ storage[getY(to, from, rn)][getX(to, from, rn)] = null;
+ }
+ }
+ // 'Refresh' storage elements for the region 'to'
+ // After that loop all 'old' objects in the region 'to'
+ // should become unreachable.
+ for (int k = 0; k < K; k++) {
+ storage[(to * K + k) % storage.length] = new Object[N];
+ }
+ }
+ }
+ } catch (TimeoutException e) {
+ System.out.println("%% TIMEOUT!!!");
+ }
+ long now = System.currentTimeMillis();
+ System.out.println("%% Summary");
+ System.out.println("%% Time spent : " + ((now - start) / 1000) + " seconds");
+ System.out.println("%% Free memory left : " + Runtime.getRuntime().freeMemory() / KB + "K");
+ System.out.println("%% Test passed");
+ }
+
+ /**
+ * Returns X index in the Storage of the reference #rn from the region
+ * 'from' to the region 'to'.
+ *
+ * @param to region # to refer to
+ * @param from region # to refer from
+ * @param rn number of reference
+ *
+ * @return X index in the range: [0 ... N-1]
+ */
+ private int getX(int to, int from, int rn) {
+ return (rn * regionCount + to) % N;
+ }
+
+ /**
+ * Returns Y index in the Storage of the reference #rn from the region
+ * 'from' to the region 'to'.
+ *
+ * @param to region # to refer to
+ * @param from region # to refer from
+ * @param rn number of reference
+ *
+ * @return Y index in the range: [0 ... K*regionCount -1]
+ */
+ private int getY(int to, int from, int rn) {
+ return ((rn * regionCount + to) / N + from * K) % (regionCount * K);
+ }
+}
+
--- a/hotspot/test/runtime/modules/AccessCheck/ExportAllUnnamed.java Thu Apr 21 13:36:14 2016 -0700
+++ b/hotspot/test/runtime/modules/AccessCheck/ExportAllUnnamed.java Fri Apr 22 00:08:54 2016 -0700
@@ -28,11 +28,12 @@
* @summary Test if package p2 in module m2 is exported to all unnamed,
* then class p1.c1 in an unnamed module can read p2.c2 in module m2.
* @library /testlibrary /test/lib
+ * @modules java.base/jdk.internal.module
* @compile myloaders/MySameClassLoader.java
* @compile p2/c2.java
* @compile p1/c1.java
- * @compile -XaddExports:java.base/jdk.internal.module=ALL-UNNAMED ExportAllUnnamed.java
- * @run main/othervm -XaddExports:java.base/jdk.internal.module=ALL-UNNAMED -Xbootclasspath/a:. ExportAllUnnamed
+ * @build ExportAllUnnamed
+ * @run main/othervm -Xbootclasspath/a:. ExportAllUnnamed
*/
import static jdk.test.lib.Asserts.*;
--- a/hotspot/test/stress/gc/TestGCOld.java Thu Apr 21 13:36:14 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,417 +0,0 @@
-/*
-* Copyright (c) 2015, Oracle and/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 TestGCOld
- * @key gc
- * @key stress
- * @requires vm.gc=="null"
- * @summary Stress the GC by trying to make old objects more likely to be garbage than young objects.
- * @run main/othervm -Xmx384M -XX:+UseSerialGC TestGCOld 50 1 20 10 10000
- * @run main/othervm -Xmx384M -XX:+UseParallelGC TestGCOld 50 1 20 10 10000
- * @run main/othervm -Xmx384M -XX:+UseParallelGC -XX:-UseParallelOldGC TestGCOld 50 1 20 10 10000
- * @run main/othervm -Xmx384M -XX:+UseConcMarkSweepGC TestGCOld 50 1 20 10 10000
- * @run main/othervm -Xmx384M -XX:+UseG1GC TestGCOld 50 1 20 10 10000
- */
-
-import java.text.*;
-import java.util.Random;
-
-class TreeNode {
- public TreeNode left, right;
- public int val; // will always be the height of the tree
-}
-
-
-/* Args:
- live-data-size: in megabytes (approximate, will be rounded down).
- work: units of mutator non-allocation work per byte allocated,
- (in unspecified units. This will affect the promotion rate
- printed at the end of the run: more mutator work per step implies
- fewer steps per second implies fewer bytes promoted per second.)
- short/long ratio: ratio of short-lived bytes allocated to long-lived
- bytes allocated.
- pointer mutation rate: number of pointer mutations per step.
- steps: number of steps to do.
-*/
-
-public class TestGCOld {
-
- // Command-line parameters.
-
- private static int size, workUnits, promoteRate, ptrMutRate, steps;
-
- // Constants.
-
- private static final int MEG = 1000000;
- private static final int INSIGNIFICANT = 999; // this many bytes don't matter
- private static final int BYTES_PER_WORD = 4;
- private static final int BYTES_PER_NODE = 20; // bytes per TreeNode
- private static final int WORDS_DEAD = 100; // size of young garbage object
-
- private final static int treeHeight = 14;
- private final static long treeSize = heightToBytes(treeHeight);
-
- private static final String msg1
- = "Usage: java TestGCOld <size> <work> <ratio> <mutation> <steps>";
- private static final String msg2
- = " where <size> is the live storage in megabytes";
- private static final String msg3
- = " <work> is the mutator work per step (arbitrary units)";
- private static final String msg4
- = " <ratio> is the ratio of short-lived to long-lived allocation";
- private static final String msg5
- = " <mutation> is the mutations per step";
- private static final String msg6
- = " <steps> is the number of steps";
-
- // Counters (and global variables that discourage optimization)
-
- private static long youngBytes = 0; // total young bytes allocated
- private static long nodes = 0; // total tree nodes allocated
- private static long actuallyMut = 0; // pointer mutations in old trees
- private static long mutatorSum = 0; // checksum to discourage optimization
- public static int[] aexport; // exported array to discourage opt
-
- // Global variables.
-
- private static TreeNode[] trees;
- private static int where = 0; // roving index into trees
- private static Random rnd = new Random();
-
- // Returns the height of the given tree.
-
- private static int height (TreeNode t) {
- if (t == null) {
- return 0;
- }
- else {
- return 1 + Math.max (height (t.left), height (t.right));
- }
- }
-
- // Returns the length of the shortest path in the given tree.
-
- private static int shortestPath (TreeNode t) {
- if (t == null) {
- return 0;
- }
- else {
- return 1 + Math.min (shortestPath (t.left), shortestPath (t.right));
- }
- }
-
- // Returns the number of nodes in a balanced tree of the given height.
-
- private static long heightToNodes (int h) {
- if (h == 0) {
- return 0;
- }
- else {
- long n = 1;
- while (h > 1) {
- n = n + n;
- h = h - 1;
- }
- return n + n - 1;
- }
- }
-
- // Returns the number of bytes in a balanced tree of the given height.
-
- private static long heightToBytes (int h) {
- return BYTES_PER_NODE * heightToNodes (h);
- }
-
- // Returns the height of the largest balanced tree
- // that has no more than the given number of nodes.
-
- private static int nodesToHeight (long nodes) {
- int h = 1;
- long n = 1;
- while (n + n - 1 <= nodes) {
- n = n + n;
- h = h + 1;
- }
- return h - 1;
- }
-
- // Returns the height of the largest balanced tree
- // that occupies no more than the given number of bytes.
-
- private static int bytesToHeight (long bytes) {
- return nodesToHeight (bytes / BYTES_PER_NODE);
- }
-
- // Returns a newly allocated balanced binary tree of height h.
-
- private static TreeNode makeTree(int h) {
- if (h == 0) return null;
- else {
- TreeNode res = new TreeNode();
- nodes++;
- res.left = makeTree(h-1);
- res.right = makeTree(h-1);
- res.val = h;
- return res;
- }
- }
-
- // Allocates approximately size megabytes of trees and stores
- // them into a global array.
-
- private static void init() {
- int ntrees = (int) ((size * MEG) / treeSize);
- trees = new TreeNode[ntrees];
-
- System.err.println("Allocating " + ntrees + " trees.");
- System.err.println(" (" + (ntrees * treeSize) + " bytes)");
- for (int i = 0; i < ntrees; i++) {
- trees[i] = makeTree(treeHeight);
- // doYoungGenAlloc(promoteRate*ntrees*treeSize, WORDS_DEAD);
- }
- System.err.println(" (" + nodes + " nodes)");
-
- /* Allow any in-progress GC to catch up... */
- // try { Thread.sleep(20000); } catch (InterruptedException x) {}
- }
-
- // Confirms that all trees are balanced and have the correct height.
-
- private static void checkTrees() {
- int ntrees = trees.length;
- for (int i = 0; i < ntrees; i++) {
- TreeNode t = trees[i];
- int h1 = height(t);
- int h2 = shortestPath(t);
- if ((h1 != treeHeight) || (h2 != treeHeight)) {
- System.err.println("*****BUG: " + h1 + " " + h2);
- }
- }
- }
-
- // Called only by replaceTree (below) and by itself.
-
- private static void replaceTreeWork(TreeNode full, TreeNode partial, boolean dir) {
- boolean canGoLeft = full.left != null && full.left.val > partial.val;
- boolean canGoRight = full.right != null && full.right.val > partial.val;
- if (canGoLeft && canGoRight) {
- if (dir)
- replaceTreeWork(full.left, partial, !dir);
- else
- replaceTreeWork(full.right, partial, !dir);
- } else if (!canGoLeft && !canGoRight) {
- if (dir)
- full.left = partial;
- else
- full.right = partial;
- } else if (!canGoLeft) {
- full.left = partial;
- } else {
- full.right = partial;
- }
- }
-
- // Given a balanced tree full and a smaller balanced tree partial,
- // replaces an appropriate subtree of full by partial, taking care
- // to preserve the shape of the full tree.
-
- private static void replaceTree(TreeNode full, TreeNode partial) {
- boolean dir = (partial.val % 2) == 0;
- actuallyMut++;
- replaceTreeWork(full, partial, dir);
- }
-
- // Allocates approximately n bytes of long-lived storage,
- // replacing oldest existing long-lived storage.
-
- private static void oldGenAlloc(long n) {
- int full = (int) (n / treeSize);
- long partial = n % treeSize;
- // System.out.println("In oldGenAlloc, doing " + full + " full trees "
- // + "and one partial tree of size " + partial);
- for (int i = 0; i < full; i++) {
- trees[where++] = makeTree(treeHeight);
- if (where == trees.length) where = 0;
- }
- while (partial > INSIGNIFICANT) {
- int h = bytesToHeight(partial);
- TreeNode newTree = makeTree(h);
- replaceTree(trees[where++], newTree);
- if (where == trees.length) where = 0;
- partial = partial - heightToBytes(h);
- }
- }
-
- // Interchanges two randomly selected subtrees (of same size and depth).
-
- private static void oldGenSwapSubtrees() {
- // Randomly pick:
- // * two tree indices
- // * A depth
- // * A path to that depth.
- int index1 = rnd.nextInt(trees.length);
- int index2 = rnd.nextInt(trees.length);
- int depth = rnd.nextInt(treeHeight);
- int path = rnd.nextInt();
- TreeNode tn1 = trees[index1];
- TreeNode tn2 = trees[index2];
- for (int i = 0; i < depth; i++) {
- if ((path & 1) == 0) {
- tn1 = tn1.left;
- tn2 = tn2.left;
- } else {
- tn1 = tn1.right;
- tn2 = tn2.right;
- }
- path >>= 1;
- }
- TreeNode tmp;
- if ((path & 1) == 0) {
- tmp = tn1.left;
- tn1.left = tn2.left;
- tn2.left = tmp;
- } else {
- tmp = tn1.right;
- tn1.right = tn2.right;
- tn2.right = tmp;
- }
- actuallyMut += 2;
- }
-
- // Update "n" old-generation pointers.
-
- private static void oldGenMut(long n) {
- for (int i = 0; i < n/2; i++) {
- oldGenSwapSubtrees();
- }
- }
-
- // Does the amount of mutator work appropriate for n bytes of young-gen
- // garbage allocation.
-
- private static void doMutWork(long n) {
- int sum = 0;
- long limit = workUnits*n/10;
- for (long k = 0; k < limit; k++) sum++;
- // We don't want dead code elimination to eliminate the loop above.
- mutatorSum = mutatorSum + sum;
- }
-
- // Allocate n bytes of young-gen garbage, in units of "nwords"
- // words.
-
- private static void doYoungGenAlloc(long n, int nwords) {
- final int nbytes = nwords*BYTES_PER_WORD;
- int allocated = 0;
- while (allocated < n) {
- aexport = new int[nwords];
- /* System.err.println("Step"); */
- allocated += nbytes;
- }
- youngBytes = youngBytes + allocated;
- }
-
- // Allocate "n" bytes of young-gen data; and do the
- // corresponding amount of old-gen allocation and pointer
- // mutation.
-
- // oldGenAlloc may perform some mutations, so this code
- // takes those mutations into account.
-
- private static void doStep(long n) {
- long mutations = actuallyMut;
-
- doYoungGenAlloc(n, WORDS_DEAD);
- doMutWork(n);
- oldGenAlloc(n / promoteRate);
- oldGenMut(Math.max(0L, (mutations + ptrMutRate) - actuallyMut));
- }
-
- public static void main(String[] args) {
- if (args.length != 5) {
- System.err.println(msg1);
- System.err.println(msg2);
- System.err.println(msg3);
- System.err.println(msg4);
- System.err.println(msg5);
- System.err.println(msg6);
- return;
- }
-
- size = Integer.parseInt(args[0]);
- workUnits = Integer.parseInt(args[1]);
- promoteRate = Integer.parseInt(args[2]);
- ptrMutRate = Integer.parseInt(args[3]);
- steps = Integer.parseInt(args[4]);
-
- System.out.println(size + " megabytes of live storage");
- System.out.println(workUnits + " work units per step");
- System.out.println("promotion ratio is 1:" + promoteRate);
- System.out.println("pointer mutation rate is " + ptrMutRate);
- System.out.println(steps + " steps");
-
- init();
-// checkTrees();
- youngBytes = 0;
- nodes = 0;
-
- System.err.println("Initialization complete...");
-
- long start = System.currentTimeMillis();
-
- for (int step = 0; step < steps; step++) {
- doStep(MEG);
- }
-
- long end = System.currentTimeMillis();
- float secs = ((float)(end-start))/1000.0F;
-
-// checkTrees();
-
- NumberFormat nf = NumberFormat.getInstance();
- nf.setMaximumFractionDigits(1);
- System.out.println("\nTook " + nf.format(secs) + " sec in steady state.");
- nf.setMaximumFractionDigits(2);
- System.out.println("Allocated " + steps + " Mb of young gen garbage"
- + " (= " + nf.format(((float)steps)/secs) +
- " Mb/sec)");
- System.out.println(" (actually allocated " +
- nf.format(((float) youngBytes)/MEG) + " megabytes)");
- float promoted = ((float)steps) / (float)promoteRate;
- System.out.println("Promoted " + promoted +
- " Mb (= " + nf.format(promoted/secs) + " Mb/sec)");
- System.out.println(" (actually promoted " +
- nf.format(((float) (nodes * BYTES_PER_NODE))/MEG) +
- " megabytes)");
- if (ptrMutRate != 0) {
- System.out.println("Mutated " + actuallyMut +
- " pointers (= " +
- nf.format(actuallyMut/secs) + " ptrs/sec)");
-
- }
- // This output serves mainly to discourage optimization.
- System.out.println("Checksum = " + (mutatorSum + aexport.length));
-
- }
-}
--- a/hotspot/test/stress/gc/TestMultiThreadStressRSet.java Thu Apr 21 13:36:14 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,305 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/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.
- */
-
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import sun.hotspot.WhiteBox;
-
-/*
- * @test TestMultiThreadStressRSet.java
- * @key stress
- * @requires vm.gc=="G1" | vm.gc=="null"
- * @requires os.maxMemory > 2G
- *
- * @summary Stress G1 Remembered Set using multiple threads
- * @library /test/lib /testlibrary
- * @build sun.hotspot.WhiteBox
- * @run main ClassFileInstaller sun.hotspot.WhiteBox
- * sun.hotspot.WhiteBox$WhiteBoxPermission
- * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
- * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=1 -Xlog:gc
- * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestMultiThreadStressRSet 10 4
- *
- * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
- * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc
- * -Xmx1G -XX:G1HeapRegionSize=8m -XX:MaxGCPauseMillis=1000 TestMultiThreadStressRSet 60 16
- *
- * @run main/othervm/timeout=700 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
- * -XX:+UseG1GC -XX:G1SummarizeRSetStatsPeriod=100 -Xlog:gc
- * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestMultiThreadStressRSet 600 32
- */
-public class TestMultiThreadStressRSet {
-
- private static final Random RND = new Random(2015 * 2016);
- private static final WhiteBox WB = WhiteBox.getWhiteBox();
- private static final int REF_SIZE = WB.getHeapOopSize();
- private static final int REGION_SIZE = WB.g1RegionSize();
-
- // How many regions to use for the storage
- private static final int STORAGE_REGIONS = 20;
-
- // Size a single obj in the storage
- private static final int OBJ_SIZE = 1024;
-
- // How many regions of young/old gen to use in the BUFFER
- private static final int BUFFER_YOUNG_REGIONS = 60;
- private static final int BUFFER_OLD_REGIONS = 40;
-
- // Total number of objects in the storage.
- private final int N;
-
- // The storage of byte[]
- private final List<Object> STORAGE;
-
- // Where references to the Storage will be stored
- private final List<Object[]> BUFFER;
-
- // The length of a buffer element.
- // RSet deals with "cards" (areas of 512 bytes), not with single refs
- // So, to affect the RSet the BUFFER refs should be allocated in different
- // memory cards.
- private final int BUF_ARR_LEN = 100 * (512 / REF_SIZE);
-
- // Total number of objects in the young/old buffers
- private final int YOUNG;
- private final int OLD;
-
- // To cause Remembered Sets change their coarse level the test uses a window
- // within STORAGE. All the BUFFER elements refer to only STORAGE objects
- // from the current window. The window is defined by a range.
- // The first element has got the index: 'windowStart',
- // the last one: 'windowStart + windowSize - 1'
- // The window is shifting periodically.
- private int windowStart;
- private final int windowSize;
-
- // Counter of created worker threads
- private int counter = 0;
-
- private volatile String errorMessage = null;
- private volatile boolean isEnough = false;
-
- public static void main(String args[]) {
- if (args.length != 2) {
- throw new IllegalArgumentException("TEST BUG: wrong arg count " + args.length);
- }
- long time = Long.parseLong(args[0]);
- int threads = Integer.parseInt(args[1]);
- new TestMultiThreadStressRSet().test(time * 1000, threads);
- }
-
- /**
- * Initiates test parameters, fills out the STORAGE and BUFFER.
- */
- public TestMultiThreadStressRSet() {
-
- N = (REGION_SIZE - 1) * STORAGE_REGIONS / OBJ_SIZE + 1;
- STORAGE = new ArrayList<>(N);
- int bytes = OBJ_SIZE - 20;
- for (int i = 0; i < N - 1; i++) {
- STORAGE.add(new byte[bytes]);
- }
- STORAGE.add(new byte[REGION_SIZE / 2 + 100]); // humongous
- windowStart = 0;
- windowSize = REGION_SIZE / OBJ_SIZE;
-
- BUFFER = new ArrayList<>();
- int sizeOfBufferObject = 20 + REF_SIZE * BUF_ARR_LEN;
- OLD = REGION_SIZE * BUFFER_OLD_REGIONS / sizeOfBufferObject;
- YOUNG = REGION_SIZE * BUFFER_YOUNG_REGIONS / sizeOfBufferObject;
- for (int i = 0; i < OLD + YOUNG; i++) {
- BUFFER.add(new Object[BUF_ARR_LEN]);
- }
- }
-
- /**
- * Does the testing. Steps:
- * <ul>
- * <li> starts the Shifter thread
- * <li> during the given time starts new Worker threads, keeping the number
- * of live thread under limit.
- * <li> stops the Shifter thread
- * </ul>
- *
- * @param timeInMillis how long to stress
- * @param maxThreads the maximum number of Worker thread working together.
- */
- public void test(long timeInMillis, int maxThreads) {
- if (timeInMillis <= 0 || maxThreads <= 0) {
- throw new IllegalArgumentException("TEST BUG: be positive!");
- }
- System.out.println("%% Time to work: " + timeInMillis / 1000 + "s");
- System.out.println("%% Number of threads: " + maxThreads);
- long finish = System.currentTimeMillis() + timeInMillis;
- Shifter shift = new Shifter(this, 1000, (int) (windowSize * 0.9));
- shift.start();
- for (int i = 0; i < maxThreads; i++) {
- new Worker(this, 100).start();
- }
- try {
- while (System.currentTimeMillis() < finish && errorMessage == null) {
- Thread.sleep(100);
- }
- } catch (Throwable t) {
- printAllStackTraces(System.err);
- t.printStackTrace(System.err);
- this.errorMessage = t.getMessage();
- } finally {
- isEnough = true;
- }
- System.out.println("%% Total work cycles: " + counter);
- if (errorMessage != null) {
- throw new RuntimeException(errorMessage);
- }
- }
-
- /**
- * Returns an element from from the BUFFER (an object array) to keep
- * references to the storage.
- *
- * @return an Object[] from buffer.
- */
- private Object[] getFromBuffer() {
- int index = counter % (OLD + YOUNG);
- synchronized (BUFFER) {
- if (index < OLD) {
- if (counter % 100 == (counter / 100) % 100) {
- // need to generate garbage in the old gen to provoke mixed GC
- return replaceInBuffer(index);
- } else {
- return BUFFER.get(index);
- }
- } else {
- return replaceInBuffer(index);
- }
- }
- }
-
- private Object[] replaceInBuffer(int index) {
- Object[] objs = new Object[BUF_ARR_LEN];
- BUFFER.set(index, objs);
- return objs;
- }
-
- /**
- * Returns a random object from the current window within the storage.
- * A storage element with index from windowStart to windowStart+windowSize.
- *
- * @return a random element from the current window within the storage.
- */
- private Object getRandomObject() {
- int index = (windowStart + RND.nextInt(windowSize)) % N;
- return STORAGE.get(index);
- }
-
- private static void printAllStackTraces(PrintStream ps) {
- Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
- for (Thread t : traces.keySet()) {
- ps.println(t.toString() + " " + t.getState());
- for (StackTraceElement traceElement : traces.get(t)) {
- ps.println("\tat " + traceElement);
- }
- }
- }
-
- /**
- * Thread to create a number of references from BUFFER to STORAGE.
- */
- private static class Worker extends Thread {
-
- final TestMultiThreadStressRSet boss;
- final int refs; // number of refs to OldGen
-
- /**
- * @param boss the tests
- * @param refsToOldGen how many references to the OldGen to create
- */
- Worker(TestMultiThreadStressRSet boss, int refsToOldGen) {
- this.boss = boss;
- this.refs = refsToOldGen;
- }
-
- @Override
- public void run() {
- try {
- while (!boss.isEnough) {
- Object[] objs = boss.getFromBuffer();
- int step = objs.length / refs;
- for (int i = 0; i < refs; i += step) {
- objs[i] = boss.getRandomObject();
- }
- boss.counter++;
- }
- } catch (Throwable t) {
- t.printStackTrace(System.out);
- boss.errorMessage = t.getMessage();
- }
- }
- }
-
- /**
- * Periodically shifts the current STORAGE window, removing references
- * in BUFFER that refer to objects outside the window.
- */
- private static class Shifter extends Thread {
-
- final TestMultiThreadStressRSet boss;
- final int sleepTime;
- final int shift;
-
- Shifter(TestMultiThreadStressRSet boss, int sleepTime, int shift) {
- this.boss = boss;
- this.sleepTime = sleepTime;
- this.shift = shift;
- }
-
- @Override
- public void run() {
- try {
- while (!boss.isEnough) {
- Thread.sleep(sleepTime);
- boss.windowStart += shift;
- for (int i = 0; i < boss.OLD; i++) {
- Object[] objs = boss.BUFFER.get(i);
- for (int j = 0; j < objs.length; j++) {
- objs[j] = null;
- }
- }
- if (!WB.g1InConcurrentMark()) {
- System.out.println("%% start CMC");
- WB.g1StartConcMarkCycle();
- } else {
- System.out.println("%% CMC is already in progress");
- }
- }
- } catch (Throwable t) {
- t.printStackTrace(System.out);
- boss.errorMessage = t.getMessage();
- }
- }
- }
-}
-
--- a/hotspot/test/stress/gc/TestStressIHOPMultiThread.java Thu Apr 21 13:36:14 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,216 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/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 TestStressIHOPMultiThread
- * @bug 8148397
- * @key stress
- * @summary Stress test for IHOP
- * @requires vm.gc=="G1" | vm.gc=="null"
- * @run main/othervm/timeout=200 -Xmx128m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
- * -XX:+UseG1GC -XX:G1HeapRegionSize=1m -XX:+G1UseAdaptiveIHOP
- * -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread1.log
- * -Dtimeout=2 -DheapUsageMinBound=30 -DheapUsageMaxBound=80
- * -Dthreads=2 TestStressIHOPMultiThread
- * @run main/othervm/timeout=200 -Xmx256m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
- * -XX:+UseG1GC -XX:G1HeapRegionSize=2m -XX:+G1UseAdaptiveIHOP
- * -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread2.log
- * -Dtimeout=2 -DheapUsageMinBound=60 -DheapUsageMaxBound=90
- * -Dthreads=3 TestStressIHOPMultiThread
- * @run main/othervm/timeout=200 -Xmx256m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
- * -XX:+UseG1GC -XX:G1HeapRegionSize=4m -XX:-G1UseAdaptiveIHOP
- * -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread3.log
- * -Dtimeout=2 -DheapUsageMinBound=40 -DheapUsageMaxBound=90
- * -Dthreads=5 TestStressIHOPMultiThread
- * @run main/othervm/timeout=200 -Xmx128m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
- * -XX:+UseG1GC -XX:G1HeapRegionSize=8m -XX:+G1UseAdaptiveIHOP
- * -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread4.log
- * -Dtimeout=2 -DheapUsageMinBound=20 -DheapUsageMaxBound=90
- * -Dthreads=10 TestStressIHOPMultiThread
- * @run main/othervm/timeout=200 -Xmx512m -XX:G1HeapWastePercent=0 -XX:G1MixedGCCountTarget=1
- * -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:+G1UseAdaptiveIHOP
- * -Xlog:gc+ihop=debug,gc+ihop+ergo=debug,gc+ergo=debug:TestStressIHOPMultiThread5.log
- * -Dtimeout=2 -DheapUsageMinBound=20 -DheapUsageMaxBound=90
- * -Dthreads=17 TestStressIHOPMultiThread
- */
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Stress test for Adaptive IHOP. Starts a number of threads that fill and free
- * specified amount of memory. Tests work with enabled IHOP logging.
- *
- */
-public class TestStressIHOPMultiThread {
-
- public final static List<Object> GARBAGE = new LinkedList<>();
-
- private final long HEAP_SIZE;
- // Amount of memory to be allocated before iterations start
- private final long HEAP_PREALLOC_SIZE;
- // Amount of memory to be allocated and freed during iterations
- private final long HEAP_ALLOC_SIZE;
- private final int CHUNK_SIZE = 100000;
-
- private final int TIMEOUT;
- private final int THREADS;
- private final int HEAP_LOW_BOUND;
- private final int HEAP_HIGH_BOUND;
-
- private volatile boolean running = true;
- private final List<AllocationThread> threads;
-
- public static void main(String[] args) throws InterruptedException {
- new TestStressIHOPMultiThread().start();
-
- }
-
- TestStressIHOPMultiThread() {
-
- TIMEOUT = Integer.getInteger("timeout") * 60;
- THREADS = Integer.getInteger("threads");
- HEAP_LOW_BOUND = Integer.getInteger("heapUsageMinBound");
- HEAP_HIGH_BOUND = Integer.getInteger("heapUsageMaxBound");
- HEAP_SIZE = Runtime.getRuntime().maxMemory();
-
- HEAP_PREALLOC_SIZE = HEAP_SIZE * HEAP_LOW_BOUND / 100;
- HEAP_ALLOC_SIZE = HEAP_SIZE * (HEAP_HIGH_BOUND - HEAP_LOW_BOUND) / 100;
-
- threads = new ArrayList<>(THREADS);
- }
-
- public void start() throws InterruptedException {
- fill();
- createThreads();
- waitForStress();
- stressDone();
- waitForFinish();
- }
-
- /**
- * Fills HEAP_PREALLOC_SIZE bytes of garbage.
- */
- private void fill() {
- long allocated = 0;
- while (allocated < HEAP_PREALLOC_SIZE) {
- GARBAGE.add(new byte[CHUNK_SIZE]);
- allocated += CHUNK_SIZE;
- }
- }
-
- /**
- * Creates a number of threads which will fill and free amount of memory.
- */
- private void createThreads() {
- for (int i = 0; i < THREADS; ++i) {
- System.out.println("Create thread " + i);
- AllocationThread thread =new TestStressIHOPMultiThread.AllocationThread(i, HEAP_ALLOC_SIZE / THREADS);
- // Put reference to thread garbage into common garbage for avoiding possible optimization.
- GARBAGE.add(thread.getList());
- threads.add(thread);
- }
- threads.forEach(t -> t.start());
- }
-
- /**
- * Wait each thread for finishing
- */
- private void waitForFinish() {
- threads.forEach(thread -> {
- thread.silentJoin();
- });
- }
-
- private boolean isRunning() {
- return running;
- }
-
- private void stressDone() {
- running = false;
- }
-
- private void waitForStress() throws InterruptedException {
- Thread.sleep(TIMEOUT * 1000);
- }
-
- private class AllocationThread extends Thread {
-
- private final List<Object> garbage;
-
- private final long amountOfGarbage;
- private final int threadId;
-
- public AllocationThread(int id, long amount) {
- super("Thread " + id);
- threadId = id;
- amountOfGarbage = amount;
- garbage = new LinkedList<>();
- }
-
- /**
- * Returns list of garbage.
- * @return List with thread garbage.
- */
- public List<Object> getList(){
- return garbage;
- }
-
- @Override
- public void run() {
- System.out.println("Start the thread " + threadId);
- while (TestStressIHOPMultiThread.this.isRunning()) {
- allocate(amountOfGarbage);
- free();
- }
- }
-
- private void silentJoin() {
- System.out.println("Join the thread " + threadId);
- try {
- join();
- } catch (InterruptedException ie) {
- throw new RuntimeException(ie);
- }
- }
-
- /**
- * Allocates thread local garbage
- */
- private void allocate(long amount) {
- long allocated = 0;
- while (allocated < amount && TestStressIHOPMultiThread.this.isRunning()) {
- garbage.add(new byte[CHUNK_SIZE]);
- allocated += CHUNK_SIZE;
- }
- }
-
- /**
- * Frees thread local garbage
- */
- private void free() {
- garbage.clear();
- }
- }
-}
--- a/hotspot/test/stress/gc/TestStressRSetCoarsening.java Thu Apr 21 13:36:14 2016 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,335 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/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.
- */
-
-import java.util.concurrent.TimeoutException;
-import sun.hotspot.WhiteBox;
-
-/*
- * @test TestStressRSetCoarsening.java
- * @key stress
- * @bug 8146984 8147087
- * @requires vm.gc=="G1" | vm.gc=="null"
- * @requires os.maxMemory > 3G
- *
- * @summary Stress G1 Remembered Set by creating a lot of cross region links
- * @modules java.base/jdk.internal.misc
- * @library /testlibrary /test/lib
- * @build sun.hotspot.WhiteBox
- * @run main ClassFileInstaller sun.hotspot.WhiteBox
- * sun.hotspot.WhiteBox$WhiteBoxPermission
- * @run main/othervm/timeout=300
- * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
- * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
- * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 1 0 300
- * @run main/othervm/timeout=300
- * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
- * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
- * -Xmx500m -XX:G1HeapRegionSize=8m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 1 10 300
- * @run main/othervm/timeout=300
- * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
- * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
- * -Xmx500m -XX:G1HeapRegionSize=32m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 42 10 300
- * @run main/othervm/timeout=300
- * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
- * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
- * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 2 0 300
- * @run main/othervm/timeout=1800
- * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
- * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
- * -Xmx1G -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 500 0 1800
- * @run main/othervm/timeout=1800
- * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
- * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
- * -Xmx1G -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 10 10 1800
- */
-
-/**
- * What the test does.
- * Preparation stage:
- * Fill out ~90% of the heap with objects, each object is an object array.
- * If we want to allocate K objects per region, we calculate N to meet:
- * sizeOf(Object[N]) ~= regionSize / K
- * Stress stage:
- * No more allocation, so no more GC.
- * We will perform a number of iterations. On each iteration i,
- * for each pair of regions Rx and Ry we will set c[i] references
- * from Rx to Ry. If c[i] less than c[i-1] at the end of iteration
- * concurrent mark cycle will be initiated (to recalculate remembered sets).
- * As the result RSet will be growing up and down, up and down many times.
- *
- * The test expects: no crash and no timeouts.
- *
- * Test Parameters:
- * args[0] - number of objects per Heap Region (1 - means humongous)
- * args[1] - number of regions to refresh to provoke GC at the end of cycle.
- * (0 - means no GC, i.e. no reading from RSet)
- * args[2] - timeout in seconds (to stop execution to avoid jtreg timeout)
- */
-public class TestStressRSetCoarsening {
-
- public static void main(String... args) throws InterruptedException {
- if (args.length != 3) {
- throw new IllegalArgumentException("Wrong number of arguments " + args.length);
- }
- int objectsPerRegion = Integer.parseInt(args[0]); // 1 means humongous
- int regsToRefresh = Integer.parseInt(args[1]); // 0 means no regions to refresh at the end of cycle
- int timeout = Integer.parseInt(args[2]); // in seconds, test should stop working eariler
- new TestStressRSetCoarsening(objectsPerRegion, regsToRefresh, timeout).go();
- }
-
- private static final long KB = 1024;
- private static final long MB = 1024 * KB;
-
- private static final WhiteBox WB = WhiteBox.getWhiteBox();
-
- public final Object[][] storage;
-
- /**
- * Number of objects per region. This is a test parameter.
- */
- public final int K;
-
- /**
- * Length of object array: sizeOf(Object[N]) ~= regionSize / K
- * N will be calculated as function of K.
- */
- public final int N;
-
- /**
- * How many regions involved into testing.
- * Will be calculated as heapFractionToAllocate * freeRegionCount.
- */
- public final int regionCount;
-
- /**
- * How much heap to use.
- */
- public final float heapFractionToAllocate = 0.9f;
-
- /**
- * How many regions to be refreshed at the end of cycle.
- * This is a test parameter.
- */
- public final int regsToRefresh;
-
- /**
- * Initial time.
- */
- public final long start;
-
- /**
- * Time when the test should stop working.
- */
- public final long finishAt;
-
- /**
- * Does pre-calculation and allocate necessary objects.
- *
- * @param objPerRegions how many objects per G1 heap region
- */
- TestStressRSetCoarsening(int objPerRegions, int regsToRefresh, int timeout) {
- this.K = objPerRegions;
- this.regsToRefresh = regsToRefresh;
- this.start = System.currentTimeMillis();
- this.finishAt = start + timeout * 900; // 10% ahead of jtreg timeout
-
- long regionSize = WB.g1RegionSize();
-
- // How many free regions
- Runtime rt = Runtime.getRuntime();
- long used = rt.totalMemory() - rt.freeMemory();
- long totalFree = rt.maxMemory() - used;
- regionCount = (int) ((totalFree / regionSize) * heapFractionToAllocate);
- long toAllocate = regionCount * regionSize;
- System.out.println("%% Test parameters");
- System.out.println("%% Objects per region : " + K);
- System.out.println("%% Heap fraction to allocate : " + (int) (heapFractionToAllocate * 100) + "%");
- System.out.println("%% Regions to refresh to provoke GC: " + regsToRefresh);
-
- System.out.println("%% Memory");
- System.out.println("%% used : " + used / MB + "M");
- System.out.println("%% available : " + totalFree / MB + "M");
- System.out.println("%% to allocate : " + toAllocate / MB + "M");
- System.out.println("%% (in regs) : " + regionCount);
- System.out.println("%% G1 Region Size: " + regionSize / MB + "M");
-
- int refSize = WB.getHeapOopSize();
-
- // Calculate N: K*sizeOf(Object[N]) ~= regionSize
- // sizeOf(Object[N]) ~= (N+4)*refSize
- // ==>
- // N = regionSize / K / refSize - 4;
- N = (int) ((regionSize / K) / refSize) - 5;
-
- /*
- * --------------
- * region0 storage[0] = new Object[N]
- * ...
- * storage[K-1] = new Object[N]
- * ---------------
- * region1 storage[K] = new Object[N]
- * ...
- * storage[2*K - 1] = new Object[N]
- * --------------
- * ...
- * --------------
- * regionX storage[X*K] = new Object[N]
- * ...
- * storage[(X+1)*K -1] = new Object[N]
- * where X = HeapFraction * TotalRegions
- * -------------
- */
- System.out.println("%% Objects");
- System.out.println("%% N (array length) : " + N);
- System.out.println("%% K (objects in regions): " + K);
- System.out.println("%% Reference size : " + refSize);
- System.out.println("%% Approximate obj size : " + (N + 2) * refSize / KB + "K)");
-
- storage = new Object[regionCount * K][];
- for (int i = 0; i < storage.length; i++) {
- storage[i] = new Object[N];
- }
- }
-
- public void go() throws InterruptedException {
- // threshold for sparce -> fine
- final int FINE = WB.getIntxVMFlag("G1RSetSparseRegionEntries").intValue();
-
- // threshold for fine -> coarse
- final int COARSE = WB.getIntxVMFlag("G1RSetRegionEntries").intValue();
-
- // regToRegRefCounts - array of reference counts from region to region
- // at the the end of iteration.
- // The number of test iterations is array length - 1.
- // If c[i] > c[i-1] then during the iteration i more references will
- // be created.
- // If c[i] < c[i-1] then some referenes will be cleaned.
- int[] regToRegRefCounts = {0, FINE / 2, 0, FINE, (FINE + COARSE) / 2, 0,
- COARSE, COARSE + 10, FINE + 1, FINE / 2, 0};
-
- // For progress tracking
- int[] progress = new int[regToRegRefCounts.length];
- progress[0] = 0;
- for (int i = 1; i < regToRegRefCounts.length; i++) {
- progress[i] = progress[i - 1] + Math.abs(regToRegRefCounts[i] - regToRegRefCounts[i - 1]);
- }
- try {
- for (int i = 1; i < regToRegRefCounts.length; i++) {
- int pre = regToRegRefCounts[i - 1];
- int cur = regToRegRefCounts[i];
- float prog = ((float) progress[i - 1] / progress[progress.length - 1]);
-
- System.out.println("%% step " + i
- + " out of " + (regToRegRefCounts.length - 1)
- + " (~" + (int) (100 * prog) + "% done)");
- System.out.println("%% " + pre + " --> " + cur);
- for (int to = 0; to < regionCount; to++) {
- // Select a celebrity object that we will install references to.
- // The celebrity will be referred from all other regions.
- // If the number of references after should be less than they
- // were before, select NULL.
- Object celebrity = cur > pre ? storage[to * K] : null;
- for (int from = 0; from < regionCount; from++) {
- if (to == from) {
- continue; // no need to refer to itself
- }
-
- int step = cur > pre ? +1 : -1;
- for (int rn = pre; rn != cur; rn += step) {
- storage[getY(to, from, rn)][getX(to, from, rn)] = celebrity;
- if (System.currentTimeMillis() > finishAt) {
- throw new TimeoutException();
- }
- }
- }
- }
- if (pre > cur) {
- // Number of references went down.
- // Need to provoke recalculation of RSet.
- WB.g1StartConcMarkCycle();
- while (WB.g1InConcurrentMark()) {
- Thread.sleep(1);
- }
- }
-
- // To force the use of rememebered set entries we need to provoke a GC.
- // To induce some fragmentation, and some mixed GCs, we need
- // to make a few objects unreachable.
- for (int toClean = i * regsToRefresh; toClean < (i + 1) * regsToRefresh; toClean++) {
- int to = toClean % regionCount;
- // Need to remove all references from all regions to the region 'to'
- for (int from = 0; from < regionCount; from++) {
- if (to == from) {
- continue; // no need to refer to itself
- }
- for (int rn = 0; rn <= cur; rn++) {
- storage[getY(to, from, rn)][getX(to, from, rn)] = null;
- }
- }
- // 'Refresh' storage elements for the region 'to'
- // After that loop all 'old' objects in the region 'to'
- // should become unreachable.
- for (int k = 0; k < K; k++) {
- storage[(to * K + k) % storage.length] = new Object[N];
- }
- }
- }
- } catch (TimeoutException e) {
- System.out.println("%% TIMEOUT!!!");
- }
- long now = System.currentTimeMillis();
- System.out.println("%% Summary");
- System.out.println("%% Time spent : " + ((now - start) / 1000) + " seconds");
- System.out.println("%% Free memory left : " + Runtime.getRuntime().freeMemory() / KB + "K");
- System.out.println("%% Test passed");
- }
-
- /**
- * Returns X index in the Storage of the reference #rn from the region
- * 'from' to the region 'to'.
- *
- * @param to region # to refer to
- * @param from region # to refer from
- * @param rn number of reference
- *
- * @return X index in the range: [0 ... N-1]
- */
- private int getX(int to, int from, int rn) {
- return (rn * regionCount + to) % N;
- }
-
- /**
- * Returns Y index in the Storage of the reference #rn from the region
- * 'from' to the region 'to'.
- *
- * @param to region # to refer to
- * @param from region # to refer from
- * @param rn number of reference
- *
- * @return Y index in the range: [0 ... K*regionCount -1]
- */
- private int getY(int to, int from, int rn) {
- return ((rn * regionCount + to) / N + from * K) % (regionCount * K);
- }
-}
-