--- a/hotspot/src/os/linux/vm/os_linux.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/os/linux/vm/os_linux.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -2531,10 +2531,14 @@
}
return true;
}
- return false;
- }
-
- return commit_memory(addr, size, exec);
+ // Fall through and try to use small pages
+ }
+
+ if (commit_memory(addr, size, exec)) {
+ realign_memory(addr, size, alignment_hint);
+ return true;
+ }
+ return false;
}
void os::realign_memory(char *addr, size_t bytes, size_t alignment_hint) {
--- a/hotspot/src/os/windows/vm/os_windows.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/os/windows/vm/os_windows.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -2706,11 +2706,10 @@
static bool numa_interleaving_init() {
bool success = false;
- bool use_numa_specified = !FLAG_IS_DEFAULT(UseNUMA);
bool use_numa_interleaving_specified = !FLAG_IS_DEFAULT(UseNUMAInterleaving);
- // print a warning if UseNUMA or UseNUMAInterleaving flag is specified on command line
- bool warn_on_failure = use_numa_specified || use_numa_interleaving_specified;
+ // print a warning if UseNUMAInterleaving flag is specified on command line
+ bool warn_on_failure = use_numa_interleaving_specified;
# define WARN(msg) if (warn_on_failure) { warning(msg); }
// NUMAInterleaveGranularity cannot be less than vm_allocation_granularity (or _large_page_size if using large pages)
@@ -2720,7 +2719,7 @@
if (os::Kernel32Dll::NumaCallsAvailable()) {
if (numa_node_list_holder.build()) {
if (PrintMiscellaneous && Verbose) {
- tty->print("NUMA UsedNodeCount=%d, namely ", os::numa_get_groups_num());
+ tty->print("NUMA UsedNodeCount=%d, namely ", numa_node_list_holder.get_count());
for (int i = 0; i < numa_node_list_holder.get_count(); i++) {
tty->print("%d ", numa_node_list_holder.get_node_list_entry(i));
}
@@ -2734,7 +2733,6 @@
WARN("NUMA Interleaving is not supported by the operating system.");
}
if (!success) {
- if (use_numa_specified) WARN("...Ignoring UseNUMA flag.");
if (use_numa_interleaving_specified) WARN("...Ignoring UseNUMAInterleaving flag.");
}
return success;
@@ -2816,7 +2814,8 @@
prot);
} else {
// get the next node to use from the used_node_list
- DWORD node = numa_node_list_holder.get_node_list_entry(count % os::numa_get_groups_num());
+ assert(numa_node_list_holder.get_count() > 0, "Multiple NUMA nodes expected");
+ DWORD node = numa_node_list_holder.get_node_list_entry(count % numa_node_list_holder.get_count());
p_new = (char *)os::Kernel32Dll::VirtualAllocExNuma(hProc,
next_alloc_addr,
bytes_to_rq,
@@ -3132,15 +3131,21 @@
void os::numa_make_global(char *addr, size_t bytes) { }
void os::numa_make_local(char *addr, size_t bytes, int lgrp_hint) { }
bool os::numa_topology_changed() { return false; }
-size_t os::numa_get_groups_num() { return numa_node_list_holder.get_count(); }
+size_t os::numa_get_groups_num() { return MAX2(numa_node_list_holder.get_count(), 1); }
int os::numa_get_group_id() { return 0; }
size_t os::numa_get_leaf_groups(int *ids, size_t size) {
- // check for size bigger than actual groups_num
- size = MIN2(size, numa_get_groups_num());
- for (int i = 0; i < (int)size; i++) {
- ids[i] = numa_node_list_holder.get_node_list_entry(i);
- }
- return size;
+ if (numa_node_list_holder.get_count() == 0 && size > 0) {
+ // Provide an answer for UMA systems
+ ids[0] = 0;
+ return 1;
+ } else {
+ // check for size bigger than actual groups_num
+ size = MIN2(size, numa_get_groups_num());
+ for (int i = 0; i < (int)size; i++) {
+ ids[i] = numa_node_list_holder.get_node_list_entry(i);
+ }
+ return size;
+ }
}
bool os::get_page_info(char *start, page_info* info) {
@@ -3768,6 +3773,10 @@
// initialize thread priority policy
prio_init();
+ if (UseNUMA && !ForceNUMA) {
+ UseNUMA = false; // We don't fully support this yet
+ }
+
if (UseNUMAInterleaving) {
// first check whether this Windows OS supports VirtualAllocExNuma, if not ignore this flag
bool success = numa_interleaving_init();
--- a/hotspot/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/collectionSetChooser.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2011, 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
@@ -26,6 +26,7 @@
#include "gc_implementation/g1/collectionSetChooser.hpp"
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "gc_implementation/g1/g1CollectorPolicy.hpp"
+#include "gc_implementation/g1/g1ErgoVerbose.hpp"
#include "memory/space.inline.hpp"
CSetChooserCache::CSetChooserCache() {
@@ -358,6 +359,9 @@
if (_cache.is_empty()) {
assert(_curMarkedIndex == _numMarkedRegions,
"if cache is empty, list should also be empty");
+ ergo_verbose0(ErgoCSetConstruction,
+ "stop adding old regions to CSet",
+ ergo_format_reason("cache is empty"));
return NULL;
}
@@ -368,10 +372,23 @@
if (g1p->adaptive_young_list_length()) {
if (time_remaining - predicted_time < 0.0) {
g1h->check_if_region_is_too_expensive(predicted_time);
+ ergo_verbose2(ErgoCSetConstruction,
+ "stop adding old regions to CSet",
+ ergo_format_reason("predicted old region time higher than remaining time")
+ ergo_format_ms("predicted old region time")
+ ergo_format_ms("remaining time"),
+ predicted_time, time_remaining);
return NULL;
}
} else {
- if (predicted_time > 2.0 * avg_prediction) {
+ double threshold = 2.0 * avg_prediction;
+ if (predicted_time > threshold) {
+ ergo_verbose2(ErgoCSetConstruction,
+ "stop adding old regions to CSet",
+ ergo_format_reason("predicted old region time higher than threshold")
+ ergo_format_ms("predicted old region time")
+ ergo_format_ms("threshold"),
+ predicted_time, threshold);
return NULL;
}
}
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1RefineThread.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -91,7 +91,7 @@
}
}
- g1p->check_prediction_validity();
+ g1p->revise_young_list_target_length_if_necessary();
}
}
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -28,6 +28,7 @@
#include "gc_implementation/g1/concurrentMarkThread.inline.hpp"
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "gc_implementation/g1/g1CollectorPolicy.hpp"
+#include "gc_implementation/g1/g1ErgoVerbose.hpp"
#include "gc_implementation/g1/g1OopClosures.inline.hpp"
#include "gc_implementation/g1/g1RemSet.hpp"
#include "gc_implementation/g1/heapRegionRemSet.hpp"
@@ -1727,18 +1728,21 @@
size_t known_garbage_bytes =
g1_par_count_task.used_bytes() - g1_par_count_task.live_bytes();
-#if 0
- gclog_or_tty->print_cr("used %1.2lf, live %1.2lf, garbage %1.2lf",
- (double) g1_par_count_task.used_bytes() / (double) (1024 * 1024),
- (double) g1_par_count_task.live_bytes() / (double) (1024 * 1024),
- (double) known_garbage_bytes / (double) (1024 * 1024));
-#endif // 0
g1p->set_known_garbage_bytes(known_garbage_bytes);
size_t start_used_bytes = g1h->used();
_at_least_one_mark_complete = true;
g1h->set_marking_complete();
+ ergo_verbose4(ErgoConcCycles,
+ "finish cleanup",
+ ergo_format_byte("occupancy")
+ ergo_format_byte("capacity")
+ ergo_format_byte_perc("known garbage"),
+ start_used_bytes, g1h->capacity(),
+ known_garbage_bytes,
+ ((double) known_garbage_bytes / (double) g1h->capacity()) * 100.0);
+
double count_end = os::elapsedTime();
double this_final_counting_time = (count_end - start);
if (G1PrintParCleanupStats) {
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -31,6 +31,7 @@
#include "gc_implementation/g1/g1AllocRegion.inline.hpp"
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "gc_implementation/g1/g1CollectorPolicy.hpp"
+#include "gc_implementation/g1/g1ErgoVerbose.hpp"
#include "gc_implementation/g1/g1MarkSweep.hpp"
#include "gc_implementation/g1/g1OopClosures.inline.hpp"
#include "gc_implementation/g1/g1RemSet.inline.hpp"
@@ -577,6 +578,11 @@
res = new_region_try_secondary_free_list();
}
if (res == NULL && do_expand) {
+ ergo_verbose1(ErgoHeapSizing,
+ "attempt heap expansion",
+ ergo_format_reason("region allocation request failed")
+ ergo_format_byte("allocation request"),
+ word_size * HeapWordSize);
if (expand(word_size * HeapWordSize)) {
// Even though the heap was expanded, it might not have reached
// the desired size. So, we cannot assume that the allocation
@@ -790,6 +796,11 @@
// room available.
assert(num_regions > fs, "earlier allocation should have succeeded");
+ ergo_verbose1(ErgoHeapSizing,
+ "attempt heap expansion",
+ ergo_format_reason("humongous allocation request failed")
+ ergo_format_byte("allocation request"),
+ word_size * HeapWordSize);
if (expand((num_regions - fs) * HeapRegion::GrainBytes)) {
// Even though the heap was expanded, it might not have
// reached the desired size. So, we cannot assume that the
@@ -906,6 +917,8 @@
if (GC_locker::is_active_and_needs_gc()) {
if (g1_policy()->can_expand_young_list()) {
+ // No need for an ergo verbose message here,
+ // can_expand_young_list() does this when it returns true.
result = _mutator_alloc_region.attempt_allocation_force(word_size,
false /* bot_updates */);
if (result != NULL) {
@@ -1477,63 +1490,34 @@
// we'll try to make the capacity smaller than it, not greater).
maximum_desired_capacity = MAX2(maximum_desired_capacity, min_heap_size);
- if (PrintGC && Verbose) {
- const double free_percentage =
- (double) free_after_gc / (double) capacity_after_gc;
- gclog_or_tty->print_cr("Computing new size after full GC ");
- gclog_or_tty->print_cr(" "
- " minimum_free_percentage: %6.2f",
- minimum_free_percentage);
- gclog_or_tty->print_cr(" "
- " maximum_free_percentage: %6.2f",
- maximum_free_percentage);
- gclog_or_tty->print_cr(" "
- " capacity: %6.1fK"
- " minimum_desired_capacity: %6.1fK"
- " maximum_desired_capacity: %6.1fK",
- (double) capacity_after_gc / (double) K,
- (double) minimum_desired_capacity / (double) K,
- (double) maximum_desired_capacity / (double) K);
- gclog_or_tty->print_cr(" "
- " free_after_gc: %6.1fK"
- " used_after_gc: %6.1fK",
- (double) free_after_gc / (double) K,
- (double) used_after_gc / (double) K);
- gclog_or_tty->print_cr(" "
- " free_percentage: %6.2f",
- free_percentage);
- }
if (capacity_after_gc < minimum_desired_capacity) {
// Don't expand unless it's significant
size_t expand_bytes = minimum_desired_capacity - capacity_after_gc;
- if (expand(expand_bytes)) {
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr(" "
- " expanding:"
- " max_heap_size: %6.1fK"
- " minimum_desired_capacity: %6.1fK"
- " expand_bytes: %6.1fK",
- (double) max_heap_size / (double) K,
- (double) minimum_desired_capacity / (double) K,
- (double) expand_bytes / (double) K);
- }
- }
+ ergo_verbose4(ErgoHeapSizing,
+ "attempt heap expansion",
+ ergo_format_reason("capacity lower than "
+ "min desired capacity after Full GC")
+ ergo_format_byte("capacity")
+ ergo_format_byte("occupancy")
+ ergo_format_byte_perc("min desired capacity"),
+ capacity_after_gc, used_after_gc,
+ minimum_desired_capacity, (double) MinHeapFreeRatio);
+ expand(expand_bytes);
// No expansion, now see if we want to shrink
} else if (capacity_after_gc > maximum_desired_capacity) {
// Capacity too large, compute shrinking size
size_t shrink_bytes = capacity_after_gc - maximum_desired_capacity;
+ ergo_verbose4(ErgoHeapSizing,
+ "attempt heap shrinking",
+ ergo_format_reason("capacity higher than "
+ "max desired capacity after Full GC")
+ ergo_format_byte("capacity")
+ ergo_format_byte("occupancy")
+ ergo_format_byte_perc("max desired capacity"),
+ capacity_after_gc, used_after_gc,
+ maximum_desired_capacity, (double) MaxHeapFreeRatio);
shrink(shrink_bytes);
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr(" "
- " shrinking:"
- " min_heap_size: %6.1fK"
- " maximum_desired_capacity: %6.1fK"
- " shrink_bytes: %6.1fK",
- (double) min_heap_size / (double) K,
- (double) maximum_desired_capacity / (double) K,
- (double) shrink_bytes / (double) K);
- }
}
}
@@ -1619,6 +1603,11 @@
verify_region_sets_optional();
size_t expand_bytes = MAX2(word_size * HeapWordSize, MinHeapDeltaBytes);
+ ergo_verbose1(ErgoHeapSizing,
+ "attempt heap expansion",
+ ergo_format_reason("allocation request failed")
+ ergo_format_byte("allocation request"),
+ word_size * HeapWordSize);
if (expand(expand_bytes)) {
_hrs.verify_optional();
verify_region_sets_optional();
@@ -1646,11 +1635,11 @@
size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes);
aligned_expand_bytes = align_size_up(aligned_expand_bytes,
HeapRegion::GrainBytes);
-
- if (Verbose && PrintGC) {
- gclog_or_tty->print("Expanding garbage-first heap from %ldK by %ldK",
- old_mem_size/K, aligned_expand_bytes/K);
- }
+ ergo_verbose2(ErgoHeapSizing,
+ "expand the heap",
+ ergo_format_byte("requested expansion amount")
+ ergo_format_byte("attempted expansion amount"),
+ expand_bytes, aligned_expand_bytes);
// First commit the memory.
HeapWord* old_end = (HeapWord*) _g1_storage.high();
@@ -1693,7 +1682,11 @@
}
assert(curr == mr.end(), "post-condition");
}
+ g1_policy()->record_new_heap_size(n_regions());
} else {
+ ergo_verbose0(ErgoHeapSizing,
+ "did not expand the heap",
+ ergo_format_reason("heap expansion operation failed"));
// The expansion of the virtual storage space was unsuccessful.
// Let's see if it was because we ran out of swap.
if (G1ExitOnExpansionFailure &&
@@ -1702,13 +1695,6 @@
vm_exit_out_of_memory(aligned_expand_bytes, "G1 heap expansion");
}
}
-
- if (Verbose && PrintGC) {
- size_t new_mem_size = _g1_storage.committed_size();
- gclog_or_tty->print_cr("...%s, expanded to %ldK",
- (successful ? "Successful" : "Failed"),
- new_mem_size/K);
- }
return successful;
}
@@ -1722,6 +1708,13 @@
MemRegion mr = _hrs.shrink_by(aligned_shrink_bytes, &num_regions_deleted);
HeapWord* old_end = (HeapWord*) _g1_storage.high();
assert(mr.end() == old_end, "post-condition");
+
+ ergo_verbose3(ErgoHeapSizing,
+ "shrink the heap",
+ ergo_format_byte("requested shrinking amount")
+ ergo_format_byte("aligned shrinking amount")
+ ergo_format_byte("attempted shrinking amount"),
+ shrink_bytes, aligned_shrink_bytes, mr.byte_size());
if (mr.byte_size() > 0) {
if (_hr_printer.is_active()) {
HeapWord* curr = mr.end();
@@ -1740,13 +1733,11 @@
_expansion_regions += num_regions_deleted;
update_committed_space(old_end, new_end);
HeapRegionRemSet::shrink_heap(n_regions());
-
- if (Verbose && PrintGC) {
- size_t new_mem_size = _g1_storage.committed_size();
- gclog_or_tty->print_cr("Shrinking garbage-first heap from %ldK by %ldK to %ldK",
- old_mem_size/K, aligned_shrink_bytes/K,
- new_mem_size/K);
- }
+ g1_policy()->record_new_heap_size(n_regions());
+ } else {
+ ergo_verbose0(ErgoHeapSizing,
+ "did not shrink the heap",
+ ergo_format_reason("heap shrinking operation failed"));
}
}
@@ -3534,6 +3525,19 @@
init_mutator_alloc_region();
+ {
+ size_t expand_bytes = g1_policy()->expansion_amount();
+ if (expand_bytes > 0) {
+ size_t bytes_before = capacity();
+ if (!expand(expand_bytes)) {
+ // We failed to expand the heap so let's verify that
+ // committed/uncommitted amount match the backing store
+ assert(capacity() == _g1_storage.committed_size(), "committed size mismatch");
+ assert(max_capacity() == _g1_storage.reserved_size(), "reserved size mismatch");
+ }
+ }
+ }
+
double end_time_sec = os::elapsedTime();
double pause_time_ms = (end_time_sec - start_time_sec) * MILLIUNITS;
g1_policy()->record_pause_time_ms(pause_time_ms);
@@ -3579,6 +3583,8 @@
size_t expand_bytes = g1_policy()->expansion_amount();
if (expand_bytes > 0) {
size_t bytes_before = capacity();
+ // No need for an ergo verbose message here,
+ // expansion_amount() does this when it returns a value > 0.
if (!expand(expand_bytes)) {
// We failed to expand the heap so let's verify that
// committed/uncommitted amount match the backing store
@@ -3732,13 +3738,6 @@
bool do_object_b(oop p) {
// It is reachable if it is outside the collection set, or is inside
// and forwarded.
-
-#ifdef G1_DEBUG
- gclog_or_tty->print_cr("is alive "PTR_FORMAT" in CS %d forwarded %d overall %d",
- (void*) p, _g1->obj_in_cs(p), p->is_forwarded(),
- !_g1->obj_in_cs(p) || p->is_forwarded());
-#endif // G1_DEBUG
-
return !_g1->obj_in_cs(p) || p->is_forwarded();
}
};
@@ -3750,20 +3749,9 @@
void do_oop(narrowOop* p) { guarantee(false, "Not needed"); }
void do_oop( oop* p) {
oop obj = *p;
-#ifdef G1_DEBUG
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr("keep alive *"PTR_FORMAT" = "PTR_FORMAT" "PTR_FORMAT,
- p, (void*) obj, (void*) *p);
- }
-#endif // G1_DEBUG
-
if (_g1->obj_in_cs(obj)) {
assert( obj->is_forwarded(), "invariant" );
*p = obj->forwardee();
-#ifdef G1_DEBUG
- gclog_or_tty->print_cr(" in CSet: moved "PTR_FORMAT" -> "PTR_FORMAT,
- (void*) obj, (void*) *p);
-#endif // G1_DEBUG
}
}
};
@@ -4613,7 +4601,6 @@
// keep entries (which are added by the marking threads) on them
// live until they can be processed at the end of marking.
ref_processor()->weak_oops_do(&buf_scan_non_heap_roots);
- ref_processor()->oops_do(&buf_scan_non_heap_roots);
}
// Finish up any enqueued closure apps (attributed as object copy time).
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -28,6 +28,7 @@
#include "gc_implementation/g1/concurrentMarkThread.inline.hpp"
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "gc_implementation/g1/g1CollectorPolicy.hpp"
+#include "gc_implementation/g1/g1ErgoVerbose.hpp"
#include "gc_implementation/g1/heapRegionRemSet.hpp"
#include "gc_implementation/shared/gcPolicyCounters.hpp"
#include "runtime/arguments.hpp"
@@ -145,6 +146,7 @@
_stop_world_start(0.0),
_all_stop_world_times_ms(new NumberSeq()),
_all_yield_times_ms(new NumberSeq()),
+ _using_new_ratio_calculations(false),
_all_mod_union_times_ms(new NumberSeq()),
@@ -271,15 +273,26 @@
_recorded_survivor_tail(NULL),
_survivors_age_table(true),
- _gc_overhead_perc(0.0)
-
-{
+ _gc_overhead_perc(0.0) {
+
// Set up the region size and associated fields. Given that the
// policy is created before the heap, we have to set this up here,
// so it's done as soon as possible.
HeapRegion::setup_heap_region_size(Arguments::min_heap_size());
HeapRegionRemSet::setup_remset_size();
+ G1ErgoVerbose::initialize();
+ if (PrintAdaptiveSizePolicy) {
+ // Currently, we only use a single switch for all the heuristics.
+ G1ErgoVerbose::set_enabled(true);
+ // Given that we don't currently have a verboseness level
+ // parameter, we'll hardcode this to high. This can be easily
+ // changed in the future.
+ G1ErgoVerbose::set_level(ErgoHigh);
+ } else {
+ G1ErgoVerbose::set_enabled(false);
+ }
+
// Verify PLAB sizes
const uint region_size = HeapRegion::GrainWords;
if (YoungPLABSize > region_size || OldPLABSize > region_size) {
@@ -402,7 +415,7 @@
_concurrent_mark_cleanup_times_ms->add(0.20);
_tenuring_threshold = MaxTenuringThreshold;
// _max_survivor_regions will be calculated by
- // calculate_young_list_target_length() during initialization.
+ // update_young_list_target_length() during initialization.
_max_survivor_regions = 0;
assert(GCTimeRatio > 0,
@@ -410,6 +423,18 @@
"if a user set it to 0");
_gc_overhead_perc = 100.0 * (1.0 / (1.0 + GCTimeRatio));
+ uintx reserve_perc = G1ReservePercent;
+ // Put an artificial ceiling on this so that it's not set to a silly value.
+ if (reserve_perc > 50) {
+ reserve_perc = 50;
+ warning("G1ReservePercent is set to a value that is too large, "
+ "it's been updated to %u", reserve_perc);
+ }
+ _reserve_factor = (double) reserve_perc / 100.0;
+ // This will be set when the heap is expanded
+ // for the first time during initialization.
+ _reserve_regions = 0;
+
initialize_all();
}
@@ -434,16 +459,15 @@
// ParallelScavengeHeap::initialize()). We might change this in the
// future, but it's a good start.
class G1YoungGenSizer : public TwoGenerationCollectorPolicy {
- size_t size_to_region_num(size_t byte_size) {
- return MAX2((size_t) 1, byte_size / HeapRegion::GrainBytes);
- }
public:
G1YoungGenSizer() {
initialize_flags();
initialize_size_info();
}
-
+ size_t size_to_region_num(size_t byte_size) {
+ return MAX2((size_t) 1, byte_size / HeapRegion::GrainBytes);
+ }
size_t min_young_region_num() {
return size_to_region_num(_min_gen0_size);
}
@@ -455,6 +479,13 @@
}
};
+void G1CollectorPolicy::update_young_list_size_using_newratio(size_t number_of_heap_regions) {
+ assert(number_of_heap_regions > 0, "Heap must be initialized");
+ size_t young_size = number_of_heap_regions / (NewRatio + 1);
+ _min_desired_young_length = young_size;
+ _max_desired_young_length = young_size;
+}
+
void G1CollectorPolicy::init() {
// Set aside an initial future to_space.
_g1 = G1CollectedHeap::heap();
@@ -465,18 +496,35 @@
G1YoungGenSizer sizer;
size_t initial_region_num = sizer.initial_young_region_num();
-
- if (UseAdaptiveSizePolicy) {
- set_adaptive_young_list_length(true);
+ _min_desired_young_length = sizer.min_young_region_num();
+ _max_desired_young_length = sizer.max_young_region_num();
+
+ if (FLAG_IS_CMDLINE(NewRatio)) {
+ if (FLAG_IS_CMDLINE(NewSize) || FLAG_IS_CMDLINE(MaxNewSize)) {
+ gclog_or_tty->print_cr("-XX:NewSize and -XX:MaxNewSize overrides -XX:NewRatio");
+ } else {
+ // Treat NewRatio as a fixed size that is only recalculated when the heap size changes
+ size_t heap_regions = sizer.size_to_region_num(_g1->n_regions());
+ update_young_list_size_using_newratio(heap_regions);
+ _using_new_ratio_calculations = true;
+ }
+ }
+
+ // GenCollectorPolicy guarantees that min <= initial <= max.
+ // Asserting here just to state that we rely on this property.
+ assert(_min_desired_young_length <= _max_desired_young_length, "Invalid min/max young gen size values");
+ assert(initial_region_num <= _max_desired_young_length, "Initial young gen size too large");
+ assert(_min_desired_young_length <= initial_region_num, "Initial young gen size too small");
+
+ set_adaptive_young_list_length(_min_desired_young_length < _max_desired_young_length);
+ if (adaptive_young_list_length()) {
_young_list_fixed_length = 0;
} else {
- set_adaptive_young_list_length(false);
_young_list_fixed_length = initial_region_num;
}
_free_regions_at_end_of_collection = _g1->free_regions();
- calculate_young_list_min_length();
- guarantee( _young_list_min_length == 0, "invariant, not enough info" );
- calculate_young_list_target_length();
+ update_young_list_target_length();
+ _prev_eden_capacity = _young_list_target_length * HeapRegion::GrainBytes;
// We may immediately start allocating regions and placing them on the
// collection set list. Initialize the per-collection set info
@@ -484,238 +532,261 @@
}
// Create the jstat counters for the policy.
-void G1CollectorPolicy::initialize_gc_policy_counters()
-{
+void G1CollectorPolicy::initialize_gc_policy_counters() {
_gc_policy_counters = new GCPolicyCounters("GarbageFirst", 1, 3);
}
-void G1CollectorPolicy::calculate_young_list_min_length() {
- _young_list_min_length = 0;
-
- if (!adaptive_young_list_length())
- return;
-
- if (_alloc_rate_ms_seq->num() > 3) {
- double now_sec = os::elapsedTime();
- double when_ms = _mmu_tracker->when_max_gc_sec(now_sec) * 1000.0;
- double alloc_rate_ms = predict_alloc_rate_ms();
- size_t min_regions = (size_t) ceil(alloc_rate_ms * when_ms);
- size_t current_region_num = _g1->young_list()->length();
- _young_list_min_length = min_regions + current_region_num;
- }
-}
-
-void G1CollectorPolicy::calculate_young_list_target_length() {
- if (adaptive_young_list_length()) {
- size_t rs_lengths = (size_t) get_new_prediction(_rs_lengths_seq);
- calculate_young_list_target_length(rs_lengths);
- } else {
- if (full_young_gcs())
- _young_list_target_length = _young_list_fixed_length;
- else
- _young_list_target_length = _young_list_fixed_length / 2;
- }
-
- // Make sure we allow the application to allocate at least one
- // region before we need to do a collection again.
- size_t min_length = _g1->young_list()->length() + 1;
- _young_list_target_length = MAX2(_young_list_target_length, min_length);
- calculate_max_gc_locker_expansion();
- calculate_survivors_policy();
-}
-
-void G1CollectorPolicy::calculate_young_list_target_length(size_t rs_lengths) {
- guarantee( adaptive_young_list_length(), "pre-condition" );
- guarantee( !_in_marking_window || !_last_full_young_gc, "invariant" );
-
- double start_time_sec = os::elapsedTime();
- size_t min_reserve_perc = MAX2((size_t)2, (size_t)G1ReservePercent);
- min_reserve_perc = MIN2((size_t) 50, min_reserve_perc);
- size_t reserve_regions =
- (size_t) ((double) min_reserve_perc * (double) _g1->n_regions() / 100.0);
-
- if (full_young_gcs() && _free_regions_at_end_of_collection > 0) {
- // we are in fully-young mode and there are free regions in the heap
-
- double survivor_regions_evac_time =
- predict_survivor_regions_evac_time();
-
- double target_pause_time_ms = _mmu_tracker->max_gc_time() * 1000.0;
- size_t pending_cards = (size_t) get_new_prediction(_pending_cards_seq);
- size_t adj_rs_lengths = rs_lengths + predict_rs_length_diff();
- size_t scanned_cards = predict_young_card_num(adj_rs_lengths);
- double base_time_ms = predict_base_elapsed_time_ms(pending_cards, scanned_cards)
- + survivor_regions_evac_time;
-
- // the result
- size_t final_young_length = 0;
-
- size_t init_free_regions =
- MAX2((size_t)0, _free_regions_at_end_of_collection - reserve_regions);
-
- // if we're still under the pause target...
- if (base_time_ms <= target_pause_time_ms) {
- // We make sure that the shortest young length that makes sense
- // fits within the target pause time.
- size_t min_young_length = 1;
-
- if (predict_will_fit(min_young_length, base_time_ms,
- init_free_regions, target_pause_time_ms)) {
- // The shortest young length will fit within the target pause time;
- // we'll now check whether the absolute maximum number of young
- // regions will fit in the target pause time. If not, we'll do
- // a binary search between min_young_length and max_young_length
- size_t abs_max_young_length = _free_regions_at_end_of_collection - 1;
- size_t max_young_length = abs_max_young_length;
-
- if (max_young_length > min_young_length) {
- // Let's check if the initial max young length will fit within the
- // target pause. If so then there is no need to search for a maximal
- // young length - we'll return the initial maximum
-
- if (predict_will_fit(max_young_length, base_time_ms,
- init_free_regions, target_pause_time_ms)) {
- // The maximum young length will satisfy the target pause time.
- // We are done so set min young length to this maximum length.
- // The code after the loop will then set final_young_length using
- // the value cached in the minimum length.
- min_young_length = max_young_length;
- } else {
- // The maximum possible number of young regions will not fit within
- // the target pause time so let's search....
-
- size_t diff = (max_young_length - min_young_length) / 2;
- max_young_length = min_young_length + diff;
-
- while (max_young_length > min_young_length) {
- if (predict_will_fit(max_young_length, base_time_ms,
- init_free_regions, target_pause_time_ms)) {
-
- // The current max young length will fit within the target
- // pause time. Note we do not exit the loop here. By setting
- // min = max, and then increasing the max below means that
- // we will continue searching for an upper bound in the
- // range [max..max+diff]
- min_young_length = max_young_length;
- }
- diff = (max_young_length - min_young_length) / 2;
- max_young_length = min_young_length + diff;
- }
- // the above loop found a maximal young length that will fit
- // within the target pause time.
- }
- assert(min_young_length <= abs_max_young_length, "just checking");
- }
- final_young_length = min_young_length;
- }
- }
- // and we're done!
-
- // we should have at least one region in the target young length
- _young_list_target_length =
- final_young_length + _recorded_survivor_regions;
-
- // let's keep an eye of how long we spend on this calculation
- // right now, I assume that we'll print it when we need it; we
- // should really adde it to the breakdown of a pause
- double end_time_sec = os::elapsedTime();
- double elapsed_time_ms = (end_time_sec - start_time_sec) * 1000.0;
-
-#ifdef TRACE_CALC_YOUNG_LENGTH
- // leave this in for debugging, just in case
- gclog_or_tty->print_cr("target = %1.1lf ms, young = " SIZE_FORMAT ", "
- "elapsed %1.2lf ms, (%s%s) " SIZE_FORMAT SIZE_FORMAT,
- target_pause_time_ms,
- _young_list_target_length
- elapsed_time_ms,
- full_young_gcs() ? "full" : "partial",
- during_initial_mark_pause() ? " i-m" : "",
- _in_marking_window,
- _in_marking_window_im);
-#endif // TRACE_CALC_YOUNG_LENGTH
-
- if (_young_list_target_length < _young_list_min_length) {
- // bummer; this means that, if we do a pause when the maximal
- // length dictates, we'll violate the pause spacing target (the
- // min length was calculate based on the application's current
- // alloc rate);
-
- // so, we have to bite the bullet, and allocate the minimum
- // number. We'll violate our target, but we just can't meet it.
-
-#ifdef TRACE_CALC_YOUNG_LENGTH
- // leave this in for debugging, just in case
- gclog_or_tty->print_cr("adjusted target length from "
- SIZE_FORMAT " to " SIZE_FORMAT,
- _young_list_target_length, _young_list_min_length);
-#endif // TRACE_CALC_YOUNG_LENGTH
-
- _young_list_target_length = _young_list_min_length;
- }
- } else {
- // we are in a partially-young mode or we've run out of regions (due
- // to evacuation failure)
-
-#ifdef TRACE_CALC_YOUNG_LENGTH
- // leave this in for debugging, just in case
- gclog_or_tty->print_cr("(partial) setting target to " SIZE_FORMAT
- _young_list_min_length);
-#endif // TRACE_CALC_YOUNG_LENGTH
- // we'll do the pause as soon as possible by choosing the minimum
- _young_list_target_length = _young_list_min_length;
- }
-
- _rs_lengths_prediction = rs_lengths;
-}
-
-// This is used by: calculate_young_list_target_length(rs_length). It
-// returns true iff:
-// the predicted pause time for the given young list will not overflow
-// the target pause time
-// and:
-// the predicted amount of surviving data will not overflow the
-// the amount of free space available for survivor regions.
-//
-bool
-G1CollectorPolicy::predict_will_fit(size_t young_length,
- double base_time_ms,
- size_t init_free_regions,
- double target_pause_time_ms) {
-
- if (young_length >= init_free_regions)
+bool G1CollectorPolicy::predict_will_fit(size_t young_length,
+ double base_time_ms,
+ size_t base_free_regions,
+ double target_pause_time_ms) {
+ if (young_length >= base_free_regions) {
// end condition 1: not enough space for the young regions
return false;
-
- double accum_surv_rate_adj = 0.0;
- double accum_surv_rate =
- accum_yg_surv_rate_pred((int)(young_length - 1)) - accum_surv_rate_adj;
-
+ }
+
+ double accum_surv_rate = accum_yg_surv_rate_pred((int)(young_length - 1));
size_t bytes_to_copy =
- (size_t) (accum_surv_rate * (double) HeapRegion::GrainBytes);
-
+ (size_t) (accum_surv_rate * (double) HeapRegion::GrainBytes);
double copy_time_ms = predict_object_copy_time_ms(bytes_to_copy);
-
- double young_other_time_ms =
- predict_young_other_time_ms(young_length);
-
- double pause_time_ms =
- base_time_ms + copy_time_ms + young_other_time_ms;
-
- if (pause_time_ms > target_pause_time_ms)
- // end condition 2: over the target pause time
+ double young_other_time_ms = predict_young_other_time_ms(young_length);
+ double pause_time_ms = base_time_ms + copy_time_ms + young_other_time_ms;
+ if (pause_time_ms > target_pause_time_ms) {
+ // end condition 2: prediction is over the target pause time
return false;
+ }
size_t free_bytes =
- (init_free_regions - young_length) * HeapRegion::GrainBytes;
-
- if ((2.0 + sigma()) * (double) bytes_to_copy > (double) free_bytes)
- // end condition 3: out of to-space (conservatively)
+ (base_free_regions - young_length) * HeapRegion::GrainBytes;
+ if ((2.0 * sigma()) * (double) bytes_to_copy > (double) free_bytes) {
+ // end condition 3: out-of-space (conservatively!)
return false;
+ }
// success!
return true;
}
+void G1CollectorPolicy::record_new_heap_size(size_t new_number_of_regions) {
+ // re-calculate the necessary reserve
+ double reserve_regions_d = (double) new_number_of_regions * _reserve_factor;
+ // We use ceiling so that if reserve_regions_d is > 0.0 (but
+ // smaller than 1.0) we'll get 1.
+ _reserve_regions = (size_t) ceil(reserve_regions_d);
+
+ if (_using_new_ratio_calculations) {
+ // -XX:NewRatio was specified so we need to update the
+ // young gen length when the heap size has changed.
+ update_young_list_size_using_newratio(new_number_of_regions);
+ }
+}
+
+size_t G1CollectorPolicy::calculate_young_list_desired_min_length(
+ size_t base_min_length) {
+ size_t desired_min_length = 0;
+ if (adaptive_young_list_length()) {
+ if (_alloc_rate_ms_seq->num() > 3) {
+ double now_sec = os::elapsedTime();
+ double when_ms = _mmu_tracker->when_max_gc_sec(now_sec) * 1000.0;
+ double alloc_rate_ms = predict_alloc_rate_ms();
+ desired_min_length = (size_t) ceil(alloc_rate_ms * when_ms);
+ } else {
+ // otherwise we don't have enough info to make the prediction
+ }
+ }
+ desired_min_length += base_min_length;
+ // make sure we don't go below any user-defined minimum bound
+ return MAX2(_min_desired_young_length, desired_min_length);
+}
+
+size_t G1CollectorPolicy::calculate_young_list_desired_max_length() {
+ // Here, we might want to also take into account any additional
+ // constraints (i.e., user-defined minimum bound). Currently, we
+ // effectively don't set this bound.
+ return _max_desired_young_length;
+}
+
+void G1CollectorPolicy::update_young_list_target_length(size_t rs_lengths) {
+ if (rs_lengths == (size_t) -1) {
+ // if it's set to the default value (-1), we should predict it;
+ // otherwise, use the given value.
+ rs_lengths = (size_t) get_new_prediction(_rs_lengths_seq);
+ }
+
+ // Calculate the absolute and desired min bounds.
+
+ // This is how many young regions we already have (currently: the survivors).
+ size_t base_min_length = recorded_survivor_regions();
+ // This is the absolute minimum young length, which ensures that we
+ // can allocate one eden region in the worst-case.
+ size_t absolute_min_length = base_min_length + 1;
+ size_t desired_min_length =
+ calculate_young_list_desired_min_length(base_min_length);
+ if (desired_min_length < absolute_min_length) {
+ desired_min_length = absolute_min_length;
+ }
+
+ // Calculate the absolute and desired max bounds.
+
+ // We will try our best not to "eat" into the reserve.
+ size_t absolute_max_length = 0;
+ if (_free_regions_at_end_of_collection > _reserve_regions) {
+ absolute_max_length = _free_regions_at_end_of_collection - _reserve_regions;
+ }
+ size_t desired_max_length = calculate_young_list_desired_max_length();
+ if (desired_max_length > absolute_max_length) {
+ desired_max_length = absolute_max_length;
+ }
+
+ size_t young_list_target_length = 0;
+ if (adaptive_young_list_length()) {
+ if (full_young_gcs()) {
+ young_list_target_length =
+ calculate_young_list_target_length(rs_lengths,
+ base_min_length,
+ desired_min_length,
+ desired_max_length);
+ _rs_lengths_prediction = rs_lengths;
+ } else {
+ // Don't calculate anything and let the code below bound it to
+ // the desired_min_length, i.e., do the next GC as soon as
+ // possible to maximize how many old regions we can add to it.
+ }
+ } else {
+ if (full_young_gcs()) {
+ young_list_target_length = _young_list_fixed_length;
+ } else {
+ // A bit arbitrary: during partially-young GCs we allocate half
+ // the young regions to try to add old regions to the CSet.
+ young_list_target_length = _young_list_fixed_length / 2;
+ // We choose to accept that we might go under the desired min
+ // length given that we intentionally ask for a smaller young gen.
+ desired_min_length = absolute_min_length;
+ }
+ }
+
+ // Make sure we don't go over the desired max length, nor under the
+ // desired min length. In case they clash, desired_min_length wins
+ // which is why that test is second.
+ if (young_list_target_length > desired_max_length) {
+ young_list_target_length = desired_max_length;
+ }
+ if (young_list_target_length < desired_min_length) {
+ young_list_target_length = desired_min_length;
+ }
+
+ assert(young_list_target_length > recorded_survivor_regions(),
+ "we should be able to allocate at least one eden region");
+ assert(young_list_target_length >= absolute_min_length, "post-condition");
+ _young_list_target_length = young_list_target_length;
+
+ update_max_gc_locker_expansion();
+}
+
+size_t
+G1CollectorPolicy::calculate_young_list_target_length(size_t rs_lengths,
+ size_t base_min_length,
+ size_t desired_min_length,
+ size_t desired_max_length) {
+ assert(adaptive_young_list_length(), "pre-condition");
+ assert(full_young_gcs(), "only call this for fully-young GCs");
+
+ // In case some edge-condition makes the desired max length too small...
+ if (desired_max_length <= desired_min_length) {
+ return desired_min_length;
+ }
+
+ // We'll adjust min_young_length and max_young_length not to include
+ // the already allocated young regions (i.e., so they reflect the
+ // min and max eden regions we'll allocate). The base_min_length
+ // will be reflected in the predictions by the
+ // survivor_regions_evac_time prediction.
+ assert(desired_min_length > base_min_length, "invariant");
+ size_t min_young_length = desired_min_length - base_min_length;
+ assert(desired_max_length > base_min_length, "invariant");
+ size_t max_young_length = desired_max_length - base_min_length;
+
+ double target_pause_time_ms = _mmu_tracker->max_gc_time() * 1000.0;
+ double survivor_regions_evac_time = predict_survivor_regions_evac_time();
+ size_t pending_cards = (size_t) get_new_prediction(_pending_cards_seq);
+ size_t adj_rs_lengths = rs_lengths + predict_rs_length_diff();
+ size_t scanned_cards = predict_young_card_num(adj_rs_lengths);
+ double base_time_ms =
+ predict_base_elapsed_time_ms(pending_cards, scanned_cards) +
+ survivor_regions_evac_time;
+ size_t available_free_regions = _free_regions_at_end_of_collection;
+ size_t base_free_regions = 0;
+ if (available_free_regions > _reserve_regions) {
+ base_free_regions = available_free_regions - _reserve_regions;
+ }
+
+ // Here, we will make sure that the shortest young length that
+ // makes sense fits within the target pause time.
+
+ if (predict_will_fit(min_young_length, base_time_ms,
+ base_free_regions, target_pause_time_ms)) {
+ // The shortest young length will fit into the target pause time;
+ // we'll now check whether the absolute maximum number of young
+ // regions will fit in the target pause time. If not, we'll do
+ // a binary search between min_young_length and max_young_length.
+ if (predict_will_fit(max_young_length, base_time_ms,
+ base_free_regions, target_pause_time_ms)) {
+ // The maximum young length will fit into the target pause time.
+ // We are done so set min young length to the maximum length (as
+ // the result is assumed to be returned in min_young_length).
+ min_young_length = max_young_length;
+ } else {
+ // The maximum possible number of young regions will not fit within
+ // the target pause time so we'll search for the optimal
+ // length. The loop invariants are:
+ //
+ // min_young_length < max_young_length
+ // min_young_length is known to fit into the target pause time
+ // max_young_length is known not to fit into the target pause time
+ //
+ // Going into the loop we know the above hold as we've just
+ // checked them. Every time around the loop we check whether
+ // the middle value between min_young_length and
+ // max_young_length fits into the target pause time. If it
+ // does, it becomes the new min. If it doesn't, it becomes
+ // the new max. This way we maintain the loop invariants.
+
+ assert(min_young_length < max_young_length, "invariant");
+ size_t diff = (max_young_length - min_young_length) / 2;
+ while (diff > 0) {
+ size_t young_length = min_young_length + diff;
+ if (predict_will_fit(young_length, base_time_ms,
+ base_free_regions, target_pause_time_ms)) {
+ min_young_length = young_length;
+ } else {
+ max_young_length = young_length;
+ }
+ assert(min_young_length < max_young_length, "invariant");
+ diff = (max_young_length - min_young_length) / 2;
+ }
+ // The results is min_young_length which, according to the
+ // loop invariants, should fit within the target pause time.
+
+ // These are the post-conditions of the binary search above:
+ assert(min_young_length < max_young_length,
+ "otherwise we should have discovered that max_young_length "
+ "fits into the pause target and not done the binary search");
+ assert(predict_will_fit(min_young_length, base_time_ms,
+ base_free_regions, target_pause_time_ms),
+ "min_young_length, the result of the binary search, should "
+ "fit into the pause target");
+ assert(!predict_will_fit(min_young_length + 1, base_time_ms,
+ base_free_regions, target_pause_time_ms),
+ "min_young_length, the result of the binary search, should be "
+ "optimal, so no larger length should fit into the pause target");
+ }
+ } else {
+ // Even the minimum length doesn't fit into the pause time
+ // target, return it as the result nevertheless.
+ }
+ return base_min_length + min_young_length;
+}
+
double G1CollectorPolicy::predict_survivor_regions_evac_time() {
double survivor_regions_evac_time = 0.0;
for (HeapRegion * r = _recorded_survivor_head;
@@ -726,17 +797,19 @@
return survivor_regions_evac_time;
}
-void G1CollectorPolicy::check_prediction_validity() {
+void G1CollectorPolicy::revise_young_list_target_length_if_necessary() {
guarantee( adaptive_young_list_length(), "should not call this otherwise" );
size_t rs_lengths = _g1->young_list()->sampled_rs_lengths();
if (rs_lengths > _rs_lengths_prediction) {
// add 10% to avoid having to recalculate often
size_t rs_lengths_prediction = rs_lengths * 1100 / 1000;
- calculate_young_list_target_length(rs_lengths_prediction);
+ update_young_list_target_length(rs_lengths_prediction);
}
}
+
+
HeapWord* G1CollectorPolicy::mem_allocate_work(size_t size,
bool is_tlab,
bool* gc_overhead_limit_was_exceeded) {
@@ -843,8 +916,7 @@
_free_regions_at_end_of_collection = _g1->free_regions();
// Reset survivors SurvRateGroup.
_survivor_surv_rate_group->reset();
- calculate_young_list_min_length();
- calculate_young_list_target_length();
+ update_young_list_target_length();
}
void G1CollectorPolicy::record_stop_world_start() {
@@ -859,6 +931,11 @@
gclog_or_tty->print(" (%s)", full_young_gcs() ? "young" : "partial");
}
+ // We only need to do this here as the policy will only be applied
+ // to the GC we're about to start. so, no point is calculating this
+ // every time we calculate / recalculate the target young length.
+ update_survivors_policy();
+
assert(_g1->used() == _g1->recalculate_used(),
err_msg("sanity, used: "SIZE_FORMAT" recalculate_used: "SIZE_FORMAT,
_g1->used(), _g1->recalculate_used()));
@@ -959,11 +1036,9 @@
G1CollectorPolicy::
record_concurrent_mark_cleanup_end_work1(size_t freed_bytes,
size_t max_live_bytes) {
- if (_n_marks < 2) _n_marks++;
- if (G1PolicyVerbose > 0)
- gclog_or_tty->print_cr("At end of marking, max_live is " SIZE_FORMAT " MB "
- " (of " SIZE_FORMAT " MB heap).",
- max_live_bytes/M, _g1->capacity()/M);
+ if (_n_marks < 2) {
+ _n_marks++;
+ }
}
// The important thing about this is that it includes "os::elapsedTime".
@@ -977,14 +1052,6 @@
_mmu_tracker->add_pause(_mark_cleanup_start_sec, end_time_sec, true);
_num_markings++;
-
- // We did a marking, so reset the "since_last_mark" variables.
- double considerConcMarkCost = 1.0;
- // If there are available processors, concurrent activity is free...
- if (Threads::number_of_non_daemon_threads() * 2 <
- os::active_processor_count()) {
- considerConcMarkCost = 0.0;
- }
_n_pauses_at_mark_end = _n_pauses;
_n_marks_since_last_pause++;
}
@@ -994,8 +1061,6 @@
_should_revert_to_full_young_gcs = false;
_last_full_young_gc = true;
_in_marking_window = false;
- if (adaptive_young_list_length())
- calculate_young_list_target_length();
}
void G1CollectorPolicy::record_concurrent_pause() {
@@ -1148,20 +1213,37 @@
if (last_pause_included_initial_mark)
record_concurrent_mark_init_end(0.0);
- size_t min_used_targ =
+ size_t marking_initiating_used_threshold =
(_g1->capacity() / 100) * InitiatingHeapOccupancyPercent;
-
if (!_g1->mark_in_progress() && !_last_full_young_gc) {
assert(!last_pause_included_initial_mark, "invariant");
- if (cur_used_bytes > min_used_targ &&
- cur_used_bytes > _prev_collection_pause_used_at_end_bytes) {
+ if (cur_used_bytes > marking_initiating_used_threshold) {
+ if (cur_used_bytes > _prev_collection_pause_used_at_end_bytes) {
assert(!during_initial_mark_pause(), "we should not see this here");
+ ergo_verbose3(ErgoConcCycles,
+ "request concurrent cycle initiation",
+ ergo_format_reason("occupancy higher than threshold")
+ ergo_format_byte("occupancy")
+ ergo_format_byte_perc("threshold"),
+ cur_used_bytes,
+ marking_initiating_used_threshold,
+ (double) InitiatingHeapOccupancyPercent);
+
// Note: this might have already been set, if during the last
// pause we decided to start a cycle but at the beginning of
// this pause we decided to postpone it. That's OK.
set_initiate_conc_mark_if_possible();
+ } else {
+ ergo_verbose2(ErgoConcCycles,
+ "do not request concurrent cycle initiation",
+ ergo_format_reason("occupancy lower than previous occupancy")
+ ergo_format_byte("occupancy")
+ ergo_format_byte("previous occupancy"),
+ cur_used_bytes,
+ _prev_collection_pause_used_at_end_bytes);
+ }
}
}
@@ -1437,16 +1519,45 @@
}
if (_last_full_young_gc) {
+ ergo_verbose2(ErgoPartiallyYoungGCs,
+ "start partially-young GCs",
+ ergo_format_byte_perc("known garbage"),
+ _known_garbage_bytes, _known_garbage_ratio * 100.0);
set_full_young_gcs(false);
_last_full_young_gc = false;
}
if ( !_last_young_gc_full ) {
- if ( _should_revert_to_full_young_gcs ||
- _known_garbage_ratio < 0.05 ||
- (adaptive_young_list_length() &&
- (get_gc_eff_factor() * cur_efficiency < predict_young_gc_eff())) ) {
- set_full_young_gcs(true);
+ if (_should_revert_to_full_young_gcs) {
+ ergo_verbose2(ErgoPartiallyYoungGCs,
+ "end partially-young GCs",
+ ergo_format_reason("partially-young GCs end requested")
+ ergo_format_byte_perc("known garbage"),
+ _known_garbage_bytes, _known_garbage_ratio * 100.0);
+ set_full_young_gcs(true);
+ } else if (_known_garbage_ratio < 0.05) {
+ ergo_verbose3(ErgoPartiallyYoungGCs,
+ "end partially-young GCs",
+ ergo_format_reason("known garbage percent lower than threshold")
+ ergo_format_byte_perc("known garbage")
+ ergo_format_perc("threshold"),
+ _known_garbage_bytes, _known_garbage_ratio * 100.0,
+ 0.05 * 100.0);
+ set_full_young_gcs(true);
+ } else if (adaptive_young_list_length() &&
+ (get_gc_eff_factor() * cur_efficiency < predict_young_gc_eff())) {
+ ergo_verbose5(ErgoPartiallyYoungGCs,
+ "end partially-young GCs",
+ ergo_format_reason("current GC efficiency lower than "
+ "predicted fully-young GC efficiency")
+ ergo_format_double("GC efficiency factor")
+ ergo_format_double("current GC efficiency")
+ ergo_format_double("predicted fully-young GC efficiency")
+ ergo_format_byte_perc("known garbage"),
+ get_gc_eff_factor(), cur_efficiency,
+ predict_young_gc_eff(),
+ _known_garbage_bytes, _known_garbage_ratio * 100.0);
+ set_full_young_gcs(true);
}
}
_should_revert_to_full_young_gcs = false;
@@ -1600,8 +1711,7 @@
_in_marking_window = new_in_marking_window;
_in_marking_window_im = new_in_marking_window_im;
_free_regions_at_end_of_collection = _g1->free_regions();
- calculate_young_list_min_length();
- calculate_young_list_target_length();
+ update_young_list_target_length();
// Note that _mmu_tracker->max_gc_time() returns the time in seconds.
double update_rs_time_goal_ms = _mmu_tracker->max_gc_time() * MILLIUNITS * G1RSetUpdatingPauseTimePercent / 100.0;
@@ -1622,20 +1732,26 @@
size_t used_before_gc = _cur_collection_pause_used_at_start_bytes;
size_t used = _g1->used();
size_t capacity = _g1->capacity();
+ size_t eden_capacity =
+ (_young_list_target_length * HeapRegion::GrainBytes) - survivor_bytes;
gclog_or_tty->print_cr(
- " [Eden: "EXT_SIZE_FORMAT"->"EXT_SIZE_FORMAT" "
- "Survivors: "EXT_SIZE_FORMAT"->"EXT_SIZE_FORMAT" "
- "Heap: "EXT_SIZE_FORMAT"("EXT_SIZE_FORMAT")->"
- EXT_SIZE_FORMAT"("EXT_SIZE_FORMAT")]",
- EXT_SIZE_PARAMS(_eden_bytes_before_gc),
- EXT_SIZE_PARAMS(eden_bytes),
- EXT_SIZE_PARAMS(_survivor_bytes_before_gc),
- EXT_SIZE_PARAMS(survivor_bytes),
- EXT_SIZE_PARAMS(used_before_gc),
- EXT_SIZE_PARAMS(_capacity_before_gc),
- EXT_SIZE_PARAMS(used),
- EXT_SIZE_PARAMS(capacity));
+ " [Eden: "EXT_SIZE_FORMAT"("EXT_SIZE_FORMAT")->"EXT_SIZE_FORMAT"("EXT_SIZE_FORMAT") "
+ "Survivors: "EXT_SIZE_FORMAT"->"EXT_SIZE_FORMAT" "
+ "Heap: "EXT_SIZE_FORMAT"("EXT_SIZE_FORMAT")->"
+ EXT_SIZE_FORMAT"("EXT_SIZE_FORMAT")]",
+ EXT_SIZE_PARAMS(_eden_bytes_before_gc),
+ EXT_SIZE_PARAMS(_prev_eden_capacity),
+ EXT_SIZE_PARAMS(eden_bytes),
+ EXT_SIZE_PARAMS(eden_capacity),
+ EXT_SIZE_PARAMS(_survivor_bytes_before_gc),
+ EXT_SIZE_PARAMS(survivor_bytes),
+ EXT_SIZE_PARAMS(used_before_gc),
+ EXT_SIZE_PARAMS(_capacity_before_gc),
+ EXT_SIZE_PARAMS(used),
+ EXT_SIZE_PARAMS(capacity));
+
+ _prev_eden_capacity = eden_capacity;
} else if (PrintGC) {
_g1->print_size_transition(gclog_or_tty,
_cur_collection_pause_used_at_start_bytes,
@@ -1877,6 +1993,12 @@
// I don't think we need to do this when in young GC mode since
// marking will be initiated next time we hit the soft limit anyway...
if (predicted_time_ms > _expensive_region_limit_ms) {
+ ergo_verbose2(ErgoPartiallyYoungGCs,
+ "request partially-young GCs end",
+ ergo_format_reason("predicted region time higher than threshold")
+ ergo_format_ms("predicted region time")
+ ergo_format_ms("threshold"),
+ predicted_time_ms, _expensive_region_limit_ms);
// no point in doing another partial one
_should_revert_to_full_young_gcs = true;
}
@@ -1986,7 +2108,9 @@
}
size_t G1CollectorPolicy::expansion_amount() {
- if ((recent_avg_pause_time_ratio() * 100.0) > _gc_overhead_perc) {
+ double recent_gc_overhead = recent_avg_pause_time_ratio() * 100.0;
+ double threshold = _gc_overhead_perc;
+ if (recent_gc_overhead > threshold) {
// We will double the existing space, or take
// G1ExpandByPercentOfAvailable % of the available expansion
// space, whichever is smaller, bounded below by a minimum
@@ -2001,20 +2125,19 @@
expand_bytes = MIN2(expand_bytes_via_pct, committed_bytes);
expand_bytes = MAX2(expand_bytes, min_expand_bytes);
expand_bytes = MIN2(expand_bytes, uncommitted_bytes);
- if (G1PolicyVerbose > 1) {
- gclog_or_tty->print("Decided to expand: ratio = %5.2f, "
- "committed = %d%s, uncommited = %d%s, via pct = %d%s.\n"
- " Answer = %d.\n",
- recent_avg_pause_time_ratio(),
- byte_size_in_proper_unit(committed_bytes),
- proper_unit_for_byte_size(committed_bytes),
- byte_size_in_proper_unit(uncommitted_bytes),
- proper_unit_for_byte_size(uncommitted_bytes),
- byte_size_in_proper_unit(expand_bytes_via_pct),
- proper_unit_for_byte_size(expand_bytes_via_pct),
- byte_size_in_proper_unit(expand_bytes),
- proper_unit_for_byte_size(expand_bytes));
- }
+
+ ergo_verbose5(ErgoHeapSizing,
+ "attempt heap expansion",
+ ergo_format_reason("recent GC overhead higher than "
+ "threshold after GC")
+ ergo_format_perc("recent GC overhead")
+ ergo_format_perc("threshold")
+ ergo_format_byte("uncommitted")
+ ergo_format_byte_perc("calculated expansion amount"),
+ recent_gc_overhead, threshold,
+ uncommitted_bytes,
+ expand_bytes_via_pct, (double) G1ExpandByPercentOfAvailable);
+
return expand_bytes;
} else {
return 0;
@@ -2237,8 +2360,7 @@
#endif // PRODUCT
}
-void
-G1CollectorPolicy::update_region_num(bool young) {
+void G1CollectorPolicy::update_region_num(bool young) {
if (young) {
++_region_num_young;
} else {
@@ -2270,7 +2392,7 @@
};
}
-void G1CollectorPolicy::calculate_max_gc_locker_expansion() {
+void G1CollectorPolicy::update_max_gc_locker_expansion() {
size_t expansion_region_num = 0;
if (GCLockerEdenExpansionPercent > 0) {
double perc = (double) GCLockerEdenExpansionPercent / 100.0;
@@ -2286,9 +2408,13 @@
}
// Calculates survivor space parameters.
-void G1CollectorPolicy::calculate_survivors_policy()
-{
- _max_survivor_regions = _young_list_target_length / SurvivorRatio;
+void G1CollectorPolicy::update_survivors_policy() {
+ double max_survivor_regions_d =
+ (double) _young_list_target_length / (double) SurvivorRatio;
+ // We use ceiling so that if max_survivor_regions_d is > 0.0 (but
+ // smaller than 1.0) we'll get 1.
+ _max_survivor_regions = (size_t) ceil(max_survivor_regions_d);
+
_tenuring_threshold = _survivors_age_table.compute_tenuring_threshold(
HeapRegion::GrainWords * _max_survivor_regions);
}
@@ -2315,13 +2441,23 @@
}
#endif
-bool
-G1CollectorPolicy::force_initial_mark_if_outside_cycle() {
+bool G1CollectorPolicy::force_initial_mark_if_outside_cycle(
+ GCCause::Cause gc_cause) {
bool during_cycle = _g1->concurrent_mark()->cmThread()->during_cycle();
if (!during_cycle) {
+ ergo_verbose1(ErgoConcCycles,
+ "request concurrent cycle initiation",
+ ergo_format_reason("requested by GC cause")
+ ergo_format_str("GC cause"),
+ GCCause::to_string(gc_cause));
set_initiate_conc_mark_if_possible();
return true;
} else {
+ ergo_verbose1(ErgoConcCycles,
+ "do not request concurrent cycle initiation",
+ ergo_format_reason("concurrent cycle already in progress")
+ ergo_format_str("GC cause"),
+ GCCause::to_string(gc_cause));
return false;
}
}
@@ -2353,6 +2489,10 @@
// And we can now clear initiate_conc_mark_if_possible() as
// we've already acted on it.
clear_initiate_conc_mark_if_possible();
+
+ ergo_verbose0(ErgoConcCycles,
+ "initiate concurrent cycle",
+ ergo_format_reason("concurrent cycle initiation requested"));
} else {
// The concurrent marking thread is still finishing up the
// previous cycle. If we start one right now the two cycles
@@ -2366,6 +2506,9 @@
// and, if it's in a yield point, it's waiting for us to
// finish. So, at this point we will not start a cycle and we'll
// let the concurrent marking thread complete the last one.
+ ergo_verbose0(ErgoConcCycles,
+ "do not initiate concurrent cycle",
+ ergo_format_reason("concurrent cycle already in progress"));
}
}
}
@@ -2756,6 +2899,8 @@
// Set this here - in case we're not doing young collections.
double non_young_start_time_sec = os::elapsedTime();
+ YoungList* young_list = _g1->young_list();
+
start_recording_regions();
guarantee(target_pause_time_ms > 0.0,
@@ -2768,61 +2913,62 @@
double time_remaining_ms = target_pause_time_ms - base_time_ms;
+ ergo_verbose3(ErgoCSetConstruction | ErgoHigh,
+ "start choosing CSet",
+ ergo_format_ms("predicted base time")
+ ergo_format_ms("remaining time")
+ ergo_format_ms("target pause time"),
+ base_time_ms, time_remaining_ms, target_pause_time_ms);
+
// the 10% and 50% values are arbitrary...
- if (time_remaining_ms < 0.10 * target_pause_time_ms) {
+ double threshold = 0.10 * target_pause_time_ms;
+ if (time_remaining_ms < threshold) {
+ double prev_time_remaining_ms = time_remaining_ms;
time_remaining_ms = 0.50 * target_pause_time_ms;
_within_target = false;
+ ergo_verbose3(ErgoCSetConstruction,
+ "adjust remaining time",
+ ergo_format_reason("remaining time lower than threshold")
+ ergo_format_ms("remaining time")
+ ergo_format_ms("threshold")
+ ergo_format_ms("adjusted remaining time"),
+ prev_time_remaining_ms, threshold, time_remaining_ms);
} else {
_within_target = true;
}
- // We figure out the number of bytes available for future to-space.
- // For new regions without marking information, we must assume the
- // worst-case of complete survival. If we have marking information for a
- // region, we can bound the amount of live data. We can add a number of
- // such regions, as long as the sum of the live data bounds does not
- // exceed the available evacuation space.
- size_t max_live_bytes = _g1->free_regions() * HeapRegion::GrainBytes;
-
- size_t expansion_bytes =
- _g1->expansion_regions() * HeapRegion::GrainBytes;
+ size_t expansion_bytes = _g1->expansion_regions() * HeapRegion::GrainBytes;
+
+ HeapRegion* hr;
+ double young_start_time_sec = os::elapsedTime();
_collection_set_bytes_used_before = 0;
_collection_set_size = 0;
-
- // Adjust for expansion and slop.
- max_live_bytes = max_live_bytes + expansion_bytes;
-
- HeapRegion* hr;
- double young_start_time_sec = os::elapsedTime();
-
- if (G1PolicyVerbose > 0) {
- gclog_or_tty->print_cr("Adding %d young regions to the CSet",
- _g1->young_list()->length());
- }
-
_young_cset_length = 0;
_last_young_gc_full = full_young_gcs() ? true : false;
- if (_last_young_gc_full)
+ if (_last_young_gc_full) {
++_full_young_pause_num;
- else
+ } else {
++_partial_young_pause_num;
+ }
// 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].
- hr = _g1->young_list()->first_survivor_region();
+ size_t survivor_region_num = young_list->survivor_length();
+ size_t eden_region_num = young_list->length() - survivor_region_num;
+ size_t old_region_num = 0;
+ hr = young_list->first_survivor_region();
while (hr != NULL) {
assert(hr->is_survivor(), "badly formed young list");
hr->set_young();
hr = hr->get_next_young_region();
}
- // Clear the fields that point to the survivor list - they are
- // all young now.
- _g1->young_list()->clear_survivors();
+ // Clear the fields that point to the survivor list - they are all young now.
+ young_list->clear_survivors();
if (_g1->mark_in_progress())
_g1->concurrent_mark()->register_collection_set_finger(_inc_cset_max_finger);
@@ -2831,14 +2977,17 @@
_collection_set = _inc_cset_head;
_collection_set_size = _inc_cset_size;
_collection_set_bytes_used_before = _inc_cset_bytes_used_before;
-
- // For young regions in the collection set, we assume the worst
- // case of complete survival
- max_live_bytes -= _inc_cset_size * HeapRegion::GrainBytes;
-
time_remaining_ms -= _inc_cset_predicted_elapsed_time_ms;
predicted_pause_time_ms += _inc_cset_predicted_elapsed_time_ms;
+ ergo_verbose3(ErgoCSetConstruction | ErgoHigh,
+ "add young regions to CSet",
+ ergo_format_region("eden")
+ ergo_format_region("survivors")
+ ergo_format_ms("predicted young region time"),
+ eden_region_num, survivor_region_num,
+ _inc_cset_predicted_elapsed_time_ms);
+
// The number of recorded young regions is the incremental
// collection set's current size
set_recorded_young_regions(_inc_cset_size);
@@ -2848,14 +2997,7 @@
set_predicted_bytes_to_copy(_inc_cset_predicted_bytes_to_copy);
#endif // PREDICTIONS_VERBOSE
- if (G1PolicyVerbose > 0) {
- gclog_or_tty->print_cr(" Added " PTR_FORMAT " Young Regions to CS.",
- _inc_cset_size);
- gclog_or_tty->print_cr(" (" SIZE_FORMAT " KB left in heap.)",
- max_live_bytes/K);
- }
-
- assert(_inc_cset_size == _g1->young_list()->length(), "Invariant");
+ assert(_inc_cset_size == young_list->length(), "Invariant");
double young_end_time_sec = os::elapsedTime();
_recorded_young_cset_choice_time_ms =
@@ -2869,6 +3011,8 @@
NumberSeq seq;
double avg_prediction = 100000000000000000.0; // something very large
+ size_t prev_collection_set_size = _collection_set_size;
+ double prev_predicted_pause_time_ms = predicted_pause_time_ms;
do {
hr = _collectionSetChooser->getNextMarkedRegion(time_remaining_ms,
avg_prediction);
@@ -2878,23 +3022,58 @@
predicted_pause_time_ms += predicted_time_ms;
add_to_collection_set(hr);
record_non_young_cset_region(hr);
- max_live_bytes -= MIN2(hr->max_live_bytes(), max_live_bytes);
- if (G1PolicyVerbose > 0) {
- gclog_or_tty->print_cr(" (" SIZE_FORMAT " KB left in heap.)",
- max_live_bytes/K);
- }
seq.add(predicted_time_ms);
avg_prediction = seq.avg() + seq.sd();
}
- should_continue =
- ( hr != NULL) &&
- ( (adaptive_young_list_length()) ? time_remaining_ms > 0.0
- : _collection_set_size < _young_list_fixed_length );
+
+ should_continue = true;
+ if (hr == NULL) {
+ // No need for an ergo verbose message here,
+ // getNextMarkRegion() does this when it returns NULL.
+ should_continue = false;
+ } else {
+ if (adaptive_young_list_length()) {
+ if (time_remaining_ms < 0.0) {
+ ergo_verbose1(ErgoCSetConstruction,
+ "stop adding old regions to CSet",
+ ergo_format_reason("remaining time is lower than 0")
+ ergo_format_ms("remaining time"),
+ time_remaining_ms);
+ should_continue = false;
+ }
+ } else {
+ if (_collection_set_size < _young_list_fixed_length) {
+ ergo_verbose2(ErgoCSetConstruction,
+ "stop adding old regions to CSet",
+ ergo_format_reason("CSet length lower than target")
+ ergo_format_region("CSet")
+ ergo_format_region("young target"),
+ _collection_set_size, _young_list_fixed_length);
+ should_continue = false;
+ }
+ }
+ }
} while (should_continue);
if (!adaptive_young_list_length() &&
- _collection_set_size < _young_list_fixed_length)
+ _collection_set_size < _young_list_fixed_length) {
+ ergo_verbose2(ErgoCSetConstruction,
+ "request partially-young GCs end",
+ ergo_format_reason("CSet length lower than target")
+ ergo_format_region("CSet")
+ ergo_format_region("young target"),
+ _collection_set_size, _young_list_fixed_length);
_should_revert_to_full_young_gcs = true;
+ }
+
+ old_region_num = _collection_set_size - prev_collection_set_size;
+
+ ergo_verbose2(ErgoCSetConstruction | ErgoHigh,
+ "add old regions to CSet",
+ ergo_format_region("old")
+ ergo_format_ms("predicted old region time"),
+ old_region_num,
+ predicted_pause_time_ms - prev_predicted_pause_time_ms);
}
stop_incremental_cset_building();
@@ -2903,6 +3082,16 @@
end_recording_regions();
+ ergo_verbose5(ErgoCSetConstruction,
+ "finish choosing CSet",
+ ergo_format_region("eden")
+ ergo_format_region("survivors")
+ ergo_format_region("old")
+ ergo_format_ms("predicted pause time")
+ ergo_format_ms("target pause time"),
+ eden_region_num, survivor_region_num, old_region_num,
+ predicted_pause_time_ms, target_pause_time_ms);
+
double non_young_end_time_sec = os::elapsedTime();
_recorded_non_young_cset_choice_time_ms =
(non_young_end_time_sec - non_young_start_time_sec) * 1000.0;
@@ -2914,12 +3103,6 @@
}
void G1CollectorPolicy_BestRegionsFirst::
-expand_if_possible(size_t numRegions) {
- size_t expansion_bytes = numRegions * HeapRegion::GrainBytes;
- _g1->expand(expansion_bytes);
-}
-
-void G1CollectorPolicy_BestRegionsFirst::
record_collection_pause_end() {
G1CollectorPolicy::record_collection_pause_end();
assert(assertMarkedBytesDataOK(), "Marked regions not OK at pause end.");
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp Fri Sep 09 14:44:43 2011 +0200
@@ -183,9 +183,9 @@
// if true, then it tries to dynamically adjust the length of the
// young list
bool _adaptive_young_list_length;
- size_t _young_list_min_length;
size_t _young_list_target_length;
size_t _young_list_fixed_length;
+ size_t _prev_eden_capacity; // used for logging
// The max number of regions we can extend the eden by while the GC
// locker is active. This should be >= _young_list_target_length;
@@ -207,6 +207,9 @@
double _gc_overhead_perc;
+ double _reserve_factor;
+ size_t _reserve_regions;
+
bool during_marking() {
return _during_marking;
}
@@ -243,6 +246,10 @@
TruncatedSeq* _max_conc_overhead_seq;
+ bool _using_new_ratio_calculations;
+ size_t _min_desired_young_length; // as set on the command line or default calculations
+ size_t _max_desired_young_length; // as set on the command line or default calculations
+
size_t _recorded_young_regions;
size_t _recorded_non_young_regions;
size_t _recorded_region_num;
@@ -456,12 +463,6 @@
size_t predict_bytes_to_copy(HeapRegion* hr);
double predict_region_elapsed_time_ms(HeapRegion* hr, bool young);
- // for use by: calculate_young_list_target_length(rs_length)
- bool predict_will_fit(size_t young_region_num,
- double base_time_ms,
- size_t init_free_regions,
- double target_pause_time_ms);
-
void start_recording_regions();
void record_cset_region_info(HeapRegion* hr, bool young);
void record_non_young_cset_region(HeapRegion* hr);
@@ -493,7 +494,6 @@
// </NEW PREDICTION>
-public:
void cset_regions_freed() {
bool propagate = _last_young_gc_full && !_in_marking_window;
_short_lived_surv_rate_group->all_surviving_words_recorded(propagate);
@@ -772,9 +772,41 @@
double _mark_cleanup_start_sec;
double _mark_closure_time_ms;
- void calculate_young_list_min_length();
- void calculate_young_list_target_length();
- void calculate_young_list_target_length(size_t rs_lengths);
+ // Update the young list target length either by setting it to the
+ // desired fixed value or by calculating it using G1's pause
+ // prediction model. If no rs_lengths parameter is passed, predict
+ // the RS lengths using the prediction model, otherwise use the
+ // given rs_lengths as the prediction.
+ void update_young_list_target_length(size_t rs_lengths = (size_t) -1);
+
+ // Calculate and return the minimum desired young list target
+ // length. This is the minimum desired young list length according
+ // to the user's inputs.
+ size_t calculate_young_list_desired_min_length(size_t base_min_length);
+
+ // Calculate and return the maximum desired young list target
+ // length. This is the maximum desired young list length according
+ // to the user's inputs.
+ size_t calculate_young_list_desired_max_length();
+
+ // Calculate and return the maximum young list target length that
+ // can fit into the pause time goal. The parameters are: rs_lengths
+ // represent the prediction of how large the young RSet lengths will
+ // be, base_min_length is the alreay existing number of regions in
+ // the young list, min_length and max_length are the desired min and
+ // max young list length according to the user's inputs.
+ size_t calculate_young_list_target_length(size_t rs_lengths,
+ size_t base_min_length,
+ size_t desired_min_length,
+ size_t desired_max_length);
+
+ // 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
+ // given free space (expressed by base_free_regions). It is used by
+ // calculate_young_list_target_length().
+ bool predict_will_fit(size_t young_length, double base_time_ms,
+ size_t base_free_regions, double target_pause_time_ms);
public:
@@ -786,7 +818,10 @@
return CollectorPolicy::G1CollectorPolicyKind;
}
- void check_prediction_validity();
+ // Check the current value of the young list RSet lengths and
+ // compare it against the last prediction. If the current value is
+ // higher, recalculate the young list target length prediction.
+ void revise_young_list_target_length_if_necessary();
size_t bytes_in_collection_set() {
return _bytes_in_collection_set_before_gc;
@@ -796,6 +831,9 @@
return _all_pause_times_ms->num() + 1;
}
+ // This should be called after the heap is resized.
+ void record_new_heap_size(size_t new_number_of_regions);
+
protected:
// Count the number of bytes used in the CS.
@@ -807,6 +845,8 @@
size_t max_live_bytes);
void record_concurrent_mark_cleanup_end_work2();
+ void update_young_list_size_using_newratio(size_t number_of_heap_regions);
+
public:
virtual void init();
@@ -1045,7 +1085,7 @@
// new cycle, as long as we are not already in one. It's best if it
// is called during a safepoint when the test whether a cycle is in
// progress or not is stable.
- bool force_initial_mark_if_outside_cycle();
+ bool force_initial_mark_if_outside_cycle(GCCause::Cause gc_cause);
// This is called at the very beginning of an evacuation pause (it
// has to be the first thing that the pause does). If
@@ -1204,10 +1244,10 @@
_survivors_age_table.merge_par(age_table);
}
- void calculate_max_gc_locker_expansion();
+ void update_max_gc_locker_expansion();
// Calculates survivor space parameters.
- void calculate_survivors_policy();
+ void update_survivors_policy();
};
@@ -1234,8 +1274,6 @@
class G1CollectorPolicy_BestRegionsFirst: public G1CollectorPolicy {
CollectionSetChooser* _collectionSetChooser;
- // If the estimated is less then desirable, resize if possible.
- void expand_if_possible(size_t numRegions);
virtual void choose_collection_set(double target_pause_time_ms);
virtual void record_collection_pause_start(double start_time_sec,
@@ -1269,8 +1307,4 @@
return (sum_of_squares - 2.0 * avg * sum + n_d * avg * avg) / n_d;
}
-// Local Variables: ***
-// c-indentation-style: gnu ***
-// End: ***
-
#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1COLLECTORPOLICY_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1ErgoVerbose.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2011, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc_implementation/g1/g1ErgoVerbose.hpp"
+#include "utilities/ostream.hpp"
+
+ErgoLevel G1ErgoVerbose::_level;
+bool G1ErgoVerbose::_enabled[ErgoHeuristicNum];
+
+void G1ErgoVerbose::initialize() {
+ set_level(ErgoLow);
+ set_enabled(false);
+}
+
+void G1ErgoVerbose::set_level(ErgoLevel level) {
+ _level = level;
+}
+
+void G1ErgoVerbose::set_enabled(ErgoHeuristic n, bool enabled) {
+ assert(0 <= n && n < ErgoHeuristicNum, "pre-condition");
+ _enabled[n] = enabled;
+}
+
+void G1ErgoVerbose::set_enabled(bool enabled) {
+ for (int n = 0; n < ErgoHeuristicNum; n += 1) {
+ set_enabled((ErgoHeuristic) n, enabled);
+ }
+}
+
+const char* G1ErgoVerbose::to_string(int tag) {
+ ErgoHeuristic n = extract_heuristic(tag);
+ switch (n) {
+ case ErgoHeapSizing: return "Heap Sizing";
+ case ErgoCSetConstruction: return "CSet Construction";
+ case ErgoConcCycles: return "Concurrent Cycles";
+ case ErgoPartiallyYoungGCs: return "Partially-Young GCs";
+ default:
+ ShouldNotReachHere();
+ // Keep the Windows compiler happy
+ return NULL;
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1ErgoVerbose.hpp Fri Sep 09 14:44:43 2011 +0200
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2011, 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_VM_GC_IMPLEMENTATION_G1_G1ERGOVERBOSE_HPP
+#define SHARE_VM_GC_IMPLEMENTATION_G1_G1ERGOVERBOSE_HPP
+
+#include "memory/allocation.hpp"
+#include "utilities/debug.hpp"
+
+// The log of G1's heuristic decisions comprises of a series of
+// records which have a similar format in order to maintain
+// consistency across records and ultimately easier parsing of the
+// output, if we ever choose to do that. Each record consists of:
+// * A time stamp to be able to easily correlate each record with
+// other events.
+// * A unique string to allow us to easily identify such records.
+// * The name of the heuristic the record corresponds to.
+// * An action string which describes the action that G1 did or is
+// about to do.
+// * An optional reason string which describes the reason for the
+// action.
+// * An optional number of name/value pairs which contributed to the
+// decision to take the action described in the record.
+//
+// Each record is associated with a "tag" which is the combination of
+// the heuristic the record corresponds to, as well as the min level
+// of verboseness at which the record should be printed. The tag is
+// checked against the current settings to determine whether the record
+// should be printed or not.
+
+// The available verboseness levels.
+typedef enum {
+ // Determine which part of the tag is occupied by the level.
+ ErgoLevelShift = 8,
+ ErgoLevelMask = ~((1 << ErgoLevelShift) - 1),
+
+ // ErgoLow is 0 so that we don't have to explicitly or a heuristic
+ // id with ErgoLow to keep its use simpler.
+ ErgoLow = 0,
+ ErgoHigh = 1 << ErgoLevelShift,
+} ErgoLevel;
+
+// The available heuristics.
+typedef enum {
+ // Determines which part of the tag is occupied by the heuristic id.
+ ErgoHeuristicMask = ~ErgoLevelMask,
+
+ ErgoHeapSizing = 0,
+ ErgoCSetConstruction,
+ ErgoConcCycles,
+ ErgoPartiallyYoungGCs,
+
+ ErgoHeuristicNum
+} ErgoHeuristic;
+
+class G1ErgoVerbose : AllStatic {
+private:
+ // Determines the minimum verboseness level at which records will be
+ // printed.
+ static ErgoLevel _level;
+ // Determines which heuristics are currently enabled.
+ static bool _enabled[ErgoHeuristicNum];
+
+ static ErgoLevel extract_level(int tag) {
+ return (ErgoLevel) (tag & ErgoLevelMask);
+ }
+
+ static ErgoHeuristic extract_heuristic(int tag) {
+ return (ErgoHeuristic) (tag & ErgoHeuristicMask);
+ }
+
+public:
+ // Needs to be explicitly called at GC initialization.
+ static void initialize();
+
+ static void set_level(ErgoLevel level);
+ static void set_enabled(ErgoHeuristic h, bool enabled);
+ // It is applied to all heuristics.
+ static void set_enabled(bool enabled);
+
+ static bool enabled(int tag) {
+ ErgoLevel level = extract_level(tag);
+ ErgoHeuristic n = extract_heuristic(tag);
+ return level <= _level && _enabled[n];
+ }
+
+ // Extract the heuristic id from the tag and return a string with
+ // its name.
+ static const char* to_string(int tag);
+};
+
+// The macros below generate the format string for values of different
+// types and/or metrics.
+
+// The reason for the action is optional and is handled specially: the
+// reason string is concatenated here so it's not necessary to pass it
+// as a parameter.
+#define ergo_format_reason(_reason_) ", reason: " _reason_
+
+// Single parameter format strings
+#define ergo_format_str(_name_) ", " _name_ ": %s"
+#define ergo_format_region(_name_) ", " _name_ ": "SIZE_FORMAT" regions"
+#define ergo_format_byte(_name_) ", " _name_ ": "SIZE_FORMAT" bytes"
+#define ergo_format_double(_name_) ", " _name_ ": %1.2f"
+#define ergo_format_perc(_name_) ", " _name_ ": %1.2f %%"
+#define ergo_format_ms(_name_) ", " _name_ ": %1.2f ms"
+
+// Double parameter format strings
+#define ergo_format_byte_perc(_name_) \
+ ", " _name_ ": "SIZE_FORMAT" bytes (%1.2f %%)"
+
+// Generates the format string
+#define ergo_format(_action_, _extra_format_) \
+ " %1.3f: [G1Ergonomics (%s) " _action_ _extra_format_ "]"
+
+// Conditionally, prints an ergonomic decision record. _extra_format_
+// is the format string for the optional items we'd like to print
+// (i.e., the decision's reason and any associated values). This
+// string should be built up using the ergo_*_format macros (see
+// above) to ensure consistency.
+//
+// Since we cannot rely on the compiler supporting variable argument
+// macros, this macro accepts a fixed number of arguments and passes
+// them to the print method. For convenience, we have wrapper macros
+// below which take a specific number of arguments and set the rest to
+// a default value.
+#define ergo_verbose_common(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_, _arg2_, _arg3_, _arg4_, _arg5_) \
+ do { \
+ if (G1ErgoVerbose::enabled((_tag_))) { \
+ gclog_or_tty->print_cr(ergo_format(_action_, _extra_format_), \
+ os::elapsedTime(), \
+ G1ErgoVerbose::to_string((_tag_)), \
+ (_arg0_), (_arg1_), (_arg2_), \
+ (_arg3_), (_arg4_), (_arg5_)); \
+ } \
+ } while (0)
+
+
+#define ergo_verbose(_tag_, _action_) \
+ ergo_verbose_common(_tag_, _action_, "", 0, 0, 0, 0, 0, 0)
+
+#define ergo_verbose0(_tag_, _action_, _extra_format_) \
+ ergo_verbose_common(_tag_, _action_, _extra_format_, 0, 0, 0, 0, 0, 0)
+
+#define ergo_verbose1(_tag_, _action_, _extra_format_, \
+ _arg0_) \
+ ergo_verbose_common(_tag_, _action_, _extra_format_, \
+ _arg0_, 0, 0, 0, 0, 0)
+
+#define ergo_verbose2(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_) \
+ ergo_verbose_common(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_, 0, 0, 0, 0)
+
+#define ergo_verbose3(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_, _arg2_) \
+ ergo_verbose_common(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_, _arg2_, 0, 0, 0)
+
+#define ergo_verbose4(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_, _arg2_, _arg3_) \
+ ergo_verbose_common(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_, _arg2_, _arg3_, 0, 0)
+
+#define ergo_verbose5(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_, _arg2_, _arg3_, _arg4_) \
+ ergo_verbose_common(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_, _arg2_, _arg3_, _arg4_, 0)
+
+#define ergo_verbose6(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_, _arg2_, _arg3_, _arg4_, _arg5_) \
+ ergo_verbose_common(_tag_, _action_, _extra_format_, \
+ _arg0_, _arg1_, _arg2_, _arg3_, _arg4_, _arg5_)
+
+#endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1ERGOVERBOSE_HPP
--- a/hotspot/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1MMUTracker.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2011, 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
@@ -97,10 +97,6 @@
// or performance (we are GC'ing most of the time anyway!),
// simply overwrite the oldest entry in the tracker.
- if (G1PolicyVerbose > 1) {
- warning("MMU Tracker Queue overflow. Replacing earliest entry.");
- }
-
_head_index = trim_index(_head_index + 1);
assert(_head_index == _tail_index, "Because we have a full circular buffer");
_tail_index = trim_index(_tail_index + 1);
--- a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp Fri Sep 09 14:44:43 2011 +0200
@@ -134,9 +134,9 @@
develop(bool, G1RSCountHisto, false, \
"If true, print a histogram of RS occupancies after each pause") \
\
- product(bool, G1PrintRegionLivenessInfo, false, \
- "Prints the liveness information for all regions in the heap " \
- "at the end of a marking cycle.") \
+ diagnostic(bool, G1PrintRegionLivenessInfo, false, \
+ "Prints the liveness information for all regions in the heap " \
+ "at the end of a marking cycle.") \
\
develop(bool, G1PrintParCleanupStats, false, \
"When true, print extra stats about parallel cleanup.") \
@@ -228,7 +228,7 @@
"the number of regions for which we'll print a surv rate " \
"summary.") \
\
- product(intx, G1ReservePercent, 10, \
+ product(uintx, G1ReservePercent, 10, \
"It determines the minimum reserve we should have in the heap " \
"to minimize the probability of promotion failure.") \
\
--- a/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -98,7 +98,7 @@
// At this point we are supposed to start a concurrent cycle. We
// will do so if one is not already in progress.
- bool res = g1h->g1_policy()->force_initial_mark_if_outside_cycle();
+ bool res = g1h->g1_policy()->force_initial_mark_if_outside_cycle(_gc_cause);
// The above routine returns true if we were able to force the
// next GC pause to be an initial mark; it returns false if a
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -909,10 +909,6 @@
}
young_gen()->verify(allow_dirty);
}
- if (!silent) {
- gclog_or_tty->print("ref_proc ");
- }
- ReferenceProcessor::verify();
}
void ParallelScavengeHeap::print_heap_change(size_t prev_used) {
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -80,10 +80,6 @@
Universe::oops_do(&mark_and_push_closure);
break;
- case reference_processing:
- ReferenceProcessor::oops_do(&mark_and_push_closure);
- break;
-
case jni_handles:
JNIHandles::oops_do(&mark_and_push_closure);
break;
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.hpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/pcTasks.hpp Fri Sep 09 14:44:43 2011 +0200
@@ -98,8 +98,7 @@
management = 6,
jvmti = 7,
system_dictionary = 8,
- reference_processing = 9,
- code_cache = 10
+ code_cache = 9
};
private:
RootType _root_type;
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -516,7 +516,6 @@
{
ParallelScavengeHeap::ParStrongRootsScope psrs;
Universe::oops_do(mark_and_push_closure());
- ReferenceProcessor::oops_do(mark_and_push_closure());
JNIHandles::oops_do(mark_and_push_closure()); // Global (strong) JNI handles
CodeBlobToOopClosure each_active_code_blob(mark_and_push_closure(), /*do_marking=*/ true);
Threads::oops_do(mark_and_push_closure(), &each_active_code_blob);
@@ -623,7 +622,6 @@
// General strong roots.
Universe::oops_do(adjust_root_pointer_closure());
- ReferenceProcessor::oops_do(adjust_root_pointer_closure());
JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles
Threads::oops_do(adjust_root_pointer_closure(), NULL);
ObjectSynchronizer::oops_do(adjust_root_pointer_closure());
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -2445,7 +2445,6 @@
// General strong roots.
Universe::oops_do(adjust_root_pointer_closure());
- ReferenceProcessor::oops_do(adjust_root_pointer_closure());
JNIHandles::oops_do(adjust_root_pointer_closure()); // Global (strong) JNI handles
Threads::oops_do(adjust_root_pointer_closure(), NULL);
ObjectSynchronizer::oops_do(adjust_root_pointer_closure());
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -55,7 +55,6 @@
switch (_root_type) {
case universe:
Universe::oops_do(&roots_closure);
- ReferenceProcessor::oops_do(&roots_closure);
break;
case jni_handles:
--- a/hotspot/src/share/vm/memory/genCollectedHeap.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/memory/genCollectedHeap.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -1269,10 +1269,6 @@
gclog_or_tty->print("remset ");
}
rem_set()->verify();
- if (!silent) {
- gclog_or_tty->print("ref_proc ");
- }
- ReferenceProcessor::verify();
}
void GenCollectedHeap::print() const { print_on(tty); }
--- a/hotspot/src/share/vm/memory/referenceProcessor.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/memory/referenceProcessor.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -35,15 +35,15 @@
ReferencePolicy* ReferenceProcessor::_always_clear_soft_ref_policy = NULL;
ReferencePolicy* ReferenceProcessor::_default_soft_ref_policy = NULL;
-oop ReferenceProcessor::_sentinelRef = NULL;
const int subclasses_of_ref = REF_PHANTOM - REF_OTHER;
+bool ReferenceProcessor::_pending_list_uses_discovered_field = false;
// List of discovered references.
class DiscoveredList {
public:
DiscoveredList() : _len(0), _compressed_head(0), _oop_head(NULL) { }
oop head() const {
- return UseCompressedOops ? oopDesc::decode_heap_oop_not_null(_compressed_head) :
+ return UseCompressedOops ? oopDesc::decode_heap_oop(_compressed_head) :
_oop_head;
}
HeapWord* adr_head() {
@@ -53,12 +53,12 @@
void set_head(oop o) {
if (UseCompressedOops) {
// Must compress the head ptr.
- _compressed_head = oopDesc::encode_heap_oop_not_null(o);
+ _compressed_head = oopDesc::encode_heap_oop(o);
} else {
_oop_head = o;
}
}
- bool empty() const { return head() == ReferenceProcessor::sentinel_ref(); }
+ bool empty() const { return head() == NULL; }
size_t length() { return _len; }
void set_length(size_t len) { _len = len; }
void inc_length(size_t inc) { _len += inc; assert(_len > 0, "Error"); }
@@ -76,21 +76,9 @@
}
void ReferenceProcessor::init_statics() {
- assert(_sentinelRef == NULL, "should be initialized precisely once");
- EXCEPTION_MARK;
- _sentinelRef = instanceKlass::cast(
- SystemDictionary::Reference_klass())->
- allocate_permanent_instance(THREAD);
-
// Initialize the master soft ref clock.
java_lang_ref_SoftReference::set_clock(os::javaTimeMillis());
- if (HAS_PENDING_EXCEPTION) {
- Handle ex(THREAD, PENDING_EXCEPTION);
- vm_exit_during_initialization(ex);
- }
- assert(_sentinelRef != NULL && _sentinelRef->is_oop(),
- "Just constructed it!");
_always_clear_soft_ref_policy = new AlwaysClearPolicy();
_default_soft_ref_policy = new COMPILER2_PRESENT(LRUMaxHeapPolicy())
NOT_COMPILER2(LRUCurrentHeapPolicy());
@@ -100,6 +88,7 @@
guarantee(RefDiscoveryPolicy == ReferenceBasedDiscovery ||
RefDiscoveryPolicy == ReferentBasedDiscovery,
"Unrecongnized RefDiscoveryPolicy");
+ _pending_list_uses_discovered_field = JDK_Version::current().pending_list_uses_discovered_field();
}
ReferenceProcessor::ReferenceProcessor(MemRegion span,
@@ -130,13 +119,12 @@
_discoveredWeakRefs = &_discoveredSoftRefs[_max_num_q];
_discoveredFinalRefs = &_discoveredWeakRefs[_max_num_q];
_discoveredPhantomRefs = &_discoveredFinalRefs[_max_num_q];
- assert(sentinel_ref() != NULL, "_sentinelRef is NULL");
- // Initialized all entries to _sentinelRef
+ // Initialized all entries to NULL
for (int i = 0; i < _max_num_q * subclasses_of_ref; i++) {
- _discoveredSoftRefs[i].set_head(sentinel_ref());
+ _discoveredSoftRefs[i].set_head(NULL);
_discoveredSoftRefs[i].set_length(0);
}
- // If we do barreirs, cache a copy of the barrier set.
+ // If we do barriers, cache a copy of the barrier set.
if (discovered_list_needs_barrier) {
_bs = Universe::heap()->barrier_set();
}
@@ -167,10 +155,6 @@
}
}
-void ReferenceProcessor::oops_do(OopClosure* f) {
- f->do_oop(adr_sentinel_ref());
-}
-
void ReferenceProcessor::update_soft_ref_master_clock() {
// Update (advance) the soft ref master clock field. This must be done
// after processing the soft ref list.
@@ -283,8 +267,6 @@
}
#endif
JNIHandles::weak_oops_do(is_alive, keep_alive);
- // Finally remember to keep sentinel around
- keep_alive->do_oop(adr_sentinel_ref());
complete_gc->do_void();
}
@@ -327,46 +309,77 @@
void ReferenceProcessor::enqueue_discovered_reflist(DiscoveredList& refs_list,
HeapWord* pending_list_addr) {
// Given a list of refs linked through the "discovered" field
- // (java.lang.ref.Reference.discovered) chain them through the
- // "next" field (java.lang.ref.Reference.next) and prepend
- // to the pending list.
+ // (java.lang.ref.Reference.discovered), self-loop their "next" field
+ // thus distinguishing them from active References, then
+ // prepend them to the pending list.
+ // BKWRD COMPATIBILITY NOTE: For older JDKs (prior to the fix for 4956777),
+ // the "next" field is used to chain the pending list, not the discovered
+ // field.
+
if (TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr("ReferenceProcessor::enqueue_discovered_reflist list "
INTPTR_FORMAT, (address)refs_list.head());
}
- oop obj = refs_list.head();
- // Walk down the list, copying the discovered field into
- // the next field and clearing it (except for the last
- // non-sentinel object which is treated specially to avoid
- // confusion with an active reference).
- while (obj != sentinel_ref()) {
- assert(obj->is_instanceRef(), "should be reference object");
- oop next = java_lang_ref_Reference::discovered(obj);
- if (TraceReferenceGC && PrintGCDetails) {
- gclog_or_tty->print_cr(" obj " INTPTR_FORMAT "/next " INTPTR_FORMAT,
- obj, next);
+
+ oop obj = NULL;
+ oop next_d = refs_list.head();
+ if (pending_list_uses_discovered_field()) { // New behaviour
+ // Walk down the list, self-looping the next field
+ // so that the References are not considered active.
+ while (obj != next_d) {
+ obj = next_d;
+ assert(obj->is_instanceRef(), "should be reference object");
+ next_d = java_lang_ref_Reference::discovered(obj);
+ if (TraceReferenceGC && PrintGCDetails) {
+ gclog_or_tty->print_cr(" obj " INTPTR_FORMAT "/next_d " INTPTR_FORMAT,
+ obj, next_d);
+ }
+ assert(java_lang_ref_Reference::next(obj) == NULL,
+ "Reference not active; should not be discovered");
+ // Self-loop next, so as to make Ref not active.
+ java_lang_ref_Reference::set_next(obj, obj);
+ if (next_d == obj) { // obj is last
+ // Swap refs_list into pendling_list_addr and
+ // set obj's discovered to what we read from pending_list_addr.
+ oop old = oopDesc::atomic_exchange_oop(refs_list.head(), pending_list_addr);
+ // Need oop_check on pending_list_addr above;
+ // see special oop-check code at the end of
+ // enqueue_discovered_reflists() further below.
+ java_lang_ref_Reference::set_discovered(obj, old); // old may be NULL
+ }
}
- assert(java_lang_ref_Reference::next(obj) == NULL,
- "The reference should not be enqueued");
- if (next == sentinel_ref()) { // obj is last
- // Swap refs_list into pendling_list_addr and
- // set obj's next to what we read from pending_list_addr.
- oop old = oopDesc::atomic_exchange_oop(refs_list.head(), pending_list_addr);
- // Need oop_check on pending_list_addr above;
- // see special oop-check code at the end of
- // enqueue_discovered_reflists() further below.
- if (old == NULL) {
- // obj should be made to point to itself, since
- // pending list was empty.
- java_lang_ref_Reference::set_next(obj, obj);
+ } else { // Old behaviour
+ // Walk down the list, copying the discovered field into
+ // the next field and clearing the discovered field.
+ while (obj != next_d) {
+ obj = next_d;
+ assert(obj->is_instanceRef(), "should be reference object");
+ next_d = java_lang_ref_Reference::discovered(obj);
+ if (TraceReferenceGC && PrintGCDetails) {
+ gclog_or_tty->print_cr(" obj " INTPTR_FORMAT "/next_d " INTPTR_FORMAT,
+ obj, next_d);
+ }
+ assert(java_lang_ref_Reference::next(obj) == NULL,
+ "The reference should not be enqueued");
+ if (next_d == obj) { // obj is last
+ // Swap refs_list into pendling_list_addr and
+ // set obj's next to what we read from pending_list_addr.
+ oop old = oopDesc::atomic_exchange_oop(refs_list.head(), pending_list_addr);
+ // Need oop_check on pending_list_addr above;
+ // see special oop-check code at the end of
+ // enqueue_discovered_reflists() further below.
+ if (old == NULL) {
+ // obj should be made to point to itself, since
+ // pending list was empty.
+ java_lang_ref_Reference::set_next(obj, obj);
+ } else {
+ java_lang_ref_Reference::set_next(obj, old);
+ }
} else {
- java_lang_ref_Reference::set_next(obj, old);
+ java_lang_ref_Reference::set_next(obj, next_d);
}
- } else {
- java_lang_ref_Reference::set_next(obj, next);
+ java_lang_ref_Reference::set_discovered(obj, (oop) NULL);
}
- java_lang_ref_Reference::set_discovered(obj, (oop) NULL);
- obj = next;
}
}
@@ -376,10 +389,9 @@
RefProcEnqueueTask(ReferenceProcessor& ref_processor,
DiscoveredList discovered_refs[],
HeapWord* pending_list_addr,
- oop sentinel_ref,
int n_queues)
: EnqueueTask(ref_processor, discovered_refs,
- pending_list_addr, sentinel_ref, n_queues)
+ pending_list_addr, n_queues)
{ }
virtual void work(unsigned int work_id) {
@@ -396,7 +408,7 @@
j++, index += _n_queues) {
_ref_processor.enqueue_discovered_reflist(
_refs_lists[index], _pending_list_addr);
- _refs_lists[index].set_head(_sentinel_ref);
+ _refs_lists[index].set_head(NULL);
_refs_lists[index].set_length(0);
}
}
@@ -408,13 +420,13 @@
if (_processing_is_mt && task_executor != NULL) {
// Parallel code
RefProcEnqueueTask tsk(*this, _discoveredSoftRefs,
- pending_list_addr, sentinel_ref(), _max_num_q);
+ pending_list_addr, _max_num_q);
task_executor->execute(tsk);
} else {
// Serial code: call the parent class's implementation
for (int i = 0; i < _max_num_q * subclasses_of_ref; i++) {
enqueue_discovered_reflist(_discoveredSoftRefs[i], pending_list_addr);
- _discoveredSoftRefs[i].set_head(sentinel_ref());
+ _discoveredSoftRefs[i].set_head(NULL);
_discoveredSoftRefs[i].set_length(0);
}
}
@@ -428,7 +440,7 @@
BoolObjectClosure* is_alive);
// End Of List.
- inline bool has_next() const { return _next != ReferenceProcessor::sentinel_ref(); }
+ inline bool has_next() const { return _ref != NULL; }
// Get oop to the Reference object.
inline oop obj() const { return _ref; }
@@ -468,9 +480,13 @@
inline void update_discovered() {
// First _prev_next ref actually points into DiscoveredList (gross).
if (UseCompressedOops) {
- _keep_alive->do_oop((narrowOop*)_prev_next);
+ if (!oopDesc::is_null(*(narrowOop*)_prev_next)) {
+ _keep_alive->do_oop((narrowOop*)_prev_next);
+ }
} else {
- _keep_alive->do_oop((oop*)_prev_next);
+ if (!oopDesc::is_null(*(oop*)_prev_next)) {
+ _keep_alive->do_oop((oop*)_prev_next);
+ }
}
}
@@ -488,6 +504,7 @@
private:
DiscoveredList& _refs_list;
HeapWord* _prev_next;
+ oop _prev;
oop _ref;
HeapWord* _discovered_addr;
oop _next;
@@ -509,6 +526,7 @@
BoolObjectClosure* is_alive)
: _refs_list(refs_list),
_prev_next(refs_list.adr_head()),
+ _prev(NULL),
_ref(refs_list.head()),
#ifdef ASSERT
_first_seen(refs_list.head()),
@@ -517,7 +535,7 @@
_processed(0),
_removed(0),
#endif
- _next(refs_list.head()),
+ _next(NULL),
_keep_alive(keep_alive),
_is_alive(is_alive)
{ }
@@ -544,26 +562,43 @@
inline void DiscoveredListIterator::next() {
_prev_next = _discovered_addr;
+ _prev = _ref;
move_to_next();
}
inline void DiscoveredListIterator::remove() {
assert(_ref->is_oop(), "Dropping a bad reference");
oop_store_raw(_discovered_addr, NULL);
+
// First _prev_next ref actually points into DiscoveredList (gross).
+ oop new_next;
+ if (_next == _ref) {
+ // At the end of the list, we should make _prev point to itself.
+ // If _ref is the first ref, then _prev_next will be in the DiscoveredList,
+ // and _prev will be NULL.
+ new_next = _prev;
+ } else {
+ new_next = _next;
+ }
+
if (UseCompressedOops) {
// Remove Reference object from list.
- oopDesc::encode_store_heap_oop_not_null((narrowOop*)_prev_next, _next);
+ oopDesc::encode_store_heap_oop((narrowOop*)_prev_next, new_next);
} else {
// Remove Reference object from list.
- oopDesc::store_heap_oop((oop*)_prev_next, _next);
+ oopDesc::store_heap_oop((oop*)_prev_next, new_next);
}
NOT_PRODUCT(_removed++);
_refs_list.dec_length(1);
}
inline void DiscoveredListIterator::move_to_next() {
- _ref = _next;
+ if (_ref == _next) {
+ // End of the list.
+ _ref = NULL;
+ } else {
+ _ref = _next;
+ }
assert(_ref != _first_seen, "cyclic ref_list found");
NOT_PRODUCT(_processed++);
}
@@ -613,7 +648,7 @@
NOT_PRODUCT(
if (PrintGCDetails && TraceReferenceGC) {
gclog_or_tty->print_cr(" Dropped %d dead Refs out of %d "
- "discovered Refs by policy list " INTPTR_FORMAT,
+ "discovered Refs by policy, from list " INTPTR_FORMAT,
iter.removed(), iter.processed(), (address)refs_list.head());
}
)
@@ -725,24 +760,30 @@
assert(iter.obj()->is_oop(UseConcMarkSweepGC), "Adding a bad reference");
iter.next();
}
- // Remember to keep sentinel pointer around
+ // Remember to update the next pointer of the last ref.
iter.update_discovered();
// Close the reachable set
complete_gc->do_void();
}
void
-ReferenceProcessor::abandon_partial_discovered_list(DiscoveredList& refs_list) {
- oop obj = refs_list.head();
- while (obj != sentinel_ref()) {
- oop discovered = java_lang_ref_Reference::discovered(obj);
+ReferenceProcessor::clear_discovered_references(DiscoveredList& refs_list) {
+ oop obj = NULL;
+ oop next = refs_list.head();
+ while (next != obj) {
+ obj = next;
+ next = java_lang_ref_Reference::discovered(obj);
java_lang_ref_Reference::set_discovered_raw(obj, NULL);
- obj = discovered;
}
- refs_list.set_head(sentinel_ref());
+ refs_list.set_head(NULL);
refs_list.set_length(0);
}
+void
+ReferenceProcessor::abandon_partial_discovered_list(DiscoveredList& refs_list) {
+ clear_discovered_references(refs_list);
+}
+
void ReferenceProcessor::abandon_partial_discovery() {
// loop over the lists
for (int i = 0; i < _max_num_q * subclasses_of_ref; i++) {
@@ -859,6 +900,9 @@
refs_to_move = MIN2(ref_lists[from_idx].length() - avg_refs,
avg_refs - ref_lists[to_idx].length());
}
+
+ assert(refs_to_move > 0, "otherwise the code below will fail");
+
oop move_head = ref_lists[from_idx].head();
oop move_tail = move_head;
oop new_head = move_head;
@@ -867,10 +911,24 @@
move_tail = new_head;
new_head = java_lang_ref_Reference::discovered(new_head);
}
- java_lang_ref_Reference::set_discovered(move_tail, ref_lists[to_idx].head());
+
+ // Add the chain to the to list.
+ if (ref_lists[to_idx].head() == NULL) {
+ // to list is empty. Make a loop at the end.
+ java_lang_ref_Reference::set_discovered(move_tail, move_tail);
+ } else {
+ java_lang_ref_Reference::set_discovered(move_tail, ref_lists[to_idx].head());
+ }
ref_lists[to_idx].set_head(move_head);
ref_lists[to_idx].inc_length(refs_to_move);
- ref_lists[from_idx].set_head(new_head);
+
+ // Remove the chain from the from list.
+ if (move_tail == new_head) {
+ // We found the end of the from list.
+ ref_lists[from_idx].set_head(NULL);
+ } else {
+ ref_lists[from_idx].set_head(new_head);
+ }
ref_lists[from_idx].dec_length(refs_to_move);
if (ref_lists[from_idx].length() == 0) {
break;
@@ -1082,42 +1140,40 @@
// First we must make sure this object is only enqueued once. CAS in a non null
// discovered_addr.
oop current_head = refs_list.head();
+ // The last ref must have its discovered field pointing to itself.
+ oop next_discovered = (current_head != NULL) ? current_head : obj;
// Note: In the case of G1, this specific pre-barrier is strictly
// not necessary because the only case we are interested in
// here is when *discovered_addr is NULL (see the CAS further below),
// so this will expand to nothing. As a result, we have manually
// elided this out for G1, but left in the test for some future
- // collector that might have need for a pre-barrier here.
- if (_discovered_list_needs_barrier && !UseG1GC) {
- if (UseCompressedOops) {
- _bs->write_ref_field_pre((narrowOop*)discovered_addr, current_head);
- } else {
- _bs->write_ref_field_pre((oop*)discovered_addr, current_head);
- }
- guarantee(false, "Need to check non-G1 collector");
- }
- oop retest = oopDesc::atomic_compare_exchange_oop(current_head, discovered_addr,
+ // collector that might have need for a pre-barrier here, e.g.:-
+ // _bs->write_ref_field_pre((oop* or narrowOop*)discovered_addr, next_discovered);
+ assert(!_discovered_list_needs_barrier || UseG1GC,
+ "Need to check non-G1 collector: "
+ "may need a pre-write-barrier for CAS from NULL below");
+ oop retest = oopDesc::atomic_compare_exchange_oop(next_discovered, discovered_addr,
NULL);
if (retest == NULL) {
// This thread just won the right to enqueue the object.
- // We have separate lists for enqueueing so no synchronization
+ // We have separate lists for enqueueing, so no synchronization
// is necessary.
refs_list.set_head(obj);
refs_list.inc_length(1);
if (_discovered_list_needs_barrier) {
- _bs->write_ref_field((void*)discovered_addr, current_head);
+ _bs->write_ref_field((void*)discovered_addr, next_discovered);
}
if (TraceReferenceGC) {
- gclog_or_tty->print_cr("Enqueued reference (mt) (" INTPTR_FORMAT ": %s)",
+ gclog_or_tty->print_cr("Discovered reference (mt) (" INTPTR_FORMAT ": %s)",
obj, obj->blueprint()->internal_name());
}
} else {
// If retest was non NULL, another thread beat us to it:
// The reference has already been discovered...
if (TraceReferenceGC) {
- gclog_or_tty->print_cr("Already enqueued reference (" INTPTR_FORMAT ": %s)",
+ gclog_or_tty->print_cr("Already discovered reference (" INTPTR_FORMAT ": %s)",
obj, obj->blueprint()->internal_name());
}
}
@@ -1142,7 +1198,7 @@
// (or part of the heap being collected, indicated by our "span"
// we don't treat it specially (i.e. we scan it as we would
// a normal oop, treating its references as strong references).
-// This means that references can't be enqueued unless their
+// This means that references can't be discovered unless their
// referent is also in the same span. This is the simplest,
// most "local" and most conservative approach, albeit one
// that may cause weak references to be enqueued least promptly.
@@ -1164,14 +1220,13 @@
// and complexity in processing these references.
// We call this choice the "RefeferentBasedDiscovery" policy.
bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) {
- // We enqueue references only if we are discovering refs
- // (rather than processing discovered refs).
+ // Make sure we are discovering refs (rather than processing discovered refs).
if (!_discovering_refs || !RegisterReferences) {
return false;
}
- // We only enqueue active references.
+ // We only discover active references.
oop next = java_lang_ref_Reference::next(obj);
- if (next != NULL) {
+ if (next != NULL) { // Ref is no longer active
return false;
}
@@ -1184,8 +1239,8 @@
return false;
}
- // We only enqueue references whose referents are not (yet) strongly
- // reachable.
+ // We only discover references whose referents are not (yet)
+ // known to be strongly reachable.
if (is_alive_non_header() != NULL) {
verify_referent(obj);
if (is_alive_non_header()->do_object_b(java_lang_ref_Reference::referent(obj))) {
@@ -1211,7 +1266,7 @@
if (discovered != NULL) {
// The reference has already been discovered...
if (TraceReferenceGC) {
- gclog_or_tty->print_cr("Already enqueued reference (" INTPTR_FORMAT ": %s)",
+ gclog_or_tty->print_cr("Already discovered reference (" INTPTR_FORMAT ": %s)",
obj, obj->blueprint()->internal_name());
}
if (RefDiscoveryPolicy == ReferentBasedDiscovery) {
@@ -1233,9 +1288,9 @@
if (RefDiscoveryPolicy == ReferentBasedDiscovery) {
verify_referent(obj);
- // enqueue if and only if either:
- // reference is in our span or
- // we are an atomic collector and referent is in our span
+ // Discover if and only if EITHER:
+ // .. reference is in our span, OR
+ // .. we are an atomic collector and referent is in our span
if (_span.contains(obj_addr) ||
(discovery_is_atomic() &&
_span.contains(java_lang_ref_Reference::referent(obj)))) {
@@ -1262,30 +1317,28 @@
// here: the field will be visited later when processing the discovered
// references.
oop current_head = list->head();
+ // The last ref must have its discovered field pointing to itself.
+ oop next_discovered = (current_head != NULL) ? current_head : obj;
+
// As in the case further above, since we are over-writing a NULL
// pre-value, we can safely elide the pre-barrier here for the case of G1.
+ // e.g.:- _bs->write_ref_field_pre((oop* or narrowOop*)discovered_addr, next_discovered);
assert(discovered == NULL, "control point invariant");
- if (_discovered_list_needs_barrier && !UseG1GC) { // safe to elide for G1
- if (UseCompressedOops) {
- _bs->write_ref_field_pre((narrowOop*)discovered_addr, current_head);
- } else {
- _bs->write_ref_field_pre((oop*)discovered_addr, current_head);
- }
- guarantee(false, "Need to check non-G1 collector");
- }
- oop_store_raw(discovered_addr, current_head);
+ assert(!_discovered_list_needs_barrier || UseG1GC,
+ "For non-G1 collector, may need a pre-write-barrier for CAS from NULL below");
+ oop_store_raw(discovered_addr, next_discovered);
if (_discovered_list_needs_barrier) {
- _bs->write_ref_field((void*)discovered_addr, current_head);
+ _bs->write_ref_field((void*)discovered_addr, next_discovered);
}
list->set_head(obj);
list->inc_length(1);
if (TraceReferenceGC) {
- gclog_or_tty->print_cr("Enqueued reference (" INTPTR_FORMAT ": %s)",
+ gclog_or_tty->print_cr("Discovered reference (" INTPTR_FORMAT ": %s)",
obj, obj->blueprint()->internal_name());
}
}
- assert(obj->is_oop(), "Enqueued a bad reference");
+ assert(obj->is_oop(), "Discovered a bad reference");
verify_referent(obj);
return true;
}
@@ -1437,22 +1490,12 @@
}
#endif
-void ReferenceProcessor::verify() {
- guarantee(sentinel_ref() != NULL && sentinel_ref()->is_oop(), "Lost _sentinelRef");
-}
-
#ifndef PRODUCT
void ReferenceProcessor::clear_discovered_references() {
guarantee(!_discovering_refs, "Discovering refs?");
for (int i = 0; i < _max_num_q * subclasses_of_ref; i++) {
- oop obj = _discoveredSoftRefs[i].head();
- while (obj != sentinel_ref()) {
- oop next = java_lang_ref_Reference::discovered(obj);
- java_lang_ref_Reference::set_discovered(obj, (oop) NULL);
- obj = next;
- }
- _discoveredSoftRefs[i].set_head(sentinel_ref());
- _discoveredSoftRefs[i].set_length(0);
+ clear_discovered_references(_discoveredSoftRefs[i]);
}
}
+
#endif // PRODUCT
--- a/hotspot/src/share/vm/memory/referenceProcessor.hpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/memory/referenceProcessor.hpp Fri Sep 09 14:44:43 2011 +0200
@@ -52,8 +52,8 @@
class ReferenceProcessor : public CHeapObj {
protected:
- // End of list marker
- static oop _sentinelRef;
+ // Compatibility with pre-4965777 JDK's
+ static bool _pending_list_uses_discovered_field;
MemRegion _span; // (right-open) interval of heap
// subject to wkref discovery
bool _discovering_refs; // true when discovery enabled
@@ -106,8 +106,6 @@
int max_num_q() { return _max_num_q; }
void set_active_mt_degree(int v) { _num_q = v; }
DiscoveredList* discovered_soft_refs() { return _discoveredSoftRefs; }
- static oop sentinel_ref() { return _sentinelRef; }
- static oop* adr_sentinel_ref() { return &_sentinelRef; }
ReferencePolicy* setup_policy(bool always_clear) {
_current_soft_ref_policy = always_clear ?
_always_clear_soft_ref_policy : _default_soft_ref_policy;
@@ -115,7 +113,6 @@
return _current_soft_ref_policy;
}
- public:
// Process references with a certain reachability level.
void process_discovered_reflist(DiscoveredList refs_lists[],
ReferencePolicy* policy,
@@ -230,6 +227,7 @@
HeapWord* discovered_addr);
void verify_ok_to_handle_reflists() PRODUCT_RETURN;
+ void clear_discovered_references(DiscoveredList& refs_list);
void abandon_partial_discovered_list(DiscoveredList& refs_list);
// Calculate the number of jni handles.
@@ -300,6 +298,13 @@
bool discovery_is_atomic() const { return _discovery_is_atomic; }
void set_atomic_discovery(bool atomic) { _discovery_is_atomic = atomic; }
+ // whether the JDK in which we are embedded is a pre-4965777 JDK,
+ // and thus whether or not it uses the discovered field to chain
+ // the entries in the pending list.
+ static bool pending_list_uses_discovered_field() {
+ return _pending_list_uses_discovered_field;
+ }
+
// whether discovery is done by multiple threads same-old-timeously
bool discovery_is_mt() const { return _discovery_is_mt; }
void set_mt_discovery(bool mt) { _discovery_is_mt = mt; }
@@ -314,7 +319,6 @@
// iterate over oops
void weak_oops_do(OopClosure* f); // weak roots
- static void oops_do(OopClosure* f); // strong root(s)
// Balance each of the discovered lists.
void balance_all_queues();
@@ -340,7 +344,6 @@
// debugging
void verify_no_references_recorded() PRODUCT_RETURN;
void verify_referent(oop obj) PRODUCT_RETURN;
- static void verify();
// clear the discovered lists (unlinking each entry).
void clear_discovered_references() PRODUCT_RETURN;
@@ -524,12 +527,10 @@
EnqueueTask(ReferenceProcessor& ref_processor,
DiscoveredList refs_lists[],
HeapWord* pending_list_addr,
- oop sentinel_ref,
int n_queues)
: _ref_processor(ref_processor),
_refs_lists(refs_lists),
_pending_list_addr(pending_list_addr),
- _sentinel_ref(sentinel_ref),
_n_queues(n_queues)
{ }
@@ -540,7 +541,6 @@
ReferenceProcessor& _ref_processor;
DiscoveredList* _refs_lists;
HeapWord* _pending_list_addr;
- oop _sentinel_ref;
int _n_queues;
};
--- a/hotspot/src/share/vm/memory/sharedHeap.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/memory/sharedHeap.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -146,7 +146,6 @@
assert(_strong_roots_parity != 0, "must have called prologue code");
if (!_process_strong_tasks->is_task_claimed(SH_PS_Universe_oops_do)) {
Universe::oops_do(roots);
- ReferenceProcessor::oops_do(roots);
// Consider perm-gen discovered lists to be strong.
perm_gen()->ref_processor()->weak_oops_do(roots);
}
--- a/hotspot/src/share/vm/oops/instanceRefKlass.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/oops/instanceRefKlass.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -56,9 +56,8 @@
if (!oopDesc::is_null(heap_oop)) {
oop referent = oopDesc::decode_heap_oop_not_null(heap_oop);
if (!referent->is_gc_marked() &&
- MarkSweep::ref_processor()->
- discover_reference(obj, ref->reference_type())) {
- // reference already enqueued, referent will be traversed later
+ MarkSweep::ref_processor()->discover_reference(obj, ref->reference_type())) {
+ // reference was discovered, referent will be traversed later
ref->instanceKlass::oop_follow_contents(obj);
debug_only(
if(TraceReferenceGC && PrintGCDetails) {
@@ -76,8 +75,34 @@
MarkSweep::mark_and_push(referent_addr);
}
}
- // treat next as normal oop. next is a link in the pending list.
T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj);
+ if (ReferenceProcessor::pending_list_uses_discovered_field()) {
+ // Treat discovered as normal oop, if ref is not "active",
+ // i.e. if next is non-NULL.
+ T next_oop = oopDesc::load_heap_oop(next_addr);
+ if (!oopDesc::is_null(next_oop)) { // i.e. ref is not "active"
+ T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj);
+ debug_only(
+ if(TraceReferenceGC && PrintGCDetails) {
+ gclog_or_tty->print_cr(" Process discovered as normal "
+ INTPTR_FORMAT, discovered_addr);
+ }
+ )
+ MarkSweep::mark_and_push(discovered_addr);
+ }
+ } else {
+#ifdef ASSERT
+ // In the case of older JDKs which do not use the discovered
+ // field for the pending list, an inactive ref (next != NULL)
+ // must always have a NULL discovered field.
+ oop next = oopDesc::load_decode_heap_oop(next_addr);
+ oop discovered = java_lang_ref_Reference::discovered(obj);
+ assert(oopDesc::is_null(next) || oopDesc::is_null(discovered),
+ err_msg("Found an inactive reference " PTR_FORMAT " with a non-NULL discovered field",
+ obj));
+#endif
+ }
+ // treat next as normal oop. next is a link in the reference queue.
debug_only(
if(TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" Process next as normal " INTPTR_FORMAT, next_addr);
@@ -130,13 +155,33 @@
PSParallelCompact::mark_and_push(cm, referent_addr);
}
}
- // treat next as normal oop. next is a link in the pending list.
T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj);
- debug_only(
- if(TraceReferenceGC && PrintGCDetails) {
- gclog_or_tty->print_cr(" Process next as normal " INTPTR_FORMAT, next_addr);
+ if (ReferenceProcessor::pending_list_uses_discovered_field()) {
+ // Treat discovered as normal oop, if ref is not "active",
+ // i.e. if next is non-NULL.
+ T next_oop = oopDesc::load_heap_oop(next_addr);
+ if (!oopDesc::is_null(next_oop)) { // i.e. ref is not "active"
+ T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj);
+ debug_only(
+ if(TraceReferenceGC && PrintGCDetails) {
+ gclog_or_tty->print_cr(" Process discovered as normal "
+ INTPTR_FORMAT, discovered_addr);
+ }
+ )
+ PSParallelCompact::mark_and_push(cm, discovered_addr);
}
- )
+ } else {
+#ifdef ASSERT
+ // In the case of older JDKs which do not use the discovered
+ // field for the pending list, an inactive ref (next != NULL)
+ // must always have a NULL discovered field.
+ T next = oopDesc::load_heap_oop(next_addr);
+ oop discovered = java_lang_ref_Reference::discovered(obj);
+ assert(oopDesc::is_null(next) || oopDesc::is_null(discovered),
+ err_msg("Found an inactive reference " PTR_FORMAT " with a non-NULL discovered field",
+ obj));
+#endif
+ }
PSParallelCompact::mark_and_push(cm, next_addr);
ref->instanceKlass::oop_follow_contents(cm, obj);
}
@@ -197,27 +242,53 @@
}
#define InstanceRefKlass_SPECIALIZED_OOP_ITERATE(T, nv_suffix, contains) \
+ T* disc_addr = (T*)java_lang_ref_Reference::discovered_addr(obj); \
if (closure->apply_to_weak_ref_discovered_field()) { \
- T* disc_addr = (T*)java_lang_ref_Reference::discovered_addr(obj); \
closure->do_oop##nv_suffix(disc_addr); \
} \
\
T* referent_addr = (T*)java_lang_ref_Reference::referent_addr(obj); \
T heap_oop = oopDesc::load_heap_oop(referent_addr); \
- if (!oopDesc::is_null(heap_oop) && contains(referent_addr)) { \
- ReferenceProcessor* rp = closure->_ref_processor; \
+ ReferenceProcessor* rp = closure->_ref_processor; \
+ if (!oopDesc::is_null(heap_oop)) { \
oop referent = oopDesc::decode_heap_oop_not_null(heap_oop); \
if (!referent->is_gc_marked() && (rp != NULL) && \
rp->discover_reference(obj, reference_type())) { \
return size; \
- } else { \
+ } else if (contains(referent_addr)) { \
/* treat referent as normal oop */ \
SpecializationStats::record_do_oop_call##nv_suffix(SpecializationStats::irk);\
closure->do_oop##nv_suffix(referent_addr); \
} \
} \
+ T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj); \
+ if (ReferenceProcessor::pending_list_uses_discovered_field()) { \
+ T next_oop = oopDesc::load_heap_oop(next_addr); \
+ /* Treat discovered as normal oop, if ref is not "active" (next non-NULL) */\
+ if (!oopDesc::is_null(next_oop) && contains(disc_addr)) { \
+ /* i.e. ref is not "active" */ \
+ debug_only( \
+ if(TraceReferenceGC && PrintGCDetails) { \
+ gclog_or_tty->print_cr(" Process discovered as normal " \
+ INTPTR_FORMAT, disc_addr); \
+ } \
+ ) \
+ SpecializationStats::record_do_oop_call##nv_suffix(SpecializationStats::irk);\
+ closure->do_oop##nv_suffix(disc_addr); \
+ } \
+ } else { \
+ /* In the case of older JDKs which do not use the discovered field for */ \
+ /* the pending list, an inactive ref (next != NULL) must always have a */ \
+ /* NULL discovered field. */ \
+ debug_only( \
+ T next_oop = oopDesc::load_heap_oop(next_addr); \
+ T disc_oop = oopDesc::load_heap_oop(disc_addr); \
+ assert(oopDesc::is_null(next_oop) || oopDesc::is_null(disc_oop), \
+ err_msg("Found an inactive reference " PTR_FORMAT " with a non-NULL" \
+ "discovered field", obj)); \
+ ) \
+ } \
/* treat next as normal oop */ \
- T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj); \
if (contains(next_addr)) { \
SpecializationStats::record_do_oop_call##nv_suffix(SpecializationStats::irk); \
closure->do_oop##nv_suffix(next_addr); \
@@ -306,8 +377,37 @@
pm->claim_or_forward_depth(referent_addr);
}
}
- // treat next as normal oop
+ // Treat discovered as normal oop, if ref is not "active",
+ // i.e. if next is non-NULL.
T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj);
+ if (ReferenceProcessor::pending_list_uses_discovered_field()) {
+ T next_oop = oopDesc::load_heap_oop(next_addr);
+ if (!oopDesc::is_null(next_oop)) { // i.e. ref is not "active"
+ T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj);
+ debug_only(
+ if(TraceReferenceGC && PrintGCDetails) {
+ gclog_or_tty->print_cr(" Process discovered as normal "
+ INTPTR_FORMAT, discovered_addr);
+ }
+ )
+ if (PSScavenge::should_scavenge(discovered_addr)) {
+ pm->claim_or_forward_depth(discovered_addr);
+ }
+ }
+ } else {
+#ifdef ASSERT
+ // In the case of older JDKs which do not use the discovered
+ // field for the pending list, an inactive ref (next != NULL)
+ // must always have a NULL discovered field.
+ oop next = oopDesc::load_decode_heap_oop(next_addr);
+ oop discovered = java_lang_ref_Reference::discovered(obj);
+ assert(oopDesc::is_null(next) || oopDesc::is_null(discovered),
+ err_msg("Found an inactive reference " PTR_FORMAT " with a non-NULL discovered field",
+ obj));
+#endif
+ }
+
+ // Treat next as normal oop; next is a link in the reference queue.
if (PSScavenge::should_scavenge(next_addr)) {
pm->claim_or_forward_depth(next_addr);
}
--- a/hotspot/src/share/vm/prims/jvm.h Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/prims/jvm.h Fri Sep 09 14:44:43 2011 +0200
@@ -1579,7 +1579,8 @@
*/
unsigned int thread_park_blocker : 1;
unsigned int post_vm_init_hook_enabled : 1;
- unsigned int : 30;
+ unsigned int pending_list_uses_discovered_field : 1;
+ unsigned int : 29;
unsigned int : 32;
unsigned int : 32;
} jdk_version_info;
--- a/hotspot/src/share/vm/runtime/java.cpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/runtime/java.cpp Fri Sep 09 14:44:43 2011 +0200
@@ -672,7 +672,8 @@
_current = JDK_Version(major, minor, micro, info.update_version,
info.special_update_version, build,
info.thread_park_blocker == 1,
- info.post_vm_init_hook_enabled == 1);
+ info.post_vm_init_hook_enabled == 1,
+ info.pending_list_uses_discovered_field == 1);
}
}
--- a/hotspot/src/share/vm/runtime/java.hpp Thu Sep 08 06:36:31 2011 -0700
+++ b/hotspot/src/share/vm/runtime/java.hpp Fri Sep 09 14:44:43 2011 +0200
@@ -92,6 +92,7 @@
bool _partially_initialized;
bool _thread_park_blocker;
+ bool _pending_list_uses_discovered_field;
bool _post_vm_init_hook_enabled;
bool is_valid() const {
@@ -114,15 +115,18 @@
JDK_Version() : _major(0), _minor(0), _micro(0), _update(0),
_special(0), _build(0), _partially_initialized(false),
- _thread_park_blocker(false), _post_vm_init_hook_enabled(false) {}
+ _thread_park_blocker(false), _post_vm_init_hook_enabled(false),
+ _pending_list_uses_discovered_field(false) {}
JDK_Version(uint8_t major, uint8_t minor = 0, uint8_t micro = 0,
uint8_t update = 0, uint8_t special = 0, uint8_t build = 0,
- bool thread_park_blocker = false, bool post_vm_init_hook_enabled = false) :
+ bool thread_park_blocker = false, bool post_vm_init_hook_enabled = false,
+ bool pending_list_uses_discovered_field = false) :
_major(major), _minor(minor), _micro(micro), _update(update),
_special(special), _build(build), _partially_initialized(false),
_thread_park_blocker(thread_park_blocker),
- _post_vm_init_hook_enabled(post_vm_init_hook_enabled) {}
+ _post_vm_init_hook_enabled(post_vm_init_hook_enabled),
+ _pending_list_uses_discovered_field(pending_list_uses_discovered_field) {}
// Returns the current running JDK version
static JDK_Version current() { return _current; }
@@ -149,6 +153,10 @@
bool post_vm_init_hook_enabled() const {
return _post_vm_init_hook_enabled;
}
+ // For compatibility wrt pre-4965777 JDK's
+ bool pending_list_uses_discovered_field() const {
+ return _pending_list_uses_discovered_field;
+ }
// Performs a full ordering comparison using all fields (update, build, etc.)
int compare(const JDK_Version& other) const;