mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-22 12:51:01 +00:00
Merge
This commit is contained in:
commit
999f05b13b
@ -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\
|
||||
|
||||
@ -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() ||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 */);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
103
src/hotspot/share/gc/g1/g1SurvRateGroup.hpp
Normal file
103
src/hotspot/share/gc/g1/g1SurvRateGroup.hpp
Normal 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
|
||||
@ -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()),
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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) :
|
||||
|
||||
36
src/hotspot/share/gc/g1/sparsePRT.inline.hpp
Normal file
36
src/hotspot/share/gc/g1/sparsePRT.inline.hpp
Normal 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
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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>
|
||||
@ -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);
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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 " +
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1 +0,0 @@
|
||||
com.sun.tools.javac.platform.JDKPlatformProvider
|
||||
@ -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
|
||||
|
||||
@ -1 +0,0 @@
|
||||
com.sun.tools.javac.api.Tool
|
||||
@ -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);
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))) {
|
||||
|
||||
66
test/jdk/sun/security/provider/KeyStore/WrongStoreType.java
Normal file
66
test/jdk/sun/security/provider/KeyStore/WrongStoreType.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user