0N/A/*
3198N/A * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
1472N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
1472N/A * or visit www.oracle.com if you need additional information or have any
1472N/A * questions.
0N/A *
0N/A */
0N/A
1879N/A#include "precompiled.hpp"
1879N/A#include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp"
1879N/A#include "gc_implementation/parallelScavenge/psOldGen.hpp"
1879N/A#include "gc_implementation/parallelScavenge/psPromotionManager.inline.hpp"
1879N/A#include "gc_implementation/parallelScavenge/psScavenge.inline.hpp"
4302N/A#include "gc_implementation/shared/gcTrace.hpp"
1879N/A#include "gc_implementation/shared/mutableSpace.hpp"
1879N/A#include "memory/memRegion.hpp"
1879N/A#include "oops/oop.inline.hpp"
1879N/A#include "oops/oop.psgc.inline.hpp"
0N/A
0N/APSPromotionManager** PSPromotionManager::_manager_array = NULL;
0N/AOopStarTaskQueueSet* PSPromotionManager::_stack_array_depth = NULL;
0N/APSOldGen* PSPromotionManager::_old_gen = NULL;
0N/AMutableSpace* PSPromotionManager::_young_space = NULL;
0N/A
0N/Avoid PSPromotionManager::initialize() {
0N/A ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
0N/A assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity");
0N/A
0N/A _old_gen = heap->old_gen();
0N/A _young_space = heap->young_gen()->to_space();
0N/A
0N/A assert(_manager_array == NULL, "Attempt to initialize twice");
3863N/A _manager_array = NEW_C_HEAP_ARRAY(PSPromotionManager*, ParallelGCThreads+1, mtGC);
0N/A guarantee(_manager_array != NULL, "Could not initialize promotion manager");
0N/A
1626N/A _stack_array_depth = new OopStarTaskQueueSet(ParallelGCThreads);
4141N/A guarantee(_stack_array_depth != NULL, "Could not initialize promotion manager");
0N/A
0N/A // Create and register the PSPromotionManager(s) for the worker threads.
0N/A for(uint i=0; i<ParallelGCThreads; i++) {
0N/A _manager_array[i] = new PSPromotionManager();
0N/A guarantee(_manager_array[i] != NULL, "Could not create PSPromotionManager");
1626N/A stack_array_depth()->register_queue(i, _manager_array[i]->claimed_stack_depth());
0N/A }
0N/A
0N/A // The VMThread gets its own PSPromotionManager, which is not available
0N/A // for work stealing.
0N/A _manager_array[ParallelGCThreads] = new PSPromotionManager();
0N/A guarantee(_manager_array[ParallelGCThreads] != NULL, "Could not create PSPromotionManager");
0N/A}
0N/A
0N/APSPromotionManager* PSPromotionManager::gc_thread_promotion_manager(int index) {
0N/A assert(index >= 0 && index < (int)ParallelGCThreads, "index out of range");
0N/A assert(_manager_array != NULL, "Sanity");
0N/A return _manager_array[index];
0N/A}
0N/A
0N/APSPromotionManager* PSPromotionManager::vm_thread_promotion_manager() {
0N/A assert(_manager_array != NULL, "Sanity");
0N/A return _manager_array[ParallelGCThreads];
0N/A}
0N/A
0N/Avoid PSPromotionManager::pre_scavenge() {
0N/A ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
0N/A assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity");
0N/A
0N/A _young_space = heap->young_gen()->to_space();
0N/A
0N/A for(uint i=0; i<ParallelGCThreads+1; i++) {
0N/A manager_array(i)->reset();
0N/A }
0N/A}
0N/A
4302N/Abool PSPromotionManager::post_scavenge(YoungGCTracer& gc_tracer) {
4302N/A bool promotion_failure_occurred = false;
4141N/A
1585N/A TASKQUEUE_STATS_ONLY(if (PrintGCDetails && ParallelGCVerbose) print_stats());
1558N/A for (uint i = 0; i < ParallelGCThreads + 1; i++) {
0N/A PSPromotionManager* manager = manager_array(i);
1626N/A assert(manager->claimed_stack_depth()->is_empty(), "should be empty");
4309N/A if (manager->_promotion_failed_info.has_failed()) {
4302N/A gc_tracer.report_promotion_failed(manager->_promotion_failed_info);
4302N/A promotion_failure_occurred = true;
4141N/A }
0N/A manager->flush_labs();
0N/A }
4302N/A return promotion_failure_occurred;
0N/A}
0N/A
1585N/A#if TASKQUEUE_STATS
0N/Avoid
1585N/APSPromotionManager::print_taskqueue_stats(uint i) const {
1585N/A tty->print("%3u ", i);
1626N/A _claimed_stack_depth.stats.print();
1585N/A tty->cr();
0N/A}
0N/A
0N/Avoid
1585N/APSPromotionManager::print_local_stats(uint i) const {
1585N/A #define FMT " " SIZE_FORMAT_W(10)
1585N/A tty->print_cr("%3u" FMT FMT FMT FMT, i, _masked_pushes, _masked_steals,
1585N/A _arrays_chunked, _array_chunks_processed);
1585N/A #undef FMT
1585N/A}
1585N/A
1585N/Astatic const char* const pm_stats_hdr[] = {
1585N/A " --------masked------- arrays array",
1585N/A "thr push steal chunked chunks",
1585N/A "--- ---------- ---------- ---------- ----------"
1585N/A};
1585N/A
1585N/Avoid
0N/APSPromotionManager::print_stats() {
1626N/A tty->print_cr("== GC Tasks Stats, GC %3d",
0N/A Universe::heap()->total_collections());
0N/A
1585N/A tty->print("thr "); TaskQueueStats::print_header(1); tty->cr();
1585N/A tty->print("--- "); TaskQueueStats::print_header(2); tty->cr();
1585N/A for (uint i = 0; i < ParallelGCThreads + 1; ++i) {
1585N/A manager_array(i)->print_taskqueue_stats(i);
1585N/A }
1585N/A
1585N/A const uint hlines = sizeof(pm_stats_hdr) / sizeof(pm_stats_hdr[0]);
1585N/A for (uint i = 0; i < hlines; ++i) tty->print_cr(pm_stats_hdr[i]);
1585N/A for (uint i = 0; i < ParallelGCThreads + 1; ++i) {
1585N/A manager_array(i)->print_local_stats(i);
0N/A }
0N/A}
0N/A
1585N/Avoid
1585N/APSPromotionManager::reset_stats() {
1626N/A claimed_stack_depth()->stats.reset();
1585N/A _masked_pushes = _masked_steals = 0;
1585N/A _arrays_chunked = _array_chunks_processed = 0;
1585N/A}
1585N/A#endif // TASKQUEUE_STATS
0N/A
0N/APSPromotionManager::PSPromotionManager() {
0N/A ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
0N/A assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity");
0N/A
0N/A // We set the old lab's start array.
0N/A _old_lab.set_start_array(old_gen()->start_array());
0N/A
0N/A uint queue_size;
1626N/A claimed_stack_depth()->initialize();
1626N/A queue_size = claimed_stack_depth()->max_elems();
0N/A
0N/A _totally_drain = (ParallelGCThreads == 1) || (GCDrainStackTargetSize == 0);
0N/A if (_totally_drain) {
0N/A _target_stack_size = 0;
0N/A } else {
0N/A // don't let the target stack size to be more than 1/4 of the entries
0N/A _target_stack_size = (uint) MIN2((uint) GCDrainStackTargetSize,
0N/A (uint) (queue_size / 4));
0N/A }
0N/A
0N/A _array_chunk_size = ParGCArrayScanChunk;
0N/A // let's choose 1.5x the chunk size
0N/A _min_array_size_for_chunking = 3 * _array_chunk_size / 2;
0N/A
0N/A reset();
0N/A}
0N/A
0N/Avoid PSPromotionManager::reset() {
1558N/A assert(stacks_empty(), "reset of non-empty stack");
0N/A
0N/A // We need to get an assert in here to make sure the labs are always flushed.
0N/A
0N/A ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
0N/A assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity");
0N/A
0N/A // Do not prefill the LAB's, save heap wastage!
0N/A HeapWord* lab_base = young_space()->top();
0N/A _young_lab.initialize(MemRegion(lab_base, (size_t)0));
0N/A _young_gen_is_full = false;
0N/A
0N/A lab_base = old_gen()->object_space()->top();
0N/A _old_lab.initialize(MemRegion(lab_base, (size_t)0));
0N/A _old_gen_is_full = false;
0N/A
4141N/A _promotion_failed_info.reset();
4141N/A
1585N/A TASKQUEUE_STATS_ONLY(reset_stats());
0N/A}
0N/A
113N/A
0N/Avoid PSPromotionManager::drain_stacks_depth(bool totally_drain) {
0N/A totally_drain = totally_drain || _totally_drain;
0N/A
0N/A#ifdef ASSERT
0N/A ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
0N/A assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity");
0N/A MutableSpace* to_space = heap->young_gen()->to_space();
0N/A MutableSpace* old_space = heap->old_gen()->object_space();
0N/A MutableSpace* perm_space = heap->perm_gen()->object_space();
0N/A#endif /* ASSERT */
0N/A
1558N/A OopStarTaskQueue* const tq = claimed_stack_depth();
0N/A do {
113N/A StarTask p;
0N/A
0N/A // Drain overflow stack first, so other threads can steal from
0N/A // claimed stack while we work.
1558N/A while (tq->pop_overflow(p)) {
1558N/A process_popped_location_depth(p);
0N/A }
0N/A
0N/A if (totally_drain) {
1558N/A while (tq->pop_local(p)) {
0N/A process_popped_location_depth(p);
0N/A }
0N/A } else {
1558N/A while (tq->size() > _target_stack_size && tq->pop_local(p)) {
0N/A process_popped_location_depth(p);
0N/A }
0N/A }
1558N/A } while (totally_drain && !tq->taskqueue_empty() || !tq->overflow_empty());
0N/A
1558N/A assert(!totally_drain || tq->taskqueue_empty(), "Sanity");
1558N/A assert(totally_drain || tq->size() <= _target_stack_size, "Sanity");
1558N/A assert(tq->overflow_empty(), "Sanity");
0N/A}
0N/A
0N/Avoid PSPromotionManager::flush_labs() {
1558N/A assert(stacks_empty(), "Attempt to flush lab with live stack");
0N/A
0N/A // If either promotion lab fills up, we can flush the
0N/A // lab but not refill it, so check first.
0N/A assert(!_young_lab.is_flushed() || _young_gen_is_full, "Sanity");
0N/A if (!_young_lab.is_flushed())
0N/A _young_lab.flush();
0N/A
0N/A assert(!_old_lab.is_flushed() || _old_gen_is_full, "Sanity");
0N/A if (!_old_lab.is_flushed())
0N/A _old_lab.flush();
0N/A
0N/A // Let PSScavenge know if we overflowed
0N/A if (_young_gen_is_full) {
0N/A PSScavenge::set_survivor_overflow(true);
0N/A }
0N/A}
0N/A
113N/Atemplate <class T> void PSPromotionManager::process_array_chunk_work(
113N/A oop obj,
113N/A int start, int end) {
2213N/A assert(start <= end, "invariant");
113N/A T* const base = (T*)objArrayOop(obj)->base();
113N/A T* p = base + start;
113N/A T* const chunk_end = base + end;
113N/A while (p < chunk_end) {
113N/A if (PSScavenge::should_scavenge(p)) {
113N/A claim_or_forward_depth(p);
113N/A }
113N/A ++p;
113N/A }
113N/A}
113N/A
0N/Avoid PSPromotionManager::process_array_chunk(oop old) {
0N/A assert(PSChunkLargeArrays, "invariant");
0N/A assert(old->is_objArray(), "invariant");
0N/A assert(old->is_forwarded(), "invariant");
0N/A
1585N/A TASKQUEUE_STATS_ONLY(++_array_chunks_processed);
0N/A
0N/A oop const obj = old->forwardee();
0N/A
0N/A int start;
0N/A int const end = arrayOop(old)->length();
0N/A if (end > (int) _min_array_size_for_chunking) {
0N/A // we'll chunk more
0N/A start = end - _array_chunk_size;
0N/A assert(start > 0, "invariant");
0N/A arrayOop(old)->set_length(start);
0N/A push_depth(mask_chunked_array_oop(old));
1585N/A TASKQUEUE_STATS_ONLY(++_masked_pushes);
0N/A } else {
0N/A // this is the final chunk for this array
0N/A start = 0;
0N/A int const actual_length = arrayOop(obj)->length();
0N/A arrayOop(old)->set_length(actual_length);
0N/A }
0N/A
113N/A if (UseCompressedOops) {
113N/A process_array_chunk_work<narrowOop>(obj, start, end);
113N/A } else {
113N/A process_array_chunk_work<oop>(obj, start, end);
0N/A }
0N/A}
0N/A
0N/Aoop PSPromotionManager::oop_promotion_failed(oop obj, markOop obj_mark) {
0N/A assert(_old_gen_is_full || PromotionFailureALot, "Sanity");
0N/A
0N/A // Attempt to CAS in the header.
0N/A // This tests if the header is still the same as when
0N/A // this started. If it is the same (i.e., no forwarding
0N/A // pointer has been installed), then this thread owns
0N/A // it.
0N/A if (obj->cas_forward_to(obj, obj_mark)) {
0N/A // We won any races, we "own" this object.
0N/A assert(obj == obj->forwardee(), "Sanity");
0N/A
4309N/A _promotion_failed_info.register_copy_failure(obj->size());
4141N/A
1626N/A obj->push_contents(this);
0N/A
0N/A // Save the mark if needed
0N/A PSScavenge::oop_promotion_failed(obj, obj_mark);
0N/A } else {
0N/A // We lost, someone else "owns" this object
0N/A guarantee(obj->is_forwarded(), "Object must be forwarded if the cas failed.");
0N/A
0N/A // No unallocation to worry about.
0N/A obj = obj->forwardee();
0N/A }
0N/A
0N/A#ifdef DEBUG
0N/A if (TraceScavenge) {
0N/A gclog_or_tty->print_cr("{%s %s 0x%x (%d)}",
0N/A "promotion-failure",
0N/A obj->blueprint()->internal_name(),
0N/A obj, obj->size());
0N/A
0N/A }
0N/A#endif
0N/A
0N/A return obj;
0N/A}