This commit is contained in:
Prasanta Sadhukhan 2019-12-03 12:45:47 +05:30
commit 999f05b13b
53 changed files with 847 additions and 815 deletions

View File

@ -1,3 +1,28 @@
#
# Copyright (c) 2015, 2019, 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. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# 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.
#
toplevel.decl=\
package {0};\n\
\n\

View File

@ -1565,14 +1565,18 @@ void nmethod::flush_dependencies(bool delete_immediately) {
// Transfer information from compilation to jvmti
void nmethod::post_compiled_method_load_event() {
Method* moop = method();
// This is a bad time for a safepoint. We don't want
// this nmethod to get unloaded while we're queueing the event.
NoSafepointVerifier nsv;
Method* m = method();
HOTSPOT_COMPILED_METHOD_LOAD(
(char *) moop->klass_name()->bytes(),
moop->klass_name()->utf8_length(),
(char *) moop->name()->bytes(),
moop->name()->utf8_length(),
(char *) moop->signature()->bytes(),
moop->signature()->utf8_length(),
(char *) m->klass_name()->bytes(),
m->klass_name()->utf8_length(),
(char *) m->name()->bytes(),
m->name()->utf8_length(),
(char *) m->signature()->bytes(),
m->signature()->utf8_length(),
insts_begin(), insts_size());
if (JvmtiExport::should_post_compiled_method_load() ||

View File

@ -125,16 +125,16 @@ bool G1Analytics::enough_samples_available(TruncatedSeq const* seq) const {
return seq->num() >= 3;
}
double G1Analytics::get_new_unit_prediction(TruncatedSeq const* seq) const {
return _predictor->get_new_unit_prediction(seq);
double G1Analytics::predict_in_unit_interval(TruncatedSeq const* seq) const {
return _predictor->predict_in_unit_interval(seq);
}
size_t G1Analytics::get_new_size_prediction(TruncatedSeq const* seq) const {
return (size_t)get_new_lower_zero_bound_prediction(seq);
size_t G1Analytics::predict_size(TruncatedSeq const* seq) const {
return (size_t)predict_zero_bounded(seq);
}
double G1Analytics::get_new_lower_zero_bound_prediction(TruncatedSeq const* seq) const {
return _predictor->get_new_lower_zero_bound_prediction(seq);
double G1Analytics::predict_zero_bounded(TruncatedSeq const* seq) const {
return _predictor->predict_zero_bounded(seq);
}
int G1Analytics::num_alloc_rate_ms() const {
@ -229,50 +229,50 @@ void G1Analytics::report_rs_length(double rs_length) {
}
double G1Analytics::predict_alloc_rate_ms() const {
return get_new_lower_zero_bound_prediction(_alloc_rate_ms_seq);
return predict_zero_bounded(_alloc_rate_ms_seq);
}
double G1Analytics::predict_concurrent_refine_rate_ms() const {
return get_new_lower_zero_bound_prediction(_concurrent_refine_rate_ms_seq);
return predict_zero_bounded(_concurrent_refine_rate_ms_seq);
}
double G1Analytics::predict_logged_cards_rate_ms() const {
return get_new_lower_zero_bound_prediction(_logged_cards_rate_ms_seq);
return predict_zero_bounded(_logged_cards_rate_ms_seq);
}
double G1Analytics::predict_young_card_merge_to_scan_ratio() const {
return get_new_unit_prediction(_young_card_merge_to_scan_ratio_seq);
return predict_in_unit_interval(_young_card_merge_to_scan_ratio_seq);
}
size_t G1Analytics::predict_scan_card_num(size_t rs_length, bool for_young_gc) const {
if (for_young_gc || !enough_samples_available(_mixed_card_merge_to_scan_ratio_seq)) {
return (size_t)(rs_length * predict_young_card_merge_to_scan_ratio());
} else {
return (size_t)(rs_length * get_new_unit_prediction(_mixed_card_merge_to_scan_ratio_seq));
return (size_t)(rs_length * predict_in_unit_interval(_mixed_card_merge_to_scan_ratio_seq));
}
}
double G1Analytics::predict_card_merge_time_ms(size_t card_num, bool for_young_gc) const {
if (for_young_gc || !enough_samples_available(_mixed_cost_per_card_merge_ms_seq)) {
return card_num * get_new_lower_zero_bound_prediction(_young_cost_per_card_merge_ms_seq);
return card_num * predict_zero_bounded(_young_cost_per_card_merge_ms_seq);
} else {
return card_num * get_new_lower_zero_bound_prediction(_mixed_cost_per_card_merge_ms_seq);
return card_num * predict_zero_bounded(_mixed_cost_per_card_merge_ms_seq);
}
}
double G1Analytics::predict_card_scan_time_ms(size_t card_num, bool for_young_gc) const {
if (for_young_gc || !enough_samples_available(_mixed_cost_per_card_scan_ms_seq)) {
return card_num * get_new_lower_zero_bound_prediction(_young_cost_per_card_scan_ms_seq);
return card_num * predict_zero_bounded(_young_cost_per_card_scan_ms_seq);
} else {
return card_num * get_new_lower_zero_bound_prediction(_mixed_cost_per_card_scan_ms_seq);
return card_num * predict_zero_bounded(_mixed_cost_per_card_scan_ms_seq);
}
}
double G1Analytics::predict_object_copy_time_ms_during_cm(size_t bytes_to_copy) const {
if (!enough_samples_available(_cost_per_byte_ms_during_cm_seq)) {
return (1.1 * bytes_to_copy) * get_new_lower_zero_bound_prediction(_copy_cost_per_byte_ms_seq);
return (1.1 * bytes_to_copy) * predict_zero_bounded(_copy_cost_per_byte_ms_seq);
} else {
return bytes_to_copy * get_new_lower_zero_bound_prediction(_cost_per_byte_ms_during_cm_seq);
return bytes_to_copy * predict_zero_bounded(_cost_per_byte_ms_during_cm_seq);
}
}
@ -280,36 +280,36 @@ double G1Analytics::predict_object_copy_time_ms(size_t bytes_to_copy, bool durin
if (during_concurrent_mark) {
return predict_object_copy_time_ms_during_cm(bytes_to_copy);
} else {
return bytes_to_copy * get_new_lower_zero_bound_prediction(_copy_cost_per_byte_ms_seq);
return bytes_to_copy * predict_zero_bounded(_copy_cost_per_byte_ms_seq);
}
}
double G1Analytics::predict_constant_other_time_ms() const {
return get_new_lower_zero_bound_prediction(_constant_other_time_ms_seq);
return predict_zero_bounded(_constant_other_time_ms_seq);
}
double G1Analytics::predict_young_other_time_ms(size_t young_num) const {
return young_num * get_new_lower_zero_bound_prediction(_young_other_cost_per_region_ms_seq);
return young_num * predict_zero_bounded(_young_other_cost_per_region_ms_seq);
}
double G1Analytics::predict_non_young_other_time_ms(size_t non_young_num) const {
return non_young_num * get_new_lower_zero_bound_prediction(_non_young_other_cost_per_region_ms_seq);
return non_young_num * predict_zero_bounded(_non_young_other_cost_per_region_ms_seq);
}
double G1Analytics::predict_remark_time_ms() const {
return get_new_lower_zero_bound_prediction(_concurrent_mark_remark_times_ms);
return predict_zero_bounded(_concurrent_mark_remark_times_ms);
}
double G1Analytics::predict_cleanup_time_ms() const {
return get_new_lower_zero_bound_prediction(_concurrent_mark_cleanup_times_ms);
return predict_zero_bounded(_concurrent_mark_cleanup_times_ms);
}
size_t G1Analytics::predict_rs_length() const {
return get_new_size_prediction(_rs_length_seq) + get_new_size_prediction(_rs_length_diff_seq);
return predict_size(_rs_length_seq) + predict_size(_rs_length_diff_seq);
}
size_t G1Analytics::predict_pending_cards() const {
return get_new_size_prediction(_pending_cards_seq);
return predict_size(_pending_cards_seq);
}
double G1Analytics::last_known_gc_end_time_sec() const {

View File

@ -84,9 +84,9 @@ class G1Analytics: public CHeapObj<mtGC> {
// The constant used is random but "small".
bool enough_samples_available(TruncatedSeq const* seq) const;
double get_new_unit_prediction(TruncatedSeq const* seq) const;
size_t get_new_size_prediction(TruncatedSeq const* seq) const;
double get_new_lower_zero_bound_prediction(TruncatedSeq const* seq) const;
double predict_in_unit_interval(TruncatedSeq const* seq) const;
size_t predict_size(TruncatedSeq const* seq) const;
double predict_zero_bounded(TruncatedSeq const* seq) const;
public:
G1Analytics(const G1Predictions* predictor);

View File

@ -4266,7 +4266,7 @@ private:
HeapRegion* r = g1h->region_at(region_idx);
assert(!g1h->is_on_master_free_list(r), "sanity");
Atomic::add(&_rs_length, r->rem_set()->occupied_locked());
Atomic::add(&_rs_length, r->rem_set()->occupied());
if (!is_young) {
g1h->hot_card_cache()->reset_card_counts(r);

View File

@ -39,7 +39,7 @@
#include "utilities/globalDefinitions.hpp"
#include "utilities/quickSort.hpp"
G1CollectorState* G1CollectionSet::collector_state() {
G1CollectorState* G1CollectionSet::collector_state() const {
return _g1h->collector_state();
}
@ -47,8 +47,8 @@ G1GCPhaseTimes* G1CollectionSet::phase_times() {
return _policy->phase_times();
}
double G1CollectionSet::predict_region_elapsed_time_ms(HeapRegion* hr) {
return _policy->predict_region_elapsed_time_ms(hr, collector_state()->in_young_only_phase());
double G1CollectionSet::predict_region_non_copy_time_ms(HeapRegion* hr) const {
return _policy->predict_region_non_copy_time_ms(hr, collector_state()->in_young_only_phase());
}
G1CollectionSet::G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy) :
@ -66,15 +66,17 @@ G1CollectionSet::G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy) :
_recorded_rs_length(0),
_inc_build_state(Inactive),
_inc_part_start(0),
_inc_collection_set_stats(NULL),
_inc_bytes_used_before(0),
_inc_recorded_rs_length(0),
_inc_recorded_rs_length_diff(0),
_inc_predicted_elapsed_time_ms(0.0),
_inc_predicted_elapsed_time_ms_diff(0.0) {
_inc_predicted_non_copy_time_ms(0.0),
_inc_predicted_non_copy_time_ms_diff(0.0) {
}
G1CollectionSet::~G1CollectionSet() {
FREE_C_HEAP_ARRAY(uint, _collection_set_regions);
FREE_C_HEAP_ARRAY(IncCollectionSetRegionStat, _inc_collection_set_stats);
free_optional_regions();
clear_candidates();
}
@ -97,6 +99,7 @@ void G1CollectionSet::initialize(uint max_region_length) {
guarantee(_collection_set_regions == NULL, "Must only initialize once.");
_collection_set_max_length = max_region_length;
_collection_set_regions = NEW_C_HEAP_ARRAY(uint, max_region_length, mtGC);
_inc_collection_set_stats = NEW_C_HEAP_ARRAY(IncCollectionSetRegionStat, max_region_length, mtGC);
}
void G1CollectionSet::free_optional_regions() {
@ -145,13 +148,18 @@ void G1CollectionSet::add_optional_region(HeapRegion* hr) {
void G1CollectionSet::start_incremental_building() {
assert(_collection_set_cur_length == 0, "Collection set must be empty before starting a new collection set.");
assert(_inc_build_state == Inactive, "Precondition");
#ifdef ASSERT
for (size_t i = 0; i < _collection_set_max_length; i++) {
_inc_collection_set_stats[i].reset();
}
#endif
_inc_bytes_used_before = 0;
_inc_recorded_rs_length = 0;
_inc_recorded_rs_length_diff = 0;
_inc_predicted_elapsed_time_ms = 0.0;
_inc_predicted_elapsed_time_ms_diff = 0.0;
_inc_predicted_non_copy_time_ms = 0.0;
_inc_predicted_non_copy_time_ms_diff = 0.0;
update_incremental_marker();
}
@ -161,31 +169,17 @@ void G1CollectionSet::finalize_incremental_building() {
assert(SafepointSynchronize::is_at_safepoint(), "should be at a safepoint");
// The two "main" fields, _inc_recorded_rs_length and
// _inc_predicted_elapsed_time_ms, are updated by the thread
// _inc_predicted_non_copy_time_ms, are updated by the thread
// that adds a new region to the CSet. Further updates by the
// concurrent refinement thread that samples the young RSet lengths
// are accumulated in the *_diff fields. Here we add the diffs to
// the "main" fields.
if (_inc_recorded_rs_length_diff >= 0) {
_inc_recorded_rs_length += _inc_recorded_rs_length_diff;
} else {
// This is defensive. The diff should in theory be always positive
// as RSets can only grow between GCs. However, given that we
// sample their size concurrently with other threads updating them
// it's possible that we might get the wrong size back, which
// could make the calculations somewhat inaccurate.
size_t diffs = (size_t) (-_inc_recorded_rs_length_diff);
if (_inc_recorded_rs_length >= diffs) {
_inc_recorded_rs_length -= diffs;
} else {
_inc_recorded_rs_length = 0;
}
}
_inc_predicted_elapsed_time_ms += _inc_predicted_elapsed_time_ms_diff;
_inc_recorded_rs_length += _inc_recorded_rs_length_diff;
_inc_predicted_non_copy_time_ms += _inc_predicted_non_copy_time_ms_diff;
_inc_recorded_rs_length_diff = 0;
_inc_predicted_elapsed_time_ms_diff = 0.0;
_inc_predicted_non_copy_time_ms_diff = 0.0;
}
void G1CollectionSet::clear() {
@ -252,26 +246,23 @@ void G1CollectionSet::update_young_region_prediction(HeapRegion* hr,
assert(hr->is_young(), "Precondition");
assert(!SafepointSynchronize::is_at_safepoint(), "should not be at a safepoint");
// We could have updated _inc_recorded_rs_length and
// _inc_predicted_elapsed_time_ms directly but we'd need to do
// that atomically, as this code is executed by a concurrent
// refinement thread, potentially concurrently with a mutator thread
// allocating a new region and also updating the same fields. To
// avoid the atomic operations we accumulate these updates on two
// separate fields (*_diff) and we'll just add them to the "main"
// fields at the start of a GC.
IncCollectionSetRegionStat* stat = &_inc_collection_set_stats[hr->hrm_index()];
ssize_t old_rs_length = (ssize_t) hr->recorded_rs_length();
ssize_t rs_length_diff = (ssize_t) new_rs_length - old_rs_length;
size_t old_rs_length = stat->_rs_length;
assert(old_rs_length <= new_rs_length,
"Remembered set decreased (changed from " SIZE_FORMAT " to " SIZE_FORMAT " region %u type %s)",
old_rs_length, new_rs_length, hr->hrm_index(), hr->get_short_type_str());
size_t rs_length_diff = new_rs_length - old_rs_length;
stat->_rs_length = new_rs_length;
_inc_recorded_rs_length_diff += rs_length_diff;
double old_elapsed_time_ms = hr->predicted_elapsed_time_ms();
double new_region_elapsed_time_ms = predict_region_elapsed_time_ms(hr);
double elapsed_ms_diff = new_region_elapsed_time_ms - old_elapsed_time_ms;
_inc_predicted_elapsed_time_ms_diff += elapsed_ms_diff;
double old_non_copy_time = stat->_non_copy_time_ms;
assert(old_non_copy_time >= 0.0, "Non copy time for region %u not initialized yet, is %.3f", hr->hrm_index(), old_non_copy_time);
double new_non_copy_time = predict_region_non_copy_time_ms(hr);
double non_copy_time_ms_diff = new_non_copy_time - old_non_copy_time;
hr->set_recorded_rs_length(new_rs_length);
hr->set_predicted_elapsed_time_ms(new_region_elapsed_time_ms);
stat->_non_copy_time_ms = new_non_copy_time;
_inc_predicted_non_copy_time_ms_diff += non_copy_time_ms_diff;
}
void G1CollectionSet::add_young_region_common(HeapRegion* hr) {
@ -296,30 +287,31 @@ void G1CollectionSet::add_young_region_common(HeapRegion* hr) {
if (!_g1h->collector_state()->in_full_gc()) {
size_t rs_length = hr->rem_set()->occupied();
double region_elapsed_time_ms = predict_region_elapsed_time_ms(hr);
double non_copy_time = predict_region_non_copy_time_ms(hr);
// Cache the values we have added to the aggregated information
// in the heap region in case we have to remove this region from
// the incremental collection set, or it is updated by the
// rset sampling code
hr->set_recorded_rs_length(rs_length);
hr->set_predicted_elapsed_time_ms(region_elapsed_time_ms);
IncCollectionSetRegionStat* stat = &_inc_collection_set_stats[hr->hrm_index()];
stat->_rs_length = rs_length;
stat->_non_copy_time_ms = non_copy_time;
_inc_recorded_rs_length += rs_length;
_inc_predicted_elapsed_time_ms += region_elapsed_time_ms;
_inc_predicted_non_copy_time_ms += non_copy_time;
_inc_bytes_used_before += hr->used();
}
assert(!hr->in_collection_set(), "invariant");
_g1h->register_young_region_with_region_attr(hr);
size_t collection_set_length = _collection_set_cur_length;
// We use UINT_MAX as "invalid" marker in verification.
assert(collection_set_length < (UINT_MAX - 1),
"Collection set is too large with " SIZE_FORMAT " entries", collection_set_length);
hr->set_young_index_in_cset((uint)collection_set_length + 1);
assert(_collection_set_cur_length < (UINT_MAX - 1),
"Collection set is too large with " SIZE_FORMAT " entries", _collection_set_cur_length);
hr->set_young_index_in_cset((uint)_collection_set_cur_length + 1);
_collection_set_regions[collection_set_length] = hr->hrm_index();
_collection_set_regions[_collection_set_cur_length] = hr->hrm_index();
// Concurrent readers must observe the store of the value in the array before an
// update to the length field.
OrderAccess::storestore();
@ -341,21 +333,19 @@ void G1CollectionSet::add_eden_region(HeapRegion* hr) {
class G1VerifyYoungAgesClosure : public HeapRegionClosure {
public:
bool _valid;
public:
G1VerifyYoungAgesClosure() : HeapRegionClosure(), _valid(true) { }
virtual bool do_heap_region(HeapRegion* r) {
guarantee(r->is_young(), "Region must be young but is %s", r->get_type_str());
SurvRateGroup* group = r->surv_rate_group();
if (group == NULL) {
log_error(gc, verify)("## encountered NULL surv_rate_group in young region");
if (!r->has_surv_rate_group()) {
log_error(gc, verify)("## encountered young region without surv_rate_group");
_valid = false;
}
if (r->age_in_surv_rate_group() < 0) {
log_error(gc, verify)("## encountered negative age in young region");
if (!r->has_valid_age_in_surv_rate()) {
log_error(gc, verify)("## encountered invalid age in young region");
_valid = false;
}
@ -390,7 +380,7 @@ public:
HR_FORMAT_PARAMS(r),
p2i(r->prev_top_at_mark_start()),
p2i(r->next_top_at_mark_start()),
r->age_in_surv_rate_group_cond());
r->has_surv_rate_group() ? r->age_in_surv_rate_group() : -1);
return false;
}
};
@ -404,7 +394,7 @@ void G1CollectionSet::print(outputStream* st) {
#endif // !PRODUCT
double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1SurvivorRegions* survivors) {
double young_start_time_sec = os::elapsedTime();
Ticks start_time = Ticks::now();
finalize_incremental_building();
@ -412,18 +402,16 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi
"target_pause_time_ms = %1.6lf should be positive", target_pause_time_ms);
size_t pending_cards = _policy->pending_cards_at_gc_start() + _g1h->hot_card_cache()->num_entries();
double base_time_ms = _policy->predict_base_elapsed_time_ms(pending_cards);
double time_remaining_ms = MAX2(target_pause_time_ms - base_time_ms, 0.0);
log_trace(gc, ergo, cset)("Start choosing CSet. pending cards: " SIZE_FORMAT " predicted base time: %1.2fms remaining time: %1.2fms target pause time: %1.2fms",
pending_cards, base_time_ms, time_remaining_ms, target_pause_time_ms);
log_trace(gc, ergo, cset)("Start choosing CSet. Pending cards: " SIZE_FORMAT " target pause time: %1.2fms",
pending_cards, target_pause_time_ms);
// The young list is laid with the survivor regions from the previous
// pause are appended to the RHS of the young list, i.e.
// [Newly Young Regions ++ Survivors from last pause].
uint survivor_region_length = survivors->length();
uint eden_region_length = _g1h->eden_regions_count();
uint survivor_region_length = survivors->length();
init_region_lengths(eden_region_length, survivor_region_length);
verify_young_cset_indices();
@ -432,19 +420,23 @@ double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1Survi
survivors->convert_to_eden();
_bytes_used_before = _inc_bytes_used_before;
time_remaining_ms = MAX2(time_remaining_ms - _inc_predicted_elapsed_time_ms, 0.0);
log_trace(gc, ergo, cset)("Add young regions to CSet. eden: %u regions, survivors: %u regions, predicted young region time: %1.2fms, target pause time: %1.2fms",
eden_region_length, survivor_region_length, _inc_predicted_elapsed_time_ms, target_pause_time_ms);
// The number of recorded young regions is the incremental
// collection set's current size
set_recorded_rs_length(_inc_recorded_rs_length);
double young_end_time_sec = os::elapsedTime();
phase_times()->record_young_cset_choice_time_ms((young_end_time_sec - young_start_time_sec) * 1000.0);
double predicted_base_time_ms = _policy->predict_base_elapsed_time_ms(pending_cards);
double predicted_eden_time = _inc_predicted_non_copy_time_ms + _policy->predict_eden_copy_time_ms(eden_region_length);
double remaining_time_ms = MAX2(target_pause_time_ms - (predicted_base_time_ms + predicted_eden_time), 0.0);
return time_remaining_ms;
log_trace(gc, ergo, cset)("Added young regions to CSet. Eden: %u regions, Survivors: %u regions, "
"predicted eden time: %1.2fms, predicted base time: %1.2fms, target pause time: %1.2fms, remaining time: %1.2fms",
eden_region_length, survivor_region_length,
predicted_eden_time, predicted_base_time_ms, target_pause_time_ms, remaining_time_ms);
phase_times()->record_young_cset_choice_time_ms((Ticks::now() - start_time).seconds() * 1000.0);
return remaining_time_ms;
}
static int compare_region_idx(const uint a, const uint b) {

View File

@ -174,6 +174,22 @@ class G1CollectionSet {
CSetBuildType _inc_build_state;
size_t _inc_part_start;
// Information about eden regions in the incremental collection set.
struct IncCollectionSetRegionStat {
// The predicted non-copy time that was added to the total incremental value
// for the collection set.
double _non_copy_time_ms;
// The remembered set length that was added to the total incremental value
// for the collection set.
size_t _rs_length;
#ifdef ASSERT
// Resets members to "uninitialized" values.
void reset() { _rs_length = ~(size_t)0; _non_copy_time_ms = -1.0; }
#endif
};
IncCollectionSetRegionStat* _inc_collection_set_stats;
// The associated information that is maintained while the incremental
// collection set is being built with *young* regions. Used to populate
// the recorded info for the evacuation pause.
@ -195,25 +211,25 @@ class G1CollectionSet {
// the RSets grow. Instead of having to synchronize updates to that
// field we accumulate them in this field and add it to
// _inc_recorded_rs_length_diff at the start of a GC.
ssize_t _inc_recorded_rs_length_diff;
size_t _inc_recorded_rs_length_diff;
// The predicted elapsed time it will take to collect the regions in
// the CSet. This is updated by the thread that adds a new region to
// the CSet. See the comment for _inc_recorded_rs_length about
// MT-safety assumptions.
double _inc_predicted_elapsed_time_ms;
double _inc_predicted_non_copy_time_ms;
// See the comment for _inc_recorded_rs_length_diff.
double _inc_predicted_elapsed_time_ms_diff;
double _inc_predicted_non_copy_time_ms_diff;
void set_recorded_rs_length(size_t rs_length);
G1CollectorState* collector_state();
G1CollectorState* collector_state() const;
G1GCPhaseTimes* phase_times();
void verify_young_cset_indices() const NOT_DEBUG_RETURN;
double predict_region_elapsed_time_ms(HeapRegion* hr);
double predict_region_non_copy_time_ms(HeapRegion* hr) const;
// Update the incremental collection set information when adding a region.
void add_young_region_common(HeapRegion* hr);

View File

@ -2589,7 +2589,7 @@ void G1CMTask::do_marking_step(double time_target_ms,
bool do_stealing = do_termination && !is_serial;
G1Predictions const& predictor = _g1h->policy()->predictor();
double diff_prediction_ms = predictor.get_new_lower_zero_bound_prediction(&_marking_step_diff_ms);
double diff_prediction_ms = predictor.predict_zero_bounded(&_marking_step_diff_ms);
_time_target_ms = time_target_ms - diff_prediction_ms;
// set up the variables that are used in the work-based scheme to

View File

@ -113,8 +113,8 @@ size_t G1AdaptiveIHOPControl::actual_target_threshold() const {
);
}
double G1AdaptiveIHOPControl::get_new_prediction(TruncatedSeq const* seq) const {
return _predictor->get_new_lower_zero_bound_prediction(seq);
double G1AdaptiveIHOPControl::predict(TruncatedSeq const* seq) const {
return _predictor->predict_zero_bounded(seq);
}
bool G1AdaptiveIHOPControl::have_enough_data_for_prediction() const {
@ -124,8 +124,8 @@ bool G1AdaptiveIHOPControl::have_enough_data_for_prediction() const {
size_t G1AdaptiveIHOPControl::get_conc_mark_start_threshold() {
if (have_enough_data_for_prediction()) {
double pred_marking_time = get_new_prediction(&_marking_times_s);
double pred_promotion_rate = get_new_prediction(&_allocation_rate_s);
double pred_marking_time = predict(&_marking_times_s);
double pred_promotion_rate = predict(&_allocation_rate_s);
size_t pred_promotion_size = (size_t)(pred_marking_time * pred_promotion_rate);
size_t predicted_needed_bytes_during_marking =
@ -172,8 +172,8 @@ void G1AdaptiveIHOPControl::print() {
actual_target,
G1CollectedHeap::heap()->used(),
_last_unrestrained_young_size,
get_new_prediction(&_allocation_rate_s),
get_new_prediction(&_marking_times_s) * 1000.0,
predict(&_allocation_rate_s),
predict(&_marking_times_s) * 1000.0,
have_enough_data_for_prediction() ? "true" : "false");
}
@ -183,7 +183,7 @@ void G1AdaptiveIHOPControl::send_trace_event(G1NewTracer* tracer) {
actual_target_threshold(),
G1CollectedHeap::heap()->used(),
_last_unrestrained_young_size,
get_new_prediction(&_allocation_rate_s),
get_new_prediction(&_marking_times_s),
predict(&_allocation_rate_s),
predict(&_marking_times_s),
have_enough_data_for_prediction());
}

View File

@ -124,7 +124,7 @@ class G1AdaptiveIHOPControl : public G1IHOPControl {
size_t _last_unrestrained_young_size;
// Get a new prediction bounded below by zero from the given sequence.
double get_new_prediction(TruncatedSeq const* seq) const;
double predict(TruncatedSeq const* seq) const;
bool have_enough_data_for_prediction() const;

View File

@ -62,8 +62,8 @@ G1Policy::G1Policy(STWGCTimer* gc_timer) :
_young_list_target_length(0),
_young_list_fixed_length(0),
_young_list_max_length(0),
_short_lived_surv_rate_group(new SurvRateGroup()),
_survivor_surv_rate_group(new SurvRateGroup()),
_eden_surv_rate_group(new G1SurvRateGroup()),
_survivor_surv_rate_group(new G1SurvRateGroup()),
_reserve_factor((double) G1ReservePercent / 100.0),
_reserve_regions(0),
_young_gen_sizer(G1YoungGenSizer::create_gen_sizer()),
@ -127,19 +127,16 @@ void G1Policy::note_gc_start() {
}
class G1YoungLengthPredictor {
const bool _during_cm;
const double _base_time_ms;
const double _base_free_regions;
const double _target_pause_time_ms;
const G1Policy* const _policy;
public:
G1YoungLengthPredictor(bool during_cm,
double base_time_ms,
G1YoungLengthPredictor(double base_time_ms,
double base_free_regions,
double target_pause_time_ms,
const G1Policy* policy) :
_during_cm(during_cm),
_base_time_ms(base_time_ms),
_base_free_regions(base_free_regions),
_target_pause_time_ms(target_pause_time_ms),
@ -151,11 +148,8 @@ class G1YoungLengthPredictor {
return false;
}
const double accum_surv_rate = _policy->accum_yg_surv_rate_pred((int) young_length - 1);
const size_t bytes_to_copy =
(size_t) (accum_surv_rate * (double) HeapRegion::GrainBytes);
const double copy_time_ms =
_policy->analytics()->predict_object_copy_time_ms(bytes_to_copy, _during_cm);
size_t bytes_to_copy = 0;
const double copy_time_ms = _policy->predict_eden_copy_time_ms(young_length, &bytes_to_copy);
const double young_other_time_ms = _policy->analytics()->predict_young_other_time_ms(young_length);
const double pause_time_ms = _base_time_ms + copy_time_ms + young_other_time_ms;
if (pause_time_ms > _target_pause_time_ms) {
@ -303,11 +297,10 @@ G1Policy::YoungTargetLengths G1Policy::young_list_target_lengths(size_t rs_lengt
return result;
}
uint
G1Policy::calculate_young_list_target_length(size_t rs_length,
uint base_min_length,
uint desired_min_length,
uint desired_max_length) const {
uint G1Policy::calculate_young_list_target_length(size_t rs_length,
uint base_min_length,
uint desired_min_length,
uint desired_max_length) const {
assert(use_adaptive_young_list_length(), "pre-condition");
assert(collector_state()->in_young_only_phase(), "only call this for young GCs");
@ -327,11 +320,8 @@ G1Policy::calculate_young_list_target_length(size_t rs_length,
uint max_young_length = desired_max_length - base_min_length;
const double target_pause_time_ms = _mmu_tracker->max_gc_time() * 1000.0;
const double survivor_regions_evac_time = predict_survivor_regions_evac_time();
const size_t pending_cards = _analytics->predict_pending_cards();
const double base_time_ms =
predict_base_elapsed_time_ms(pending_cards, rs_length) +
survivor_regions_evac_time;
const double base_time_ms = predict_base_elapsed_time_ms(pending_cards, rs_length);
const uint available_free_regions = _free_regions_at_end_of_collection;
const uint base_free_regions =
available_free_regions > _reserve_regions ? available_free_regions - _reserve_regions : 0;
@ -339,8 +329,7 @@ G1Policy::calculate_young_list_target_length(size_t rs_length,
// Here, we will make sure that the shortest young length that
// makes sense fits within the target pause time.
G1YoungLengthPredictor p(collector_state()->mark_or_rebuild_in_progress(),
base_time_ms,
G1YoungLengthPredictor p(base_time_ms,
base_free_regions,
target_pause_time_ms,
this);
@ -406,11 +395,10 @@ G1Policy::calculate_young_list_target_length(size_t rs_length,
double G1Policy::predict_survivor_regions_evac_time() const {
double survivor_regions_evac_time = 0.0;
const GrowableArray<HeapRegion*>* survivor_regions = _g1h->survivor()->regions();
for (GrowableArrayIterator<HeapRegion*> it = survivor_regions->begin();
it != survivor_regions->end();
++it) {
survivor_regions_evac_time += predict_region_elapsed_time_ms(*it, collector_state()->in_young_only_phase());
survivor_regions_evac_time += predict_region_total_time_ms(*it, collector_state()->in_young_only_phase());
}
return survivor_regions_evac_time;
}
@ -466,11 +454,10 @@ void G1Policy::record_full_collection_end() {
collector_state()->set_mark_or_rebuild_in_progress(false);
collector_state()->set_clearing_next_bitmap(false);
_short_lived_surv_rate_group->start_adding_regions();
_eden_surv_rate_group->start_adding_regions();
// also call this on any additional surv rate groups
_free_regions_at_end_of_collection = _g1h->num_free_regions();
// Reset survivors SurvRateGroup.
_survivor_surv_rate_group->reset();
update_young_list_max_and_target_length();
update_rs_length_prediction();
@ -553,7 +540,7 @@ void G1Policy::record_collection_pause_start(double start_time_sec) {
_collection_set->reset_bytes_used_before();
// do that for any other surv rate groups
_short_lived_surv_rate_group->stop_adding_regions();
_eden_surv_rate_group->stop_adding_regions();
_survivors_age_table.clear();
assert(_g1h->collection_set()->verify_young_ages(), "region age verification failed");
@ -711,7 +698,7 @@ void G1Policy::record_collection_pause_end(double pause_time_ms) {
}
}
_short_lived_surv_rate_group->start_adding_regions();
_eden_surv_rate_group->start_adding_regions();
double merge_hcc_time_ms = average_time_ms(G1GCPhaseTimes::MergeHCC);
if (update_stats) {
@ -911,23 +898,14 @@ void G1Policy::print_phases() {
phase_times()->print();
}
double G1Policy::predict_yg_surv_rate(int age, SurvRateGroup* surv_rate_group) const {
TruncatedSeq* seq = surv_rate_group->get_seq(age);
guarantee(seq->num() > 0, "There should be some young gen survivor samples available. Tried to access with age %d", age);
return _predictor.get_new_unit_prediction(seq);
}
double G1Policy::accum_yg_surv_rate_pred(int age) const {
return _short_lived_surv_rate_group->accum_surv_rate_pred(age);
}
double G1Policy::predict_base_elapsed_time_ms(size_t pending_cards,
size_t rs_length) const {
size_t effective_scanned_cards = _analytics->predict_scan_card_num(rs_length, collector_state()->in_young_only_phase());
return
_analytics->predict_card_merge_time_ms(pending_cards + rs_length, collector_state()->in_young_only_phase()) +
_analytics->predict_card_scan_time_ms(effective_scanned_cards, collector_state()->in_young_only_phase()) +
_analytics->predict_constant_other_time_ms();
_analytics->predict_constant_other_time_ms() +
predict_survivor_regions_evac_time();
}
double G1Policy::predict_base_elapsed_time_ms(size_t pending_cards) const {
@ -940,25 +918,35 @@ size_t G1Policy::predict_bytes_to_copy(HeapRegion* hr) const {
if (!hr->is_young()) {
bytes_to_copy = hr->max_live_bytes();
} else {
assert(hr->age_in_surv_rate_group() != -1, "invariant");
int age = hr->age_in_surv_rate_group();
double yg_surv_rate = predict_yg_surv_rate(age, hr->surv_rate_group());
bytes_to_copy = (size_t) (hr->used() * yg_surv_rate);
bytes_to_copy = (size_t) (hr->used() * hr->surv_rate_prediction(_predictor));
}
return bytes_to_copy;
}
double G1Policy::predict_region_elapsed_time_ms(HeapRegion* hr,
bool for_young_gc) const {
double G1Policy::predict_eden_copy_time_ms(uint count, size_t* bytes_to_copy) const {
if (count == 0) {
return 0.0;
}
size_t const expected_bytes = _eden_surv_rate_group->accum_surv_rate_pred(count) * HeapRegion::GrainBytes;
if (bytes_to_copy != NULL) {
*bytes_to_copy = expected_bytes;
}
return _analytics->predict_object_copy_time_ms(expected_bytes, collector_state()->mark_or_rebuild_in_progress());
}
double G1Policy::predict_region_copy_time_ms(HeapRegion* hr) const {
size_t const bytes_to_copy = predict_bytes_to_copy(hr);
return _analytics->predict_object_copy_time_ms(bytes_to_copy, collector_state()->mark_or_rebuild_in_progress());
}
double G1Policy::predict_region_non_copy_time_ms(HeapRegion* hr,
bool for_young_gc) const {
size_t rs_length = hr->rem_set()->occupied();
size_t scan_card_num = _analytics->predict_scan_card_num(rs_length, for_young_gc);
size_t bytes_to_copy = predict_bytes_to_copy(hr);
double region_elapsed_time_ms =
_analytics->predict_card_merge_time_ms(rs_length, collector_state()->in_young_only_phase()) +
_analytics->predict_card_scan_time_ms(scan_card_num, collector_state()->in_young_only_phase()) +
_analytics->predict_object_copy_time_ms(bytes_to_copy, collector_state()->mark_or_rebuild_in_progress());
_analytics->predict_card_scan_time_ms(scan_card_num, collector_state()->in_young_only_phase());
// The prediction of the "other" time for this region is based
// upon the region type and NOT the GC type.
@ -970,6 +958,10 @@ double G1Policy::predict_region_elapsed_time_ms(HeapRegion* hr,
return region_elapsed_time_ms;
}
double G1Policy::predict_region_total_time_ms(HeapRegion* hr, bool for_young_gc) const {
return predict_region_non_copy_time_ms(hr, for_young_gc) + predict_region_copy_time_ms(hr);
}
bool G1Policy::should_allocate_mutator_region() const {
uint young_list_length = _g1h->young_regions_count();
uint young_list_target_length = _young_list_target_length;
@ -1317,7 +1309,7 @@ void G1Policy::calculate_old_collection_set_regions(G1CollectionSetCandidates* c
break;
}
double predicted_time_ms = predict_region_elapsed_time_ms(hr, false);
double predicted_time_ms = predict_region_total_time_ms(hr, false);
time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0);
// Add regions to old set until we reach the minimum amount
if (num_initial_regions < min_old_cset_length) {
@ -1377,7 +1369,7 @@ void G1Policy::calculate_optional_collection_set_regions(G1CollectionSetCandidat
HeapRegion* r = candidates->at(candidate_idx);
while (num_optional_regions < max_optional_regions) {
assert(r != NULL, "Region must exist");
prediction_ms += predict_region_elapsed_time_ms(r, false);
prediction_ms += predict_region_total_time_ms(r, false);
if (prediction_ms > time_remaining_ms) {
log_debug(gc, ergo, cset)("Prediction %.3fms for region %u does not fit remaining time: %.3fms.",
@ -1396,10 +1388,7 @@ void G1Policy::calculate_optional_collection_set_regions(G1CollectionSetCandidat
}
void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) {
// Add survivor regions to SurvRateGroup.
note_start_adding_survivor_regions();
finished_recalculating_age_indexes(true /* is_survivors */);
HeapRegion* last = NULL;
for (GrowableArrayIterator<HeapRegion*> it = survivors->regions()->begin();
@ -1421,6 +1410,4 @@ void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) {
// the next evacuation pause - we need it in order to re-tag
// the survivor regions from this evacuation pause as 'young'
// at the start of the next.
finished_recalculating_age_indexes(false /* is_survivors */);
}

View File

@ -82,10 +82,10 @@ class G1Policy: public CHeapObj<mtGC> {
// locker is active. This should be >= _young_list_target_length;
uint _young_list_max_length;
// SurvRateGroups below must be initialized after the predictor because they
// indirectly use it through this object passed to their constructor.
SurvRateGroup* _short_lived_surv_rate_group;
SurvRateGroup* _survivor_surv_rate_group;
// The survivor rate groups below must be initialized after the predictor because they
// indirectly use it through the "this" object passed to their constructor.
G1SurvRateGroup* _eden_surv_rate_group;
G1SurvRateGroup* _survivor_surv_rate_group;
double _reserve_factor;
// This will be set when the heap is expanded
@ -128,7 +128,7 @@ public:
void set_region_eden(HeapRegion* hr) {
hr->set_eden();
hr->install_surv_rate_group(_short_lived_surv_rate_group);
hr->install_surv_rate_group(_eden_surv_rate_group);
}
void set_region_survivor(HeapRegion* hr) {
@ -141,17 +141,22 @@ public:
}
double predict_base_elapsed_time_ms(size_t num_pending_cards) const;
double predict_base_elapsed_time_ms(size_t num_pending_cards,
size_t rs_length) const;
size_t predict_bytes_to_copy(HeapRegion* hr) const;
double predict_region_elapsed_time_ms(HeapRegion* hr, bool for_young_gc) const;
double predict_survivor_regions_evac_time() const;
private:
double predict_base_elapsed_time_ms(size_t num_pending_cards, size_t rs_length) const;
double predict_region_copy_time_ms(HeapRegion* hr) const;
public:
double predict_eden_copy_time_ms(uint count, size_t* bytes_to_copy = NULL) const;
double predict_region_non_copy_time_ms(HeapRegion* hr, bool for_young_gc) const;
double predict_region_total_time_ms(HeapRegion* hr, bool for_young_gc) const;
void cset_regions_freed() {
bool update = should_update_surv_rate_group_predictors();
_short_lived_surv_rate_group->all_surviving_words_recorded(predictor(), update);
_eden_surv_rate_group->all_surviving_words_recorded(predictor(), update);
_survivor_surv_rate_group->all_surviving_words_recorded(predictor(), update);
}
@ -167,12 +172,6 @@ public:
return _mmu_tracker->max_gc_time() * 1000.0;
}
double predict_yg_surv_rate(int age, SurvRateGroup* surv_rate_group) const;
double predict_yg_surv_rate(int age) const;
double accum_yg_surv_rate_pred(int age) const;
private:
G1CollectionSet* _collection_set;
double average_time_ms(G1GCPhaseTimes::GCParPhases phase) const;
@ -236,6 +235,9 @@ private:
void update_rs_length_prediction();
void update_rs_length_prediction(size_t prediction);
size_t predict_bytes_to_copy(HeapRegion* hr) const;
double predict_survivor_regions_evac_time() const;
// Check whether a given young length (young_length) fits into the
// given target pause time and whether the prediction for the amount
// of objects to be copied for the given length will fit into the
@ -375,14 +377,6 @@ public:
// the initial-mark work and start a marking cycle.
void decide_on_conc_mark_initiation();
void finished_recalculating_age_indexes(bool is_survivors) {
if (is_survivors) {
_survivor_surv_rate_group->finished_recalculating_age_indexes();
} else {
_short_lived_surv_rate_group->finished_recalculating_age_indexes();
}
}
size_t young_list_target_length() const { return _young_list_target_length; }
bool should_allocate_mutator_region() const;

View File

@ -54,16 +54,16 @@ class G1Predictions {
// Confidence factor.
double sigma() const { return _sigma; }
double get_new_prediction(TruncatedSeq const* seq) const {
double predict(TruncatedSeq const* seq) const {
return seq->davg() + _sigma * stddev_estimate(seq);
}
double get_new_unit_prediction(TruncatedSeq const* seq) const {
return clamp(get_new_prediction(seq), 0.0, 1.0);
double predict_in_unit_interval(TruncatedSeq const* seq) const {
return clamp(predict(seq), 0.0, 1.0);
}
double get_new_lower_zero_bound_prediction(TruncatedSeq const* seq) const {
return MAX2(get_new_prediction(seq), 0.0);
double predict_zero_bounded(TruncatedSeq const* seq) const {
return MAX2(predict(seq), 0.0);
}
};

View File

@ -172,7 +172,7 @@ void G1RemSetTrackingPolicy::update_after_rebuild(HeapRegion* r) {
p2i(r->next_top_at_mark_start()),
cm->liveness(r->hrm_index()) * HeapWordSize,
r->next_marked_bytes(),
r->rem_set()->occupied_locked(),
r->rem_set()->occupied(),
r->rem_set()->mem_size());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2019, 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
@ -25,98 +25,92 @@
#include "precompiled.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1Predictions.hpp"
#include "gc/g1/g1SurvRateGroup.hpp"
#include "gc/g1/heapRegion.hpp"
#include "gc/g1/survRateGroup.hpp"
#include "logging/log.hpp"
#include "memory/allocation.hpp"
SurvRateGroup::SurvRateGroup() :
G1SurvRateGroup::G1SurvRateGroup() :
_stats_arrays_length(0),
_accum_surv_rate_pred(NULL),
_last_pred(0.0),
_surv_rate_pred(NULL),
_all_regions_allocated(0),
_region_num(0),
_setup_seq_num(0)
{
_surv_rate_predictors(NULL),
_num_added_regions(0) {
reset();
start_adding_regions();
}
void SurvRateGroup::reset() {
_all_regions_allocated = 0;
_setup_seq_num = 0;
_last_pred = 0.0;
void G1SurvRateGroup::reset() {
_last_pred = 0.0;
// the following will set up the arrays with length 1
_region_num = 1;
_num_added_regions = 1;
// The call to stop_adding_regions() will use "new" to refill
// the _surv_rate_pred array, so we need to make sure to call
// "delete".
for (size_t i = 0; i < _stats_arrays_length; ++i) {
delete _surv_rate_pred[i];
delete _surv_rate_predictors[i];
}
_stats_arrays_length = 0;
stop_adding_regions();
// Seed initial _surv_rate_pred and _accum_surv_rate_pred values
guarantee( _stats_arrays_length == 1, "invariant" );
guarantee( _surv_rate_pred[0] != NULL, "invariant" );
guarantee(_stats_arrays_length == 1, "invariant" );
guarantee(_surv_rate_predictors[0] != NULL, "invariant" );
const double initial_surv_rate = 0.4;
_surv_rate_pred[0]->add(initial_surv_rate);
_surv_rate_predictors[0]->add(initial_surv_rate);
_last_pred = _accum_surv_rate_pred[0] = initial_surv_rate;
_region_num = 0;
_num_added_regions = 0;
}
void SurvRateGroup::start_adding_regions() {
_setup_seq_num = _stats_arrays_length;
_region_num = 0;
void G1SurvRateGroup::start_adding_regions() {
_num_added_regions = 0;
}
void SurvRateGroup::stop_adding_regions() {
if (_region_num > _stats_arrays_length) {
_accum_surv_rate_pred = REALLOC_C_HEAP_ARRAY(double, _accum_surv_rate_pred, _region_num, mtGC);
_surv_rate_pred = REALLOC_C_HEAP_ARRAY(TruncatedSeq*, _surv_rate_pred, _region_num, mtGC);
void G1SurvRateGroup::stop_adding_regions() {
if (_num_added_regions > _stats_arrays_length) {
_accum_surv_rate_pred = REALLOC_C_HEAP_ARRAY(double, _accum_surv_rate_pred, _num_added_regions, mtGC);
_surv_rate_predictors = REALLOC_C_HEAP_ARRAY(TruncatedSeq*, _surv_rate_predictors, _num_added_regions, mtGC);
for (size_t i = _stats_arrays_length; i < _region_num; ++i) {
_surv_rate_pred[i] = new TruncatedSeq(10);
for (size_t i = _stats_arrays_length; i < _num_added_regions; ++i) {
_surv_rate_predictors[i] = new TruncatedSeq(10);
}
_stats_arrays_length = _region_num;
_stats_arrays_length = _num_added_regions;
}
}
void SurvRateGroup::record_surviving_words(int age_in_group, size_t surv_words) {
guarantee( 0 <= age_in_group && (size_t) age_in_group < _region_num,
"pre-condition" );
void G1SurvRateGroup::record_surviving_words(int age_in_group, size_t surv_words) {
guarantee(0 <= age_in_group && (size_t)age_in_group < _num_added_regions,
"age_in_group is %d not between 0 and " SIZE_FORMAT, age_in_group, _num_added_regions);
double surv_rate = (double) surv_words / (double) HeapRegion::GrainWords;
_surv_rate_pred[age_in_group]->add(surv_rate);
double surv_rate = (double)surv_words / HeapRegion::GrainWords;
_surv_rate_predictors[age_in_group]->add(surv_rate);
}
void SurvRateGroup::all_surviving_words_recorded(const G1Predictions& predictor, bool update_predictors) {
void G1SurvRateGroup::all_surviving_words_recorded(const G1Predictions& predictor, bool update_predictors) {
if (update_predictors) {
fill_in_last_surv_rates();
}
finalize_predictions(predictor);
}
void SurvRateGroup::fill_in_last_surv_rates() {
if (_region_num > 0) { // conservative
double surv_rate = _surv_rate_pred[_region_num-1]->last();
for (size_t i = _region_num; i < _stats_arrays_length; ++i) {
_surv_rate_pred[i]->add(surv_rate);
void G1SurvRateGroup::fill_in_last_surv_rates() {
if (_num_added_regions > 0) { // conservative
double surv_rate = _surv_rate_predictors[_num_added_regions-1]->last();
for (size_t i = _num_added_regions; i < _stats_arrays_length; ++i) {
_surv_rate_predictors[i]->add(surv_rate);
}
}
}
void SurvRateGroup::finalize_predictions(const G1Predictions& predictor) {
void G1SurvRateGroup::finalize_predictions(const G1Predictions& predictor) {
double accum = 0.0;
double pred = 0.0;
for (size_t i = 0; i < _stats_arrays_length; ++i) {
pred = predictor.get_new_unit_prediction(_surv_rate_pred[i]);
pred = predictor.predict_in_unit_interval(_surv_rate_predictors[i]);
accum += pred;
_accum_surv_rate_pred[i] = accum;
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2001, 2019, 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.
*
*/
#ifndef SHARE_GC_G1_G1SURVRATEGROUP_HPP
#define SHARE_GC_G1_G1SURVRATEGROUP_HPP
#include "gc/g1/g1Predictions.hpp"
#include "utilities/numberSeq.hpp"
// A survivor rate group tracks survival ratios of objects allocated in the
// heap regions associated to a set of regions (a "space", i.e. eden or survivor)
// on a time basis to predict future survival rates of regions of the same "age".
//
// Every time a new heap region associated with a survivor rate group is retired
// (i.e. the time basis), it gets associated the next "age" entry in that group.
//
// During garbage collection G1 keeps track how much of total data is copied out
// of a heap region (i.e. survives), to update the survivor rate predictor of that age.
//
// This information is used to predict, given a particular age of a heap region,
// how much of its contents will likely survive to determine young generation sizes.
//
// The age index associated with a heap region is incremented from 0 (retired first)
// to N (retired just before the GC).
//
// To avoid copying around data all the time when the total amount of regions in
// a survivor rate group changes, this class organizes the arrays containing the
// predictors in reverse chronological order as returned by age_in_group(). I.e.
// index 0 contains the rate information for the region retired most recently.
class G1SurvRateGroup : public CHeapObj<mtGC> {
size_t _stats_arrays_length;
double* _accum_surv_rate_pred;
double _last_pred;
TruncatedSeq** _surv_rate_predictors;
size_t _num_added_regions; // The number of regions in this survivor rate group.
void fill_in_last_surv_rates();
void finalize_predictions(const G1Predictions& predictor);
public:
static const int InvalidAgeIndex = -1;
static bool is_valid_age_index(int age) { return age >= 0; }
G1SurvRateGroup();
void reset();
void start_adding_regions();
void stop_adding_regions();
void record_surviving_words(int age_in_group, size_t surv_words);
void all_surviving_words_recorded(const G1Predictions& predictor, bool update_predictors);
double accum_surv_rate_pred(int age) const {
assert(_stats_arrays_length > 0, "invariant" );
assert(is_valid_age_index(age), "must be");
if ((size_t)age < _stats_arrays_length)
return _accum_surv_rate_pred[age];
else {
double diff = (double)(age - _stats_arrays_length + 1);
return _accum_surv_rate_pred[_stats_arrays_length - 1] + diff * _last_pred;
}
}
double surv_rate_pred(G1Predictions const& predictor, int age) const {
assert(is_valid_age_index(age), "must be");
age = MIN2(age, (int)_stats_arrays_length - 1);
return predictor.predict_in_unit_interval(_surv_rate_predictors[age]);
}
int next_age_index() {
return (int)++_num_added_regions;
}
int age_in_group(int age_index) const {
int result = (int)(_num_added_regions - age_index);
assert(is_valid_age_index(result), "invariant" );
return result;
}
};
#endif // SHARE_GC_G1_G1SURVRATEGROUP_HPP

View File

@ -137,8 +137,6 @@ void HeapRegion::hr_clear(bool keep_remset, bool clear_space, bool locked) {
_evacuation_failed = false;
_gc_efficiency = 0.0;
_recorded_rs_length = 0;
_predicted_elapsed_time_ms = 0.0;
}
void HeapRegion::clear_cardtable() {
@ -149,14 +147,12 @@ void HeapRegion::clear_cardtable() {
void HeapRegion::calc_gc_efficiency() {
// GC efficiency is the ratio of how much space would be
// reclaimed over how long we predict it would take to reclaim it.
G1CollectedHeap* g1h = G1CollectedHeap::heap();
G1Policy* policy = g1h->policy();
G1Policy* policy = G1CollectedHeap::heap()->policy();
// Retrieve a prediction of the elapsed time for this region for
// a mixed gc because the region will only be evacuated during a
// mixed gc.
double region_elapsed_time_ms =
policy->predict_region_elapsed_time_ms(this, false /* for_young_gc */);
double region_elapsed_time_ms = policy->predict_region_total_time_ms(this, false /* for_young_gc */);
_gc_efficiency = (double) reclaimable_bytes() / region_elapsed_time_ms;
}
@ -256,8 +252,7 @@ HeapRegion::HeapRegion(uint hrm_index,
_prev_top_at_mark_start(NULL), _next_top_at_mark_start(NULL),
_prev_marked_bytes(0), _next_marked_bytes(0),
_young_index_in_cset(-1),
_surv_rate_group(NULL), _age_index(-1), _gc_efficiency(0.0),
_recorded_rs_length(0), _predicted_elapsed_time_ms(0),
_surv_rate_group(NULL), _age_index(G1SurvRateGroup::InvalidAgeIndex), _gc_efficiency(0.0),
_node_index(G1NUMA::UnknownNodeIndex)
{
assert(Universe::on_page_boundary(mr.start()) && Universe::on_page_boundary(mr.end()),

View File

@ -27,9 +27,9 @@
#include "gc/g1/g1BlockOffsetTable.hpp"
#include "gc/g1/g1HeapRegionTraceType.hpp"
#include "gc/g1/g1SurvRateGroup.hpp"
#include "gc/g1/heapRegionTracer.hpp"
#include "gc/g1/heapRegionType.hpp"
#include "gc/g1/survRateGroup.hpp"
#include "gc/shared/ageTable.hpp"
#include "gc/shared/spaceDecorator.hpp"
#include "gc/shared/verifyOption.hpp"
@ -38,6 +38,7 @@
class G1CollectedHeap;
class G1CMBitMap;
class G1Predictions;
class HeapRegionRemSet;
class HeapRegion;
class HeapRegionSetBase;
@ -245,7 +246,7 @@ private:
// Data for young region survivor prediction.
uint _young_index_in_cset;
SurvRateGroup* _surv_rate_group;
G1SurvRateGroup* _surv_rate_group;
int _age_index;
// Cached attributes used in the collection set policy information
@ -253,14 +254,6 @@ private:
// The calculated GC efficiency of the region.
double _gc_efficiency;
// The remembered set length that was added to the total value
// for the collection set.
size_t _recorded_rs_length;
// The predicted elapsed time that was added to total value
// for the collection set.
double _predicted_elapsed_time_ms;
uint _node_index;
void report_region_type_change(G1HeapRegionTraceType::Type to);
@ -544,50 +537,17 @@ public:
_young_index_in_cset = index;
}
int age_in_surv_rate_group() {
assert(_surv_rate_group != NULL, "pre-condition");
assert(_age_index > -1, "pre-condition");
return _surv_rate_group->age_in_group(_age_index);
}
int age_in_surv_rate_group() const;
bool has_valid_age_in_surv_rate() const;
void record_surv_words_in_group(size_t words_survived) {
assert(_surv_rate_group != NULL, "pre-condition");
assert(_age_index > -1, "pre-condition");
int age_in_group = age_in_surv_rate_group();
_surv_rate_group->record_surviving_words(age_in_group, words_survived);
}
bool has_surv_rate_group() const;
int age_in_surv_rate_group_cond() {
if (_surv_rate_group != NULL)
return age_in_surv_rate_group();
else
return -1;
}
double surv_rate_prediction(G1Predictions const& predictor) const;
SurvRateGroup* surv_rate_group() {
return _surv_rate_group;
}
void install_surv_rate_group(G1SurvRateGroup* surv_rate_group);
void uninstall_surv_rate_group();
void install_surv_rate_group(SurvRateGroup* surv_rate_group) {
assert(surv_rate_group != NULL, "pre-condition");
assert(_surv_rate_group == NULL, "pre-condition");
assert(is_young(), "pre-condition");
_surv_rate_group = surv_rate_group;
_age_index = surv_rate_group->next_age_index();
}
void uninstall_surv_rate_group() {
if (_surv_rate_group != NULL) {
assert(_age_index > -1, "pre-condition");
assert(is_young(), "pre-condition");
_surv_rate_group = NULL;
_age_index = -1;
} else {
assert(_age_index == -1, "pre-condition");
}
}
void record_surv_words_in_group(size_t words_survived);
// Determine if an object has been allocated since the last
// mark performed by the collector. This returns true iff the object
@ -610,17 +570,6 @@ public:
template <bool is_gc_active, class Closure>
inline HeapWord* oops_on_memregion_seq_iterate_careful(MemRegion mr, Closure* cl);
size_t recorded_rs_length() const { return _recorded_rs_length; }
double predicted_elapsed_time_ms() const { return _predicted_elapsed_time_ms; }
void set_recorded_rs_length(size_t rs_length) {
_recorded_rs_length = rs_length;
}
void set_predicted_elapsed_time_ms(double ms) {
_predicted_elapsed_time_ms = ms;
}
// Routines for managing a list of code roots (attached to the
// this region's RSet) that point into this heap region.
void add_strong_code_root(nmethod* nm);

View File

@ -28,6 +28,7 @@
#include "gc/g1/g1BlockOffsetTable.inline.hpp"
#include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/g1ConcurrentMarkBitMap.inline.hpp"
#include "gc/g1/g1Predictions.hpp"
#include "gc/g1/heapRegion.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/atomic.hpp"
@ -370,4 +371,51 @@ HeapWord* HeapRegion::oops_on_memregion_seq_iterate_careful(MemRegion mr,
}
}
inline int HeapRegion::age_in_surv_rate_group() const {
assert(has_surv_rate_group(), "pre-condition");
assert(has_valid_age_in_surv_rate(), "pre-condition");
return _surv_rate_group->age_in_group(_age_index);
}
inline bool HeapRegion::has_valid_age_in_surv_rate() const {
return G1SurvRateGroup::is_valid_age_index(_age_index);
}
inline bool HeapRegion::has_surv_rate_group() const {
return _surv_rate_group != NULL;
}
inline double HeapRegion::surv_rate_prediction(G1Predictions const& predictor) const {
assert(has_surv_rate_group(), "pre-condition");
return _surv_rate_group->surv_rate_pred(predictor, age_in_surv_rate_group());
}
inline void HeapRegion::install_surv_rate_group(G1SurvRateGroup* surv_rate_group) {
assert(surv_rate_group != NULL, "pre-condition");
assert(!has_surv_rate_group(), "pre-condition");
assert(is_young(), "pre-condition");
_surv_rate_group = surv_rate_group;
_age_index = surv_rate_group->next_age_index();
}
inline void HeapRegion::uninstall_surv_rate_group() {
if (has_surv_rate_group()) {
assert(has_valid_age_in_surv_rate(), "pre-condition");
assert(is_young(), "pre-condition");
_surv_rate_group = NULL;
_age_index = G1SurvRateGroup::InvalidAgeIndex;
} else {
assert(!has_valid_age_in_surv_rate(), "pre-condition");
}
}
inline void HeapRegion::record_surv_words_in_group(size_t words_survived) {
assert(has_surv_rate_group(), "pre-condition");
assert(has_valid_age_in_surv_rate(), "pre-condition");
int age_in_group = age_in_surv_rate_group();
_surv_rate_group->record_surviving_words(age_in_group, words_survived);
}
#endif // SHARE_GC_G1_HEAPREGION_INLINE_HPP

View File

@ -28,7 +28,7 @@
#include "gc/g1/g1ConcurrentRefine.hpp"
#include "gc/g1/heapRegionManager.inline.hpp"
#include "gc/g1/heapRegionRemSet.inline.hpp"
#include "gc/shared/space.inline.hpp"
#include "gc/g1/sparsePRT.inline.hpp"
#include "memory/allocation.hpp"
#include "memory/padded.inline.hpp"
#include "oops/oop.inline.hpp"
@ -68,6 +68,7 @@ size_t OtherRegionsTable::_fine_eviction_sample_size = 0;
OtherRegionsTable::OtherRegionsTable(Mutex* m) :
_g1h(G1CollectedHeap::heap()),
_m(m),
_num_occupied(0),
_coarse_map(G1CollectedHeap::heap()->max_regions(), mtGC),
_n_coarse_entries(0),
_fine_grain_regions(NULL),
@ -183,6 +184,7 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, uint tid) {
return;
}
size_t num_added_by_coarsening = 0;
// Otherwise find a per-region table to add it to.
size_t ind = from_hrm_ind & _mod_max_fine_entries_mask;
PerRegionTable* prt = find_region_table(ind, from_hr);
@ -194,13 +196,17 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, uint tid) {
CardIdx_t card_index = card_within_region(from, from_hr);
if (_sparse_table.add_card(from_hrm_ind, card_index)) {
SparsePRT::AddCardResult result = _sparse_table.add_card(from_hrm_ind, card_index);
if (result != SparsePRT::overflow) {
if (result == SparsePRT::added) {
Atomic::inc(&_num_occupied, memory_order_relaxed);
}
assert(contains_reference_locked(from), "We just added " PTR_FORMAT " to the Sparse table", p2i(from));
return;
}
if (_n_fine_entries == _max_fine_entries) {
prt = delete_region_table();
prt = delete_region_table(num_added_by_coarsening);
// There is no need to clear the links to the 'all' list here:
// prt will be reused immediately, i.e. remain in the 'all' list.
prt->init(from_hr, false /* clear_links_to_all_list */);
@ -222,7 +228,8 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, uint tid) {
Atomic::release_store(&_fine_grain_regions[ind], prt);
_n_fine_entries++;
// Transfer from sparse to fine-grain.
// Transfer from sparse to fine-grain. The cards from the sparse table
// were already added to the total in _num_occupied.
SparsePRTEntry *sprt_entry = _sparse_table.get_entry(from_hrm_ind);
assert(sprt_entry != NULL, "There should have been an entry");
for (int i = 0; i < sprt_entry->num_valid_cards(); i++) {
@ -240,7 +247,11 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, uint tid) {
// OtherRegionsTable for why this is OK.
assert(prt != NULL, "Inv");
prt->add_reference(from);
bool added = prt->add_reference(from);
if (prt->add_reference(from)) {
num_added_by_coarsening++;
}
Atomic::add(&_num_occupied, num_added_by_coarsening, memory_order_relaxed);
assert(contains_reference(from), "We just added " PTR_FORMAT " to the PRT (%d)", p2i(from), prt->contains_reference(from));
}
@ -257,7 +268,7 @@ OtherRegionsTable::find_region_table(size_t ind, HeapRegion* hr) const {
jint OtherRegionsTable::_n_coarsenings = 0;
PerRegionTable* OtherRegionsTable::delete_region_table() {
PerRegionTable* OtherRegionsTable::delete_region_table(size_t& added_by_deleted) {
assert(_m->owned_by_self(), "Precondition");
assert(_n_fine_entries == _max_fine_entries, "Precondition");
PerRegionTable* max = NULL;
@ -307,6 +318,7 @@ PerRegionTable* OtherRegionsTable::delete_region_table() {
_n_coarse_entries++;
}
added_by_deleted = HeapRegion::CardsPerRegion - max_occ;
// Unsplice.
*max_prev = max->collision_list_next();
Atomic::inc(&_n_coarsenings);
@ -315,48 +327,15 @@ PerRegionTable* OtherRegionsTable::delete_region_table() {
}
bool OtherRegionsTable::occupancy_less_or_equal_than(size_t limit) const {
if (limit <= (size_t)G1RSetSparseRegionEntries) {
return occ_coarse() == 0 && _first_all_fine_prts == NULL && occ_sparse() <= limit;
} else {
// Current uses of this method may only use values less than G1RSetSparseRegionEntries
// for the limit. The solution, comparing against occupied() would be too slow
// at this time.
Unimplemented();
return false;
}
return occupied() <= limit;
}
bool OtherRegionsTable::is_empty() const {
return occ_sparse() == 0 && occ_coarse() == 0 && _first_all_fine_prts == NULL;
return occupied() == 0;
}
size_t OtherRegionsTable::occupied() const {
size_t sum = occ_fine();
sum += occ_sparse();
sum += occ_coarse();
return sum;
}
size_t OtherRegionsTable::occ_fine() const {
size_t sum = 0;
size_t num = 0;
PerRegionTable * cur = _first_all_fine_prts;
while (cur != NULL) {
sum += cur->occupied();
cur = cur->next();
num++;
}
guarantee(num == _n_fine_entries, "just checking");
return sum;
}
size_t OtherRegionsTable::occ_coarse() const {
return (_n_coarse_entries * HeapRegion::CardsPerRegion);
}
size_t OtherRegionsTable::occ_sparse() const {
return _sparse_table.occupied();
return _num_occupied;
}
size_t OtherRegionsTable::mem_size() const {
@ -399,6 +378,8 @@ void OtherRegionsTable::clear() {
}
_n_fine_entries = 0;
_n_coarse_entries = 0;
_num_occupied = 0;
}
bool OtherRegionsTable::contains_reference(OopOrNarrowOopStar from) const {
@ -465,7 +446,7 @@ void HeapRegionRemSet::clear_locked(bool only_cardset) {
clear_fcc();
_other_regions.clear();
set_state_empty();
assert(occupied_locked() == 0, "Should be clear.");
assert(occupied() == 0, "Should be clear.");
}
// Code roots support

View File

@ -71,6 +71,8 @@ class OtherRegionsTable {
G1CollectedHeap* _g1h;
Mutex* _m;
size_t volatile _num_occupied;
// These are protected by "_m".
CHeapBitMap _coarse_map;
size_t _n_coarse_entries;
@ -107,7 +109,7 @@ class OtherRegionsTable {
// Find, delete, and return a candidate PerRegionTable, if any exists,
// adding the deleted region to the coarse bitmap. Requires the caller
// to hold _m, and the fine-grain table to be full.
PerRegionTable* delete_region_table();
PerRegionTable* delete_region_table(size_t& added_by_deleted);
// link/add the given fine grain remembered set into the "all" list
void link_to_all(PerRegionTable * prt);
@ -116,10 +118,6 @@ class OtherRegionsTable {
bool contains_reference_locked(OopOrNarrowOopStar from) const;
size_t occ_fine() const;
size_t occ_coarse() const;
size_t occ_sparse() const;
public:
// Create a new remembered set. The given mutex is used to ensure consistency.
OtherRegionsTable(Mutex* m);
@ -194,16 +192,14 @@ public:
HeapRegion* hr() const { return Atomic::load_acquire(&_hr); }
jint occupied() const {
// Overkill, but if we ever need it...
// guarantee(_occupied == _bm.count_one_bits(), "Check");
return _occupied;
}
void init(HeapRegion* hr, bool clear_links_to_all_list);
inline void add_reference(OopOrNarrowOopStar from);
inline bool add_reference(OopOrNarrowOopStar from);
inline void add_card(CardIdx_t from_card_index);
inline bool add_card(CardIdx_t from_card_index);
// (Destructively) union the bitmap of the current table into the given
// bitmap (which is assumed to be of the same size.)
@ -325,10 +321,6 @@ public:
inline void iterate_prts(Closure& cl);
size_t occupied() {
MutexLocker x(&_m, Mutex::_no_safepoint_check_flag);
return occupied_locked();
}
size_t occupied_locked() {
return _other_regions.occupied();
}

View File

@ -36,13 +36,15 @@ inline void HeapRegionRemSet::iterate_prts(Closure& cl) {
_other_regions.iterate(cl);
}
inline void PerRegionTable::add_card(CardIdx_t from_card_index) {
inline bool PerRegionTable::add_card(CardIdx_t from_card_index) {
if (_bm.par_set_bit(from_card_index)) {
Atomic::inc(&_occupied);
Atomic::inc(&_occupied, memory_order_relaxed);
return true;
}
return false;
}
inline void PerRegionTable::add_reference(OopOrNarrowOopStar from) {
inline bool PerRegionTable::add_reference(OopOrNarrowOopStar from) {
// Must make this robust in case "from" is not in "_hr", because of
// concurrency.
@ -52,8 +54,9 @@ inline void PerRegionTable::add_reference(OopOrNarrowOopStar from) {
// and adding a bit to the new table is never incorrect.
if (loc_hr->is_in_reserved(from)) {
CardIdx_t from_card = OtherRegionsTable::card_within_region(from, loc_hr);
add_card(from_card);
return add_card(from_card);
}
return false;
}
inline void PerRegionTable::init(HeapRegion* hr, bool clear_links_to_all_list) {

View File

@ -56,19 +56,19 @@ bool SparsePRTEntry::contains_card(CardIdx_t card_index) const {
return false;
}
SparsePRTEntry::AddCardResult SparsePRTEntry::add_card(CardIdx_t card_index) {
SparsePRT::AddCardResult SparsePRTEntry::add_card(CardIdx_t card_index) {
for (int i = 0; i < num_valid_cards(); i++) {
if (card(i) == card_index) {
return found;
return SparsePRT::found;
}
}
if (num_valid_cards() < cards_num() - 1) {
_cards[_next_null] = (card_elem_t)card_index;
_next_null++;
return added;
return SparsePRT::added;
}
// Otherwise, we're full.
return overflow;
return SparsePRT::overflow;
}
void SparsePRTEntry::copy_cards(card_elem_t* cards) const {
@ -91,7 +91,6 @@ RSHashTable::RSHashTable(size_t capacity) :
_capacity(capacity),
_capacity_mask(capacity-1),
_occupied_entries(0),
_occupied_cards(0),
_entries(NULL),
_buckets(NEW_C_HEAP_ARRAY(int, capacity, mtGC)),
_free_region(0),
@ -109,7 +108,6 @@ RSHashTable::~RSHashTable() {
void RSHashTable::clear() {
_occupied_entries = 0;
_occupied_cards = 0;
guarantee(_entries != NULL, "INV");
guarantee(_buckets != NULL, "INV");
@ -123,14 +121,13 @@ void RSHashTable::clear() {
_free_region = 0;
}
bool RSHashTable::add_card(RegionIdx_t region_ind, CardIdx_t card_index) {
SparsePRT::AddCardResult RSHashTable::add_card(RegionIdx_t region_ind, CardIdx_t card_index) {
SparsePRTEntry* e = entry_for_region_ind_create(region_ind);
assert(e != NULL && e->r_ind() == region_ind,
"Postcondition of call above.");
SparsePRTEntry::AddCardResult res = e->add_card(card_index);
if (res == SparsePRTEntry::added) _occupied_cards++;
SparsePRT::AddCardResult res = e->add_card(card_index);
assert(e->num_valid_cards() > 0, "Postcondition");
return res != SparsePRTEntry::overflow;
return res;
}
SparsePRTEntry* RSHashTable::get_entry(RegionIdx_t region_ind) const {
@ -163,7 +160,6 @@ bool RSHashTable::delete_entry(RegionIdx_t region_ind) {
if (cur_ind == NullEntry) return false;
// Otherwise, splice out "cur".
*prev_loc = cur->next_index();
_occupied_cards -= cur->num_valid_cards();
free_entry(cur_ind);
_occupied_entries--;
return true;
@ -209,7 +205,6 @@ void RSHashTable::add_entry(SparsePRTEntry* e) {
assert(e->num_valid_cards() > 0, "Precondition.");
SparsePRTEntry* e2 = entry_for_region_ind_create(e->r_ind());
e->copy_cards(e2);
_occupied_cards += e2->num_valid_cards();
assert(e2->num_valid_cards() > 0, "Postcondition.");
}
@ -309,7 +304,7 @@ size_t SparsePRT::mem_size() const {
return sizeof(SparsePRT) + _table->mem_size();
}
bool SparsePRT::add_card(RegionIdx_t region_id, CardIdx_t card_index) {
SparsePRT::AddCardResult SparsePRT::add_card(RegionIdx_t region_id, CardIdx_t card_index) {
if (_table->should_expand()) {
expand();
}

View File

@ -33,9 +33,56 @@
#include "utilities/align.hpp"
#include "utilities/globalDefinitions.hpp"
class RSHashTable;
class SparsePRTEntry;
class SparsePRTIter;
// Sparse remembered set for a heap region (the "owning" region). Maps
// indices of other regions to short sequences of cards in the other region
// that might contain pointers into the owner region.
// Concurrent access to a SparsePRT must be serialized by some external mutex.
class SparsePRT {
friend class SparsePRTIter;
friend class SparsePRTBucketIter;
RSHashTable* _table;
static const size_t InitialCapacity = 8;
void expand();
public:
SparsePRT();
~SparsePRT();
size_t mem_size() const;
enum AddCardResult {
overflow, // The table is full, could not add the card to the table.
found, // The card is already in the PRT.
added // The card has been added.
};
// Attempts to ensure that the given card_index in the given region is in
// the sparse table. If successful (because the card was already
// present, or because it was successfully added) returns "true".
// Otherwise, returns "false" to indicate that the addition would
// overflow the entry for the region. The caller must transfer these
// entries to a larger-capacity representation.
AddCardResult add_card(RegionIdx_t region_id, CardIdx_t card_index);
// Return the pointer to the entry associated with the given region.
SparsePRTEntry* get_entry(RegionIdx_t region_ind);
// If there is an entry for "region_ind", removes it and return "true";
// otherwise returns "false."
bool delete_entry(RegionIdx_t region_ind);
// Clear the table, and reinitialize to initial capacity.
void clear();
bool contains_card(RegionIdx_t region_id, CardIdx_t card_index) const;
};
class SparsePRTEntry: public CHeapObj<mtGC> {
public:
@ -84,15 +131,7 @@ public:
// Returns the number of non-NULL card entries.
inline int num_valid_cards() const { return _next_null; }
// Requires that the entry not contain the given card index. If there is
// space available, add the given card index to the entry and return
// "true"; otherwise, return "false" to indicate that the entry is full.
enum AddCardResult {
overflow,
found,
added
};
inline AddCardResult add_card(CardIdx_t card_index);
inline SparsePRT::AddCardResult add_card(CardIdx_t card_index);
// Copy the current entry's cards into the "_card" array of "e."
inline void copy_cards(SparsePRTEntry* e) const;
@ -153,7 +192,7 @@ public:
// Otherwise, returns "false" to indicate that the addition would
// overflow the entry for the region. The caller must transfer these
// entries to a larger-capacity representation.
bool add_card(RegionIdx_t region_id, CardIdx_t card_index);
SparsePRT::AddCardResult add_card(RegionIdx_t region_id, CardIdx_t card_index);
bool delete_entry(RegionIdx_t region_id);
@ -168,7 +207,6 @@ public:
size_t capacity() const { return _capacity; }
size_t capacity_mask() const { return _capacity_mask; }
size_t occupied_entries() const { return _occupied_entries; }
size_t occupied_cards() const { return _occupied_cards; }
size_t mem_size() const;
// The number of SparsePRTEntry instances available.
size_t num_entries() const { return _num_entries; }
@ -228,50 +266,6 @@ public:
bool has_next(SparsePRTEntry*& entry);
};
// Concurrent access to a SparsePRT must be serialized by some external mutex.
class SparsePRTIter;
class SparsePRT {
friend class SparsePRTIter;
friend class SparsePRTBucketIter;
RSHashTable* _table;
static const size_t InitialCapacity = 8;
void expand();
public:
SparsePRT();
~SparsePRT();
size_t occupied() const { return _table->occupied_cards(); }
size_t mem_size() const;
// Attempts to ensure that the given card_index in the given region is in
// the sparse table. If successful (because the card was already
// present, or because it was successfully added) returns "true".
// Otherwise, returns "false" to indicate that the addition would
// overflow the entry for the region. The caller must transfer these
// entries to a larger-capacity representation.
bool add_card(RegionIdx_t region_id, CardIdx_t card_index);
// Return the pointer to the entry associated with the given region.
SparsePRTEntry* get_entry(RegionIdx_t region_ind);
// If there is an entry for "region_ind", removes it and return "true";
// otherwise returns "false."
bool delete_entry(RegionIdx_t region_ind);
// Clear the table, and reinitialize to initial capacity.
void clear();
bool contains_card(RegionIdx_t region_id, CardIdx_t card_index) const {
return _table->contains_card(region_id, card_index);
}
};
class SparsePRTIter: public RSHashTableIter {
public:
SparsePRTIter(const SparsePRT* sprt) :

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019, 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.
*
*/
#ifndef SHARE_GC_G1_SPARSEPRT_INLINE_HPP
#define SHARE_GC_G1_SPARSEPRT_INLINE_HPP
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/g1/sparsePRT.hpp"
inline bool SparsePRT::contains_card(RegionIdx_t region_id, CardIdx_t card_index) const {
return _table->contains_card(region_id, card_index);
}
#endif // SHARE_GC_G1_SPARSEPRT_INLINE_HPP

View File

@ -1,91 +0,0 @@
/*
* Copyright (c) 2001, 2019, 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.
*
*/
#ifndef SHARE_GC_G1_SURVRATEGROUP_HPP
#define SHARE_GC_G1_SURVRATEGROUP_HPP
#include "utilities/numberSeq.hpp"
class G1Predictions;
class SurvRateGroup : public CHeapObj<mtGC> {
private:
size_t _stats_arrays_length;
double* _accum_surv_rate_pred;
double _last_pred;
TruncatedSeq** _surv_rate_pred;
int _all_regions_allocated;
size_t _region_num;
size_t _setup_seq_num;
void fill_in_last_surv_rates();
void finalize_predictions(const G1Predictions& predictor);
public:
SurvRateGroup();
void reset();
void start_adding_regions();
void stop_adding_regions();
void record_surviving_words(int age_in_group, size_t surv_words);
void all_surviving_words_recorded(const G1Predictions& predictor, bool update_predictors);
size_t region_num() const { return _region_num; }
double accum_surv_rate_pred(int age) const {
assert(age >= 0, "must be");
if ((size_t)age < _stats_arrays_length)
return _accum_surv_rate_pred[age];
else {
double diff = (double) (age - _stats_arrays_length + 1);
return _accum_surv_rate_pred[_stats_arrays_length-1] + diff * _last_pred;
}
}
TruncatedSeq* get_seq(size_t age) const {
if (age >= _setup_seq_num) {
guarantee( _setup_seq_num > 0, "invariant" );
age = _setup_seq_num-1;
}
TruncatedSeq* seq = _surv_rate_pred[age];
guarantee( seq != NULL, "invariant" );
return seq;
}
int next_age_index() {
++_region_num;
return (int) ++_all_regions_allocated;
}
int age_in_group(int age_index) const {
int ret = (int) (_all_regions_allocated - age_index);
assert( ret >= 0, "invariant" );
return ret;
}
void finished_recalculating_age_indexes() {
_all_regions_allocated = 0;
}
};
#endif // SHARE_GC_G1_SURVRATEGROUP_HPP

View File

@ -1980,7 +1980,7 @@ jmethodID InstanceKlass::get_jmethod_id(const methodHandle& method_h) {
// we're single threaded or at a safepoint - no locking needed
get_jmethod_id_length_value(jmeths, idnum, &length, &id);
} else {
MutexLocker ml(JmethodIdCreation_lock);
MutexLocker ml(JmethodIdCreation_lock, Mutex::_no_safepoint_check_flag);
get_jmethod_id_length_value(jmeths, idnum, &length, &id);
}
}
@ -2030,7 +2030,7 @@ jmethodID InstanceKlass::get_jmethod_id(const methodHandle& method_h) {
id = get_jmethod_id_fetch_or_update(idnum, new_id, new_jmeths,
&to_dealloc_id, &to_dealloc_jmeths);
} else {
MutexLocker ml(JmethodIdCreation_lock);
MutexLocker ml(JmethodIdCreation_lock, Mutex::_no_safepoint_check_flag);
id = get_jmethod_id_fetch_or_update(idnum, new_id, new_jmeths,
&to_dealloc_id, &to_dealloc_jmeths);
}

View File

@ -1349,20 +1349,27 @@ void JvmtiExport::post_class_unload(Klass* klass) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
Thread *thread = Thread::current();
// postings to the service thread so that it can perform them in a safe
// context and in-order.
MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
ResourceMark rm;
// JvmtiDeferredEvent copies the string.
JvmtiDeferredEvent event = JvmtiDeferredEvent::class_unload_event(klass->name()->as_C_string());
JvmtiDeferredEventQueue::enqueue(event);
}
void JvmtiExport::post_class_unload_internal(const char* name) {
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}
assert(Thread::current()->is_service_thread(), "must be called from ServiceThread");
JavaThread *thread = JavaThread::current();
HandleMark hm(thread);
EVT_TRIG_TRACE(EXT_EVENT_CLASS_UNLOAD, ("[?] Trg Class Unload triggered" ));
if (JvmtiEventController::is_enabled((jvmtiEvent)EXT_EVENT_CLASS_UNLOAD)) {
assert(thread->is_VM_thread(), "wrong thread");
// get JavaThread for whom we are proxy
Thread *calling_thread = ((VMThread *)thread)->vm_operation()->calling_thread();
if (!calling_thread->is_Java_thread()) {
// cannot post an event to a non-JavaThread
return;
}
JavaThread *real_thread = (JavaThread *)calling_thread;
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
@ -1370,33 +1377,14 @@ void JvmtiExport::post_class_unload(Klass* klass) {
continue;
}
if (env->is_enabled((jvmtiEvent)EXT_EVENT_CLASS_UNLOAD)) {
EVT_TRACE(EXT_EVENT_CLASS_UNLOAD, ("[?] Evt Class Unload sent %s",
klass==NULL? "NULL" : klass->external_name() ));
// do everything manually, since this is a proxy - needs special care
JNIEnv* jni_env = real_thread->jni_environment();
jthread jt = (jthread)JNIHandles::make_local(real_thread, real_thread->threadObj());
jclass jk = (jclass)JNIHandles::make_local(real_thread, klass->java_mirror());
// Before we call the JVMTI agent, we have to set the state in the
// thread for which we are proxying.
JavaThreadState prev_state = real_thread->thread_state();
assert(((Thread *)real_thread)->is_ConcurrentGC_thread() ||
(real_thread->is_Java_thread() && prev_state == _thread_blocked),
"should be ConcurrentGCThread or JavaThread at safepoint");
real_thread->set_thread_state(_thread_in_native);
EVT_TRACE(EXT_EVENT_CLASS_UNLOAD, ("[?] Evt Class Unload sent %s", name));
JvmtiEventMark jem(thread);
JvmtiJavaThreadEventTransition jet(thread);
jvmtiExtensionEvent callback = env->ext_callbacks()->ClassUnload;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jni_env, jt, jk);
(*callback)(env->jvmti_external(), jem.jni_env(), name);
}
assert(real_thread->thread_state() == _thread_in_native,
"JavaThread should be in native");
real_thread->set_thread_state(prev_state);
JNIHandles::destroy_local(jk);
JNIHandles::destroy_local(jt);
}
}
}
@ -2155,7 +2143,7 @@ jvmtiCompiledMethodLoadInlineRecord* create_inline_record(nmethod* nm) {
int stackframe = 0;
for(ScopeDesc* sd = nm->scope_desc_at(p->real_pc(nm));sd != NULL;sd = sd->sender()) {
// sd->method() can be NULL for stubs but not for nmethods. To be completely robust, include an assert that we should never see a null sd->method()
assert(sd->method() != NULL, "sd->method() cannot be null.");
guarantee(sd->method() != NULL, "sd->method() cannot be null.");
record->pcinfo[scope].methods[stackframe] = sd->method()->jmethod_id();
record->pcinfo[scope].bcis[stackframe] = sd->bci();
stackframe++;
@ -2166,6 +2154,7 @@ jvmtiCompiledMethodLoadInlineRecord* create_inline_record(nmethod* nm) {
}
void JvmtiExport::post_compiled_method_load(nmethod *nm) {
guarantee(!nm->is_unloading(), "nmethod isn't unloaded or unloading");
if (JvmtiEnv::get_phase() < JVMTI_PHASE_PRIMORDIAL) {
return;
}

View File

@ -160,6 +160,7 @@ class JvmtiExport : public AllStatic {
// internal implementation. Also called from JvmtiDeferredEvent::post()
static void post_dynamic_code_generated_internal(const char *name, const void *code_begin, const void *code_end) NOT_JVMTI_RETURN;
static void post_class_unload_internal(const char *name) NOT_JVMTI_RETURN;
private:
// GenerateEvents support to allow posting of CompiledMethodLoad and

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2019, 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
@ -70,9 +70,8 @@ void JvmtiExtensions::register_extensions() {
// register our extension event
static jvmtiParamInfo event_params[] = {
{ (char*)"JNI Environment", JVMTI_KIND_IN, JVMTI_TYPE_JNIENV, JNI_FALSE },
{ (char*)"Thread", JVMTI_KIND_IN, JVMTI_TYPE_JTHREAD, JNI_FALSE },
{ (char*)"Class", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, JNI_FALSE }
{ (char*)"JNI Environment", JVMTI_KIND_IN_PTR, JVMTI_TYPE_JNIENV, JNI_FALSE },
{ (char*)"Class", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CCHAR, JNI_FALSE }
};
static jvmtiExtensionEventInfo ext_event = {
EXT_EVENT_CLASS_UNLOAD,

View File

@ -908,9 +908,6 @@ JvmtiDeferredEvent JvmtiDeferredEvent::compiled_method_load_event(
nmethod* nm) {
JvmtiDeferredEvent event = JvmtiDeferredEvent(TYPE_COMPILED_METHOD_LOAD);
event._event_data.compiled_method_load = nm;
// Keep the nmethod alive until the ServiceThread can process
// this deferred event.
nmethodLocker::lock_nmethod(nm);
return event;
}
@ -942,15 +939,23 @@ JvmtiDeferredEvent JvmtiDeferredEvent::dynamic_code_generated_event(
return event;
}
JvmtiDeferredEvent JvmtiDeferredEvent::class_unload_event(const char* name) {
JvmtiDeferredEvent event = JvmtiDeferredEvent(TYPE_CLASS_UNLOAD);
// Need to make a copy of the name since we don't know how long
// the event poster will keep it around after we enqueue the
// deferred event and return. strdup() failure is handled in
// the post() routine below.
event._event_data.class_unload.name = os::strdup(name);
return event;
}
void JvmtiDeferredEvent::post() {
assert(ServiceThread::is_service_thread(Thread::current()),
assert(Thread::current()->is_service_thread(),
"Service thread must post enqueued events");
switch(_type) {
case TYPE_COMPILED_METHOD_LOAD: {
nmethod* nm = _event_data.compiled_method_load;
JvmtiExport::post_compiled_method_load(nm);
// done with the deferred event so unlock the nmethod
nmethodLocker::unlock_nmethod(nm);
break;
}
case TYPE_COMPILED_METHOD_UNLOAD: {
@ -975,11 +980,37 @@ void JvmtiDeferredEvent::post() {
}
break;
}
case TYPE_CLASS_UNLOAD: {
JvmtiExport::post_class_unload_internal(
// if strdup failed give the event a default name
(_event_data.class_unload.name == NULL)
? "unknown_class" : _event_data.class_unload.name);
if (_event_data.class_unload.name != NULL) {
// release our copy
os::free((void *)_event_data.class_unload.name);
}
break;
}
default:
ShouldNotReachHere();
}
}
// Keep the nmethod for compiled_method_load from being unloaded.
void JvmtiDeferredEvent::oops_do(OopClosure* f, CodeBlobClosure* cf) {
if (cf != NULL && _type == TYPE_COMPILED_METHOD_LOAD) {
cf->do_code_blob(_event_data.compiled_method_load);
}
}
// The sweeper calls this and marks the nmethods here on the stack so that
// they cannot be turned into zombies while in the queue.
void JvmtiDeferredEvent::nmethods_do(CodeBlobClosure* cf) {
if (cf != NULL && _type == TYPE_COMPILED_METHOD_LOAD) {
cf->do_code_blob(_event_data.compiled_method_load);
} // May add UNLOAD event but it doesn't work yet.
}
JvmtiDeferredEventQueue::QueueNode* JvmtiDeferredEventQueue::_queue_tail = NULL;
JvmtiDeferredEventQueue::QueueNode* JvmtiDeferredEventQueue::_queue_head = NULL;
@ -1029,3 +1060,15 @@ JvmtiDeferredEvent JvmtiDeferredEventQueue::dequeue() {
delete node;
return event;
}
void JvmtiDeferredEventQueue::oops_do(OopClosure* f, CodeBlobClosure* cf) {
for(QueueNode* node = _queue_head; node != NULL; node = node->next()) {
node->event().oops_do(f, cf);
}
}
void JvmtiDeferredEventQueue::nmethods_do(CodeBlobClosure* cf) {
for(QueueNode* node = _queue_head; node != NULL; node = node->next()) {
node->event().nmethods_do(cf);
}
}

View File

@ -440,7 +440,8 @@ class JvmtiDeferredEvent {
TYPE_NONE,
TYPE_COMPILED_METHOD_LOAD,
TYPE_COMPILED_METHOD_UNLOAD,
TYPE_DYNAMIC_CODE_GENERATED
TYPE_DYNAMIC_CODE_GENERATED,
TYPE_CLASS_UNLOAD
} Type;
Type _type;
@ -456,6 +457,9 @@ class JvmtiDeferredEvent {
const void* code_begin;
const void* code_end;
} dynamic_code_generated;
struct {
const char* name;
} class_unload;
} _event_data;
JvmtiDeferredEvent(Type t) : _type(t) {}
@ -472,9 +476,15 @@ class JvmtiDeferredEvent {
static JvmtiDeferredEvent dynamic_code_generated_event(
const char* name, const void* begin, const void* end)
NOT_JVMTI_RETURN_(JvmtiDeferredEvent());
static JvmtiDeferredEvent class_unload_event(
const char* name) NOT_JVMTI_RETURN_(JvmtiDeferredEvent());
// Actually posts the event.
void post() NOT_JVMTI_RETURN;
// Sweeper support to keep nmethods from being zombied while in the queue.
void nmethods_do(CodeBlobClosure* cf) NOT_JVMTI_RETURN;
// GC support to keep nmethod from being unloaded while in the queue.
void oops_do(OopClosure* f, CodeBlobClosure* cf) NOT_JVMTI_RETURN;
};
/**
@ -494,7 +504,7 @@ class JvmtiDeferredEventQueue : AllStatic {
QueueNode(const JvmtiDeferredEvent& event)
: _event(event), _next(NULL) {}
const JvmtiDeferredEvent& event() const { return _event; }
JvmtiDeferredEvent& event() { return _event; }
QueueNode* next() const { return _next; }
void set_next(QueueNode* next) { _next = next; }
@ -508,6 +518,10 @@ class JvmtiDeferredEventQueue : AllStatic {
static bool has_events() NOT_JVMTI_RETURN_(false);
static void enqueue(const JvmtiDeferredEvent& event) NOT_JVMTI_RETURN;
static JvmtiDeferredEvent dequeue() NOT_JVMTI_RETURN_(JvmtiDeferredEvent());
// Sweeper support to keep nmethods from being zombied while in the queue.
static void nmethods_do(CodeBlobClosure* cf) NOT_JVMTI_RETURN;
// GC support to keep nmethod from being unloaded while in the queue.
static void oops_do(OopClosure* f, CodeBlobClosure* cf) NOT_JVMTI_RETURN;
};
// Utility macro that checks for NULL pointers:

View File

@ -244,7 +244,7 @@ void mutex_init() {
Notification_lock = Service_lock;
}
def(JmethodIdCreation_lock , PaddedMutex , leaf, true, _safepoint_check_always); // used for creating jmethodIDs.
def(JmethodIdCreation_lock , PaddedMutex , leaf, true, _safepoint_check_never); // used for creating jmethodIDs.
def(SystemDictionary_lock , PaddedMonitor, leaf, true, _safepoint_check_always);
def(ProtectionDomainSet_lock , PaddedMutex , leaf-1, true, _safepoint_check_never);

View File

@ -46,6 +46,7 @@
#include "services/threadIdTable.hpp"
ServiceThread* ServiceThread::_instance = NULL;
JvmtiDeferredEvent* ServiceThread::_jvmti_event = NULL;
void ServiceThread::initialize() {
EXCEPTION_MARK;
@ -138,7 +139,9 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
}
if (has_jvmti_events) {
// Get the event under the Service_lock
jvmti_event = JvmtiDeferredEventQueue::dequeue();
_jvmti_event = &jvmti_event;
}
}
@ -151,7 +154,8 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
}
if (has_jvmti_events) {
jvmti_event.post();
_jvmti_event->post();
_jvmti_event = NULL; // reset
}
if (!UseNotificationThread) {
@ -186,6 +190,26 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
}
}
bool ServiceThread::is_service_thread(Thread* thread) {
return thread == _instance;
void ServiceThread::oops_do(OopClosure* f, CodeBlobClosure* cf) {
JavaThread::oops_do(f, cf);
// The ServiceThread "owns" the JVMTI Deferred events, scan them here
// to keep them alive until they are processed.
if (cf != NULL) {
if (_jvmti_event != NULL) {
_jvmti_event->oops_do(f, cf);
}
MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
JvmtiDeferredEventQueue::oops_do(f, cf);
}
}
void ServiceThread::nmethods_do(CodeBlobClosure* cf) {
JavaThread::nmethods_do(cf);
if (cf != NULL) {
if (_jvmti_event != NULL) {
_jvmti_event->nmethods_do(cf);
}
MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
JvmtiDeferredEventQueue::nmethods_do(cf);
}
}

View File

@ -30,12 +30,13 @@
// A hidden from external view JavaThread for JVMTI compiled-method-load
// events, oop storage cleanup, and the maintainance of string, symbol,
// protection domain, and resolved method tables.
class JvmtiDeferredEvent;
class ServiceThread : public JavaThread {
friend class VMStructs;
private:
static ServiceThread* _instance;
static JvmtiDeferredEvent* _jvmti_event;
static void service_thread_entry(JavaThread* thread, TRAPS);
ServiceThread(ThreadFunction entry_point) : JavaThread(entry_point) {};
@ -45,9 +46,11 @@ class ServiceThread : public JavaThread {
// Hide this thread from external view.
bool is_hidden_from_external_view() const { return true; }
bool is_service_thread() const { return true; }
// Returns true if the passed thread is the service thread.
static bool is_service_thread(Thread* thread);
// GC support
void oops_do(OopClosure* f, CodeBlobClosure* cf);
void nmethods_do(CodeBlobClosure* cf);
};
#endif // SHARE_RUNTIME_SERVICETHREAD_HPP

View File

@ -1649,7 +1649,7 @@ void JavaThread::initialize() {
set_deopt_compiled_method(NULL);
set_monitor_chunks(NULL);
_on_thread_list = false;
set_thread_state(_thread_new);
_thread_state = _thread_new;
_terminated = _not_terminated;
_array_for_gc = NULL;
_suspend_equivalent = false;

View File

@ -480,6 +480,7 @@ class Thread: public ThreadShadow {
virtual bool is_Java_thread() const { return false; }
virtual bool is_Compiler_thread() const { return false; }
virtual bool is_Code_cache_sweeper_thread() const { return false; }
virtual bool is_service_thread() const { return false; }
virtual bool is_hidden_from_external_view() const { return false; }
virtual bool is_jvmti_agent_thread() const { return false; }
// True iff the thread can perform GC operations at a safepoint.

View File

@ -125,6 +125,8 @@ inline JavaThreadState JavaThread::thread_state() const {
}
inline void JavaThread::set_thread_state(JavaThreadState s) {
assert(current_or_null() == NULL || current_or_null() == this,
"state change should only be called by the current thread");
#if defined(PPC64) || defined (AARCH64)
// Use membars when accessing volatile _thread_state. See
// Threads::create_vm() for size checks.

View File

@ -1,169 +0,0 @@
<!--
/*
* Copyright (c) 2012, 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of JSR-310 nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-->
<body>
<p>
A new Date and Time API for Java.
The design includes a relatively large number of classes and methods,
however each follows a common design language, especially in method prefixes.
Once the prefixes are understood, the API is relatively simple to comprehend.
</p>
<p>
The Java Time API is composed of several packages, each with a primary function:
</p>
<p>
{@link java.time} contains the main API based on the ISO-8601 standard.
The classes defined here represent the principal date-time concepts,
including instants, durations, dates, times, time-zones and periods.
They are based on the ISO calendar system, which is the <i>de facto</i> world
calendar following the proleptic Gregorian rules.
All the classes are immutable and thread-safe.
</p>
<p>
{@link java.time.temporal} contains the API for accessing the fields and units
of date-time. Units are measurable, such as years, months and hours.
For example, the expression "2 hours later" uses the hours unit.
By contrast, fields are mini-calculations, defining a value.
For example, month-of-year, day-of-week and hour-of-day are all fields.
The set of supported units and fields can be extended by applications if desired.
</p>
<p>
{@link java.time.format} contains the API to print and parse fields into date-time
objects and to customize parsing and printing.
Formatters can be created in a variety of ways, including constants, patterns,
localized styles and a builder.
Formatters are immutable and thread-safe.
</p>
<p>
{@link java.time.zone} contains the API to handle time-zones.
Detailed information is made available about the rules of each time-zone.
</p>
<p>
{@link java.time.chrono} contains the basic part of the calendar neutral API
and alternate calendar systems.
This is intended for use by applications that need to use localized calendars.
Support is provided for the Hijrah, Japanese, Minguo, and Thai Buddhist Calendars.
</p>
<h3>Design notes</h3>
<p>
Where possible, the API avoids the use of null.
All methods define whether they accept or return null in the Javadoc.
As a general rule, methods do not accept or return null.
A key exception is any method that takes an object and returns a boolean, for the purpose
of checking or validating, will generally return false for null.
</p>
<p>
The API is designed to be type-safe where reasonable in the main high-level API.
Thus, there are separate classes for the distinct concepts of date, time and date-time, plus variants
for offset and time-zones. The core 7 date-time classes, plus Instant, handle the needs of most applications.
Further classes handle other combinations - year, year-month and month-day in a type-safe manner.
</p>
<p>
In a language like Java, the use of many different types tends to cause API bloat.
This is handled here through the use of common method naming patterns throughout the API.
The common prefixes are 'of', 'get', 'is', 'with', 'plus', 'minus', 'to' and 'at'.
See {@link java.time.LocalDate} for an example of each of these methods.
</p>
<p>
Following type-safety to its logical conclusion would result in more classes, especially for time -
hour-minute, hour-minute-second and hour-minute-second-nanosecond.
While logically pure, this was not possible in practice, as the additional classes would have
excessively complicated the API. Notably, there would be additional combinations at the offset
and date-time levels, such as offset-date-hour-minute.
To avoid this explosion of types, {@link java.time.LocalTime} is used for all precisions of time.
By contrast, some additional classes were used for dates, such as {@link java.time.YearMonth}.
This proved necessary, as the API for year-month is significantly different to that for a date, whereas
an absence of nanoseconds in a time can be approximated by returning zero.
</p>
<p>
Similarly, full type-safety might argue for a separate class for each field in date-time,
such as a class for HourOfDay and another for DayOfMonth.
This approach was tried, but was excessively complicated in the Java language, lacking usability.
A similar problem occurs with periods.
There is a case for a separate class for each period unit, such as a type for Years and a type for Minutes.
However, this yields a lot of classes and a problem of type conversion.
As such, general access to fields and units is not wrapped in a class.
</p>
<p>
Multiple calendar systems is an awkward addition to the design challenges.
The first principal is that most users want the standard ISO calendar system.
As such, the main classes are ISO-only. The second principal is that most of those that want a
non-ISO calendar system want it for user interaction, thus it is a UI localization issue.
As such, date and time objects should be held as ISO objects in the data model and persistent
storage, only being converted to and from a local calendar for display.
The calendar system would be stored separately in the user preferences.
</p>
<p>
There are, however, some limited use cases where users believe they need to store and use
dates in arbitrary calendar systems throughout the application.
This is supported by {@link java.time.chrono.ChronoLocalDate}, however it is vital to read
all the associated warnings in the Javadoc of that interface before using it.
In summary, applications that require general interoperation between multiple calendar systems
typically need to be written in a very different way to those only using the ISO calendar,
thus most applications should just use ISO and avoid {@code ChronoLocalDate}.
</p>
<p>
Throughout all of this, a key goal was to allow date-time fields and units to be defined by applications.
This has been achieved having tried many different designs.
</p>
</body>

View File

@ -254,10 +254,10 @@ enum NamedGroup {
AlgorithmParameters algParams = null;
boolean mediator = (keAlgParamSpec != null);
// HACK CODE
//
// An EC provider, for example the SunEC provider, may support
// AlgorithmParameters but not KeyPairGenerator or KeyAgreement.
//
// Note: Please be careful if removing this block!
if (mediator && (namedGroupSpec == NamedGroupSpec.NAMED_GROUP_ECDHE)) {
mediator = JsseJce.isEcAvailable();
}
@ -277,10 +277,10 @@ enum NamedGroup {
"No AlgorithmParameters for " + name, exp);
}
} else {
// HACK CODE
//
// Please remove the following code if the XDH/X25519/X448
// AlgorithmParameters algorithms are supported in JDK.
//
// Note: Please be careful if removing this block!
algParams = null;
try {
KeyAgreement.getInstance(name);

View File

@ -381,7 +381,7 @@ final class SSLEngineInputRecord extends InputRecord implements SSLRecord {
"Requested to negotiate unsupported SSLv2!");
}
// hack code, the exception is caught in SSLEngineImpl
// Note that the exception is caught in SSLEngineImpl
// so that SSLv2 error message can be delivered properly.
throw new UnsupportedOperationException( // SSLv2Hello
"Unsupported SSL v2.0 ClientHello");

View File

@ -274,10 +274,10 @@ enum SignatureScheme {
Arrays.asList(handshakeSupportedProtocols);
boolean mediator = true;
// HACK CODE
//
// An EC provider, for example the SunEC provider, may support
// AlgorithmParameters but not KeyPairGenerator or Signature.
//
// Note: Please be careful if removing this block!
if ("EC".equals(keyAlgorithm)) {
mediator = JsseJce.isEcAvailable();
}

View File

@ -217,9 +217,9 @@ public class KeyStoreDelegator extends KeyStoreSpi {
try {
@SuppressWarnings("deprecation")
KeyStoreSpi tmp = primaryKeyStore.newInstance();
tmp.engineLoad(bufferedStream, password);
keystore = tmp;
type = primaryType;
keystore.engineLoad(bufferedStream, password);
} catch (Exception e) {
@ -236,11 +236,11 @@ public class KeyStoreDelegator extends KeyStoreSpi {
}
@SuppressWarnings("deprecation")
KeyStoreSpi tmp= secondaryKeyStore.newInstance();
KeyStoreSpi tmp = secondaryKeyStore.newInstance();
bufferedStream.reset();
tmp.engineLoad(bufferedStream, password);
keystore = tmp;
type = secondaryType;
bufferedStream.reset();
keystore.engineLoad(bufferedStream, password);
if (debug != null) {
debug.println("WARNING: switching from " +

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2019, Oacle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2019, 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

View File

@ -1 +0,0 @@
com.sun.tools.javac.platform.JDKPlatformProvider

View File

@ -953,7 +953,7 @@ compiler.misc.not.def.access.does.not.read.from.unnamed=\
# {1} - current module
# 0: symbol, 1: symbol
compiler.misc.not.def.access.does.not.read.unnamed=\
package {0} is declared in the unnamed module, but module {0} does not read it
package {0} is declared in the unnamed module, but module {1} does not read it
# {0} - package in which the invisible class is declared
# {1} - module in which {0} is declared

View File

@ -54,6 +54,12 @@ public class HelpWriter extends HtmlDocletWriter {
private final Navigation navBar;
private final String[][] SEARCH_EXAMPLES = {
{"j.l.obj", "\"java.lang.Object\""},
{"InpStr", "\"java.io.InputStream\""},
{"HM.cK", "\"java.util.HashMap.containsKey(Object)\""}
};
/**
* Constructor to construct HelpWriter object.
* @param configuration the configuration
@ -326,9 +332,20 @@ public class HelpWriter extends HtmlDocletWriter {
Content searchHead = HtmlTree.HEADING(Headings.CONTENT_HEADING,
contents.getContent("doclet.help.search.head"));
htmlTree = HtmlTree.SECTION(HtmlStyle.helpSection, searchHead);
Content searchBody = contents.getContent("doclet.help.search.body");
Content searchPara = HtmlTree.P(searchBody);
htmlTree.add(searchPara);
Content searchIntro = HtmlTree.P(contents.getContent("doclet.help.search.intro"));
Content searchExamples = new HtmlTree(HtmlTag.UL);
for (String[] example : SEARCH_EXAMPLES) {
searchExamples.add(HtmlTree.LI(
contents.getContent("doclet.help.search.example",
HtmlTree.CODE(new StringContent(example[0])), example[1])));
}
Content searchSpecLink = HtmlTree.A(
resources.getText("doclet.help.search.spec.url", Runtime.version().feature()),
contents.getContent("doclet.help.search.spec.title"));
Content searchRefer = HtmlTree.P(contents.getContent("doclet.help.search.refer", searchSpecLink));
htmlTree.add(searchIntro);
htmlTree.add(searchExamples);
htmlTree.add(searchRefer);
ul.add(HtmlTree.LI(HtmlStyle.blockList, htmlTree));
Content divContent = HtmlTree.DIV(HtmlStyle.contentContainer, ul);

View File

@ -215,9 +215,18 @@ doclet.help.annotation_type.declaration=\
doclet.help.annotation_type.description=\
Annotation Type Description
doclet.help.search.head=Search
doclet.help.search.body=You can search for definitions of modules, packages, types, fields, methods \
and other terms defined in the API, using some or all of the name. "Camel-case" abbreviations \
are supported: for example, "InpStr" will find "InputStream" and "InputStreamReader".
# Introduction to Javadoc search features, followed by a list of examples
doclet.help.search.intro=You can search for definitions of modules, packages, types, fields, methods, \
system properties and other terms defined in the API, using some or all of the name, optionally \
using "camel-case" abbreviations. For example:
# Used to list search examples, {0} is a search term and {1} the matching result
doclet.help.search.example={0} will match {1}
# {0} contains a link to the current Javadoc Search Specification
doclet.help.search.refer=Refer to {0} for a full description of search features.
# The URL for the Javadoc Search Specification. {0} will be replaced by the JDK version number
doclet.help.search.spec.url=https://docs.oracle.com/en/java/javase/{0}/docs/specs/javadoc/javadoc-search-spec.html
# The title for the Javadoc Search Specification
doclet.help.search.spec.title=the Javadoc Search Specification
doclet.ClassUse_Packages.that.use.0=Packages that use {0}
doclet.ClassUse_Uses.of.0.in.1=Uses of {0} in {1}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2019, 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
@ -35,17 +35,17 @@ TEST_VM(G1Predictions, basic_predictions) {
G1Predictions predictor(0.0);
TruncatedSeq s;
double p0 = predictor.get_new_prediction(&s);
double p0 = predictor.predict(&s);
ASSERT_LT(p0, epsilon) << "Initial prediction of empty sequence must be 0.0";
s.add(5.0);
double p1 = predictor.get_new_prediction(&s);
double p1 = predictor.predict(&s);
ASSERT_NEAR(p1, 5.0, epsilon);
for (int i = 0; i < 40; i++) {
s.add(5.0);
}
double p2 = predictor.get_new_prediction(&s);
double p2 = predictor.predict(&s);
ASSERT_NEAR(p2, 5.0, epsilon);
}
@ -56,20 +56,20 @@ TEST_VM(G1Predictions, average_not_stdev_predictions) {
TruncatedSeq s;
s.add(1.0);
double p1 = predictor.get_new_prediction(&s);
double p1 = predictor.predict(&s);
ASSERT_GT(p1, s.davg()) << "First prediction must be greater than average";
s.add(1.0);
double p2 = predictor.get_new_prediction(&s);
double p2 = predictor.predict(&s);
ASSERT_GT(p1, p2) << "First prediction must be greater than second";
s.add(1.0);
double p3 = predictor.get_new_prediction(&s);
double p3 = predictor.predict(&s);
ASSERT_GT(p2, p3) << "Second prediction must be greater than third";
s.add(1.0);
s.add(1.0); // Five elements are now in the sequence.
double p4 = predictor.get_new_prediction(&s);
double p4 = predictor.predict(&s);
ASSERT_LT(p4, p3) << "Fourth prediction must be smaller than third";
ASSERT_NEAR(p4, 1.0, epsilon);
}
@ -82,20 +82,20 @@ TEST_VM(G1Predictions, average_stdev_predictions) {
TruncatedSeq s;
s.add(0.5);
double p1 = predictor.get_new_prediction(&s);
double p1 = predictor.predict(&s);
ASSERT_GT(p1, s.davg()) << "First prediction must be greater than average";
s.add(0.2);
double p2 = predictor.get_new_prediction(&s);
double p2 = predictor.predict(&s);
ASSERT_GT(p1, p2) << "First prediction must be greater than second";
s.add(0.5);
double p3 = predictor.get_new_prediction(&s);
double p3 = predictor.predict(&s);
ASSERT_GT(p2, p3) << "Second prediction must be greater than third";
s.add(0.2);
s.add(2.0);
double p4 = predictor.get_new_prediction(&s);
double p4 = predictor.predict(&s);
ASSERT_GT(p4, p3) << "Fourth prediction must be greater than third";
}
@ -104,24 +104,24 @@ TEST_VM(G1Predictions, unit_predictions) {
G1Predictions predictor(0.5);
TruncatedSeq s;
double p0 = predictor.get_new_unit_prediction(&s);
double p0 = predictor.predict_in_unit_interval(&s);
ASSERT_LT(p0, epsilon) << "Initial prediction of empty sequence must be 0.0";
s.add(100.0);
double p1 = predictor.get_new_unit_prediction(&s);
double p1 = predictor.predict_in_unit_interval(&s);
ASSERT_NEAR(p1, 1.0, epsilon);
// Feed the sequence additional positive values to test the high bound.
for (int i = 0; i < 3; i++) {
s.add(2.0);
}
ASSERT_NEAR(predictor.get_new_unit_prediction(&s), 1.0, epsilon);
ASSERT_NEAR(predictor.predict_in_unit_interval(&s), 1.0, epsilon);
// Feed the sequence additional large negative value to test the low bound.
for (int i = 0; i < 4; i++) {
s.add(-200.0);
}
ASSERT_NEAR(predictor.get_new_unit_prediction(&s), 0.0, epsilon);
ASSERT_NEAR(predictor.predict_in_unit_interval(&s), 0.0, epsilon);
}
// Some tests to verify bounding between [0 .. +inf]
@ -129,7 +129,7 @@ TEST_VM(G1Predictions, lower_bound_zero_predictions) {
G1Predictions predictor(0.5);
TruncatedSeq s;
double p0 = predictor.get_new_lower_zero_bound_prediction(&s);
double p0 = predictor.predict_zero_bounded(&s);
ASSERT_LT(p0, epsilon) << "Initial prediction of empty sequence must be 0.0";
s.add(100.0);
@ -138,11 +138,11 @@ TEST_VM(G1Predictions, lower_bound_zero_predictions) {
for (int i = 0; i < 3; i++) {
s.add(2.0);
}
ASSERT_GT(predictor.get_new_lower_zero_bound_prediction(&s), 1.0);
ASSERT_GT(predictor.predict_zero_bounded(&s), 1.0);
// Feed the sequence additional large negative value to test the low bound.
for (int i = 0; i < 4; i++) {
s.add(-200.0);
}
ASSERT_NEAR(predictor.get_new_lower_zero_bound_prediction(&s), 0.0, epsilon);
ASSERT_NEAR(predictor.predict_zero_bounded(&s), 0.0, epsilon);
}

View File

@ -178,7 +178,6 @@ vmTestbase/metaspace/gc/firstGC_default/TestDescription.java 8208250 generic-all
vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted003/TestDescription.java 6606767 generic-all
vmTestbase/nsk/jvmti/ResourceExhausted/resexhausted004/TestDescription.java 6606767 generic-all
vmTestbase/nsk/jvmti/scenarios/extension/EX03/ex03t001/TestDescription.java 8173658 generic-all
vmTestbase/nsk/jvmti/AttachOnDemand/attach045/TestDescription.java 8202971 generic-all
vmTestbase/nsk/jvmti/scenarios/jni_interception/JI05/ji05t001/TestDescription.java 8219652 aix-ppc64
vmTestbase/nsk/jvmti/scenarios/jni_interception/JI06/ji06t001/TestDescription.java 8219652 aix-ppc64

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2004, 2019, 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
@ -41,18 +41,15 @@ static jrawMonitorID eventMon;
/* ============================================================================= */
static void JNICALL
ClassUnload(jvmtiEnv* jvmti_env, JNIEnv *jni_env, jthread thread, jclass klass, ...) {
/*
* With the CMS GC the event can be posted on
* a ConcurrentGC thread that is not a JavaThread.
* In this case the thread argument can be NULL, so that,
* we should not expect the thread argument to be non-NULL.
*/
if (klass == NULL) {
ClassUnload(jvmtiEnv* jvmti_env, JNIEnv* jni_env, const char* name, ...) {
// The name argument should never be null
if (name == NULL) {
nsk_jvmti_setFailStatus();
NSK_COMPLAIN0("ClassUnload: 'klass' input parameter is NULL.\n");
NSK_COMPLAIN0("ClassUnload: 'name' input parameter is NULL.\n");
} else {
NSK_DISPLAY1("Class unloaded %s\n", name);
}
NSK_DISPLAY0("Received ClassUnload event.\n");
if (eventEnabled == JNI_TRUE) {
eventReceived1 = JNI_TRUE;
@ -107,6 +104,20 @@ jboolean isClassUnloadingEnabled() {
return enabled;
}
jboolean checkParams(jvmtiExtensionEventInfo event) {
// Check parameters are:
// JNIEnv *jni_env, const char* name
if (event.param_count != 2 ||
event.params[0].kind != JVMTI_KIND_IN_PTR ||
event.params[0].base_type != JVMTI_TYPE_JNIENV ||
event.params[1].kind != JVMTI_KIND_IN_PTR ||
event.params[1].base_type != JVMTI_TYPE_CCHAR) {
return JNI_FALSE;
} else {
return JNI_TRUE;
}
}
jboolean enableClassUnloadEvent (jboolean enable) {
jint extCount, i;
jvmtiExtensionEventInfo* extList;
@ -122,6 +133,14 @@ jboolean enableClassUnloadEvent (jboolean enable) {
if (strcmp(extList[i].id, (char*)"com.sun.hotspot.events.ClassUnload") == 0) {
found = JNI_TRUE;
NSK_DISPLAY1("%s", extList[i].short_description);
if (!checkParams(extList[i])) {
NSK_COMPLAIN0("ClassUnload event has wrong parameters.");
nsk_jvmti_setFailStatus();
return JNI_FALSE;
}
if (!NSK_JVMTI_VERIFY(
jvmti->SetExtensionEventCallback(extList[i].extension_event_index,
enable ? (jvmtiExtensionEvent)ClassUnload : NULL))) {

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2019, 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
* @bug 8234744
* @summary KeyStore.store can write wrong type of file
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
public class WrongStoreType {
public static void main(String ... args) throws Exception {
char[] password = "changeit".toCharArray();
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(null, null);
System.out.println(ks.getType());
Files.createFile(Path.of("emptyfile"));
try (InputStream in = new FileInputStream("emptyfile")) {
ks.load(in, password);
} catch (Exception e) {
System.out.println(e);
}
System.out.println(ks.getType());
try (OutputStream out = new FileOutputStream("newfile")) {
ks.store(out, password);
}
ks = KeyStore.getInstance(new File("newfile"), password);
String type = ks.getType();
if (!type.equalsIgnoreCase("PKCS12")) {
throw new Exception("see storetype " + type);
}
}
}