mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-24 17:20:06 +00:00
Merge
This commit is contained in:
commit
bfa7e324d9
@ -2531,10 +2531,14 @@ bool os::commit_memory(char* addr, size_t size, size_t alignment_hint,
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// Fall through and try to use small pages
|
||||
}
|
||||
|
||||
return commit_memory(addr, size, exec);
|
||||
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) {
|
||||
|
||||
@ -2706,11 +2706,10 @@ static void cleanup_after_large_page_init() {
|
||||
|
||||
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 @@ static bool numa_interleaving_init() {
|
||||
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 @@ static bool numa_interleaving_init() {
|
||||
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 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags,
|
||||
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::free_memory(char *addr, size_t bytes) { }
|
||||
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);
|
||||
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;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool os::get_page_info(char *start, page_info* info) {
|
||||
@ -3768,6 +3773,10 @@ jint os::init_2(void) {
|
||||
// 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();
|
||||
|
||||
@ -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 @@ CollectionSetChooser::getNextMarkedRegion(double time_remaining,
|
||||
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 @@ CollectionSetChooser::getNextMarkedRegion(double time_remaining,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ void ConcurrentG1RefineThread::sample_young_list_rs_lengths() {
|
||||
}
|
||||
}
|
||||
|
||||
g1p->check_prediction_validity();
|
||||
g1p->revise_young_list_target_length_if_necessary();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 @@ void ConcurrentMark::cleanup() {
|
||||
|
||||
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) {
|
||||
|
||||
@ -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 @@ HeapRegion* G1CollectedHeap::new_region(size_t word_size, bool do_expand) {
|
||||
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 @@ HeapWord* G1CollectedHeap::humongous_obj_allocate(size_t word_size) {
|
||||
// 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 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(size_t word_size,
|
||||
|
||||
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 @@ resize_if_necessary_after_full_collection(size_t word_size) {
|
||||
// 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 @@ HeapWord* G1CollectedHeap::expand_and_allocate(size_t word_size) {
|
||||
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 @@ bool G1CollectedHeap::expand(size_t expand_bytes) {
|
||||
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 @@ bool G1CollectedHeap::expand(size_t expand_bytes) {
|
||||
}
|
||||
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 @@ bool G1CollectedHeap::expand(size_t expand_bytes) {
|
||||
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 @@ void G1CollectedHeap::shrink_helper(size_t shrink_bytes) {
|
||||
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 @@ void G1CollectedHeap::shrink_helper(size_t shrink_bytes) {
|
||||
_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 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
|
||||
|
||||
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 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ g1_process_strong_roots(bool collecting_perm_gen,
|
||||
// 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).
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -183,9 +183,9 @@ protected:
|
||||
// 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 @@ protected:
|
||||
|
||||
double _gc_overhead_perc;
|
||||
|
||||
double _reserve_factor;
|
||||
size_t _reserve_regions;
|
||||
|
||||
bool during_marking() {
|
||||
return _during_marking;
|
||||
}
|
||||
@ -243,6 +246,10 @@ private:
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
|
||||
// </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 @@ protected:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ protected:
|
||||
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 @@ public:
|
||||
// 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 @@ public:
|
||||
_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 @@ public:
|
||||
|
||||
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 @@ inline double variance(int n, double sum_of_squares, double sum) {
|
||||
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
|
||||
|
||||
65
hotspot/src/share/vm/gc_implementation/g1/g1ErgoVerbose.cpp
Normal file
65
hotspot/src/share/vm/gc_implementation/g1/g1ErgoVerbose.cpp
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
197
hotspot/src/share/vm/gc_implementation/g1/g1ErgoVerbose.hpp
Normal file
197
hotspot/src/share/vm/gc_implementation/g1/g1ErgoVerbose.hpp
Normal file
@ -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
|
||||
@ -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 @@ void G1MMUTrackerQueue::add_pause(double start, double end, bool gc_thread) {
|
||||
// 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);
|
||||
|
||||
@ -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.") \
|
||||
\
|
||||
|
||||
@ -98,7 +98,7 @@ void VM_G1IncCollectionPause::doit() {
|
||||
|
||||
// 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
|
||||
|
||||
@ -909,10 +909,6 @@ void ParallelScavengeHeap::verify(bool allow_dirty, bool silent, VerifyOption op
|
||||
}
|
||||
young_gen()->verify(allow_dirty);
|
||||
}
|
||||
if (!silent) {
|
||||
gclog_or_tty->print("ref_proc ");
|
||||
}
|
||||
ReferenceProcessor::verify();
|
||||
}
|
||||
|
||||
void ParallelScavengeHeap::print_heap_change(size_t prev_used) {
|
||||
|
||||
@ -80,10 +80,6 @@ void MarkFromRootsTask::do_it(GCTaskManager* manager, uint which) {
|
||||
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;
|
||||
|
||||
@ -98,8 +98,7 @@ class MarkFromRootsTask : public GCTask {
|
||||
management = 6,
|
||||
jvmti = 7,
|
||||
system_dictionary = 8,
|
||||
reference_processing = 9,
|
||||
code_cache = 10
|
||||
code_cache = 9
|
||||
};
|
||||
private:
|
||||
RootType _root_type;
|
||||
|
||||
@ -516,7 +516,6 @@ void PSMarkSweep::mark_sweep_phase1(bool clear_all_softrefs) {
|
||||
{
|
||||
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 @@ void PSMarkSweep::mark_sweep_phase3() {
|
||||
|
||||
// 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());
|
||||
|
||||
@ -2445,7 +2445,6 @@ void PSParallelCompact::adjust_roots() {
|
||||
|
||||
// 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());
|
||||
|
||||
@ -55,7 +55,6 @@ void ScavengeRootsTask::do_it(GCTaskManager* manager, uint which) {
|
||||
switch (_root_type) {
|
||||
case universe:
|
||||
Universe::oops_do(&roots_closure);
|
||||
ReferenceProcessor::oops_do(&roots_closure);
|
||||
break;
|
||||
|
||||
case jni_handles:
|
||||
|
||||
@ -1269,10 +1269,6 @@ void GenCollectedHeap::verify(bool allow_dirty, bool silent, VerifyOption option
|
||||
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); }
|
||||
|
||||
@ -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 @@ public:
|
||||
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() {
|
||||
}
|
||||
|
||||
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 @@ void ReferenceProcessor::init_statics() {
|
||||
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 @@ ReferenceProcessor::ReferenceProcessor(MemRegion span,
|
||||
_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::weak_oops_do(OopClosure* f) {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ void ReferenceProcessor::process_phaseJNI(BoolObjectClosure* is_alive,
|
||||
}
|
||||
#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 @@ bool ReferenceProcessor::enqueue_discovered_references(AbstractRefProcTaskExecut
|
||||
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);
|
||||
}
|
||||
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 {
|
||||
java_lang_ref_Reference::set_next(obj, old);
|
||||
|
||||
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
|
||||
}
|
||||
} else {
|
||||
java_lang_ref_Reference::set_next(obj, next);
|
||||
}
|
||||
java_lang_ref_Reference::set_discovered(obj, (oop) NULL);
|
||||
obj = next;
|
||||
} 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, next_d);
|
||||
}
|
||||
java_lang_ref_Reference::set_discovered(obj, (oop) NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,10 +389,9 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ void ReferenceProcessor::enqueue_discovered_reflists(HeapWord* pending_list_addr
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
private:
|
||||
DiscoveredList& _refs_list;
|
||||
HeapWord* _prev_next;
|
||||
oop _prev;
|
||||
oop _ref;
|
||||
HeapWord* _discovered_addr;
|
||||
oop _next;
|
||||
@ -509,6 +526,7 @@ inline DiscoveredListIterator::DiscoveredListIterator(DiscoveredList& refs_li
|
||||
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 @@ inline DiscoveredListIterator::DiscoveredListIterator(DiscoveredList& refs_li
|
||||
_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::load_ptrs(DEBUG_ONLY(bool allow_null_referen
|
||||
|
||||
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 @@ ReferenceProcessor::process_phase1(DiscoveredList& refs_list,
|
||||
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 @@ ReferenceProcessor::process_phase3(DiscoveredList& refs_list,
|
||||
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 @@ void ReferenceProcessor::balance_queues(DiscoveredList ref_lists[])
|
||||
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 @@ void ReferenceProcessor::balance_queues(DiscoveredList ref_lists[])
|
||||
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 @@ ReferenceProcessor::add_to_discovered_list_mt(DiscoveredList& refs_list,
|
||||
// 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 @@ void ReferenceProcessor::verify_referent(oop obj) {
|
||||
// (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 @@ void ReferenceProcessor::verify_referent(oop obj) {
|
||||
// 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 @@ bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) {
|
||||
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 @@ bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) {
|
||||
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 @@ bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) {
|
||||
|
||||
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 @@ bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) {
|
||||
// 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 @@ void ReferenceProcessor::verify_ok_to_handle_reflists() {
|
||||
}
|
||||
#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
|
||||
|
||||
@ -52,8 +52,8 @@ class DiscoveredList;
|
||||
|
||||
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 @@ class ReferenceProcessor : public CHeapObj {
|
||||
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 @@ class ReferenceProcessor : public CHeapObj {
|
||||
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 @@ class ReferenceProcessor : public CHeapObj {
|
||||
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 @@ class ReferenceProcessor : public CHeapObj {
|
||||
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 @@ class ReferenceProcessor : public CHeapObj {
|
||||
|
||||
// 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 @@ class ReferenceProcessor : public CHeapObj {
|
||||
// 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 @@ protected:
|
||||
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 @@ protected:
|
||||
ReferenceProcessor& _ref_processor;
|
||||
DiscoveredList* _refs_lists;
|
||||
HeapWord* _pending_list_addr;
|
||||
oop _sentinel_ref;
|
||||
int _n_queues;
|
||||
};
|
||||
|
||||
|
||||
@ -146,7 +146,6 @@ void SharedHeap::process_strong_roots(bool activate_scope,
|
||||
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);
|
||||
}
|
||||
|
||||
@ -56,9 +56,8 @@ static void specialized_oop_follow_contents(instanceRefKlass* ref, oop obj) {
|
||||
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 @@ static void specialized_oop_follow_contents(instanceRefKlass* ref, oop obj) {
|
||||
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 @@ void specialized_oop_follow_contents(instanceRefKlass* ref,
|
||||
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 @@ int instanceRefKlass::oop_adjust_pointers(oop obj) {
|
||||
}
|
||||
|
||||
#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); \
|
||||
} \
|
||||
} \
|
||||
/* treat next as normal oop */ \
|
||||
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 */ \
|
||||
if (contains(next_addr)) { \
|
||||
SpecializationStats::record_do_oop_call##nv_suffix(SpecializationStats::irk); \
|
||||
closure->do_oop##nv_suffix(next_addr); \
|
||||
@ -306,8 +377,37 @@ void specialized_oop_push_contents(instanceRefKlass *ref,
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1579,7 +1579,8 @@ typedef struct {
|
||||
*/
|
||||
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;
|
||||
|
||||
@ -672,7 +672,8 @@ void JDK_Version::initialize() {
|
||||
_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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -92,6 +92,7 @@ class JDK_Version VALUE_OBJ_CLASS_SPEC {
|
||||
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 @@ class JDK_Version VALUE_OBJ_CLASS_SPEC {
|
||||
|
||||
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 @@ class JDK_Version VALUE_OBJ_CLASS_SPEC {
|
||||
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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user