aoqi@0: /* aoqi@0: * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: * aoqi@0: */ aoqi@0: aoqi@0: #include "precompiled.hpp" aoqi@0: #include "classfile/javaClasses.hpp" aoqi@0: #include "gc_implementation/g1/g1StringDedupQueue.hpp" aoqi@0: #include "memory/gcLocker.hpp" aoqi@0: #include "runtime/mutexLocker.hpp" aoqi@0: #include "utilities/stack.inline.hpp" aoqi@0: aoqi@0: G1StringDedupQueue* G1StringDedupQueue::_queue = NULL; aoqi@0: const size_t G1StringDedupQueue::_max_size = 1000000; // Max number of elements per queue aoqi@0: const size_t G1StringDedupQueue::_max_cache_size = 0; // Max cache size per queue aoqi@0: aoqi@0: G1StringDedupQueue::G1StringDedupQueue() : aoqi@0: _cursor(0), aoqi@0: _cancel(false), aoqi@0: _empty(true), aoqi@0: _dropped(0) { aoqi@0: _nqueues = MAX2(ParallelGCThreads, (size_t)1); aoqi@0: _queues = NEW_C_HEAP_ARRAY(G1StringDedupWorkerQueue, _nqueues, mtGC); aoqi@0: for (size_t i = 0; i < _nqueues; i++) { aoqi@0: new (_queues + i) G1StringDedupWorkerQueue(G1StringDedupWorkerQueue::default_segment_size(), _max_cache_size, _max_size); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: G1StringDedupQueue::~G1StringDedupQueue() { aoqi@0: ShouldNotReachHere(); aoqi@0: } aoqi@0: aoqi@0: void G1StringDedupQueue::create() { aoqi@0: assert(_queue == NULL, "One string deduplication queue allowed"); aoqi@0: _queue = new G1StringDedupQueue(); aoqi@0: } aoqi@0: aoqi@0: void G1StringDedupQueue::wait() { aoqi@0: MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); aoqi@0: while (_queue->_empty && !_queue->_cancel) { aoqi@0: ml.wait(Mutex::_no_safepoint_check_flag); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void G1StringDedupQueue::cancel_wait() { aoqi@0: MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); aoqi@0: _queue->_cancel = true; aoqi@0: ml.notify(); aoqi@0: } aoqi@0: aoqi@0: void G1StringDedupQueue::push(uint worker_id, oop java_string) { aoqi@0: assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint"); aoqi@0: assert(worker_id < _queue->_nqueues, "Invalid queue"); aoqi@0: aoqi@0: // Push and notify waiter aoqi@0: G1StringDedupWorkerQueue& worker_queue = _queue->_queues[worker_id]; aoqi@0: if (!worker_queue.is_full()) { aoqi@0: worker_queue.push(java_string); aoqi@0: if (_queue->_empty) { aoqi@0: MonitorLockerEx ml(StringDedupQueue_lock, Mutex::_no_safepoint_check_flag); aoqi@0: if (_queue->_empty) { aoqi@0: // Mark non-empty and notify waiter aoqi@0: _queue->_empty = false; aoqi@0: ml.notify(); aoqi@0: } aoqi@0: } aoqi@0: } else { aoqi@0: // Queue is full, drop the string and update the statistics aoqi@0: Atomic::inc_ptr(&_queue->_dropped); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: oop G1StringDedupQueue::pop() { aoqi@0: assert(!SafepointSynchronize::is_at_safepoint(), "Must not be at safepoint"); aoqi@0: No_Safepoint_Verifier nsv; aoqi@0: aoqi@0: // Try all queues before giving up aoqi@0: for (size_t tries = 0; tries < _queue->_nqueues; tries++) { aoqi@0: // The cursor indicates where we left of last time aoqi@0: G1StringDedupWorkerQueue* queue = &_queue->_queues[_queue->_cursor]; aoqi@0: while (!queue->is_empty()) { aoqi@0: oop obj = queue->pop(); aoqi@0: // The oop we pop can be NULL if it was marked aoqi@0: // dead. Just ignore those and pop the next oop. aoqi@0: if (obj != NULL) { aoqi@0: return obj; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // Try next queue aoqi@0: _queue->_cursor = (_queue->_cursor + 1) % _queue->_nqueues; aoqi@0: } aoqi@0: aoqi@0: // Mark empty aoqi@0: _queue->_empty = true; aoqi@0: aoqi@0: return NULL; aoqi@0: } aoqi@0: aoqi@0: void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl) { aoqi@0: // A worker thread first claims a queue, which ensures exclusive aoqi@0: // access to that queue, then continues to process it. aoqi@0: for (;;) { aoqi@0: // Grab next queue to scan aoqi@0: size_t queue = cl->claim_queue(); aoqi@0: if (queue >= _queue->_nqueues) { aoqi@0: // End of queues aoqi@0: break; aoqi@0: } aoqi@0: aoqi@0: // Scan the queue aoqi@0: unlink_or_oops_do(cl, queue); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void G1StringDedupQueue::unlink_or_oops_do(G1StringDedupUnlinkOrOopsDoClosure* cl, size_t queue) { aoqi@0: assert(queue < _queue->_nqueues, "Invalid queue"); aoqi@0: StackIterator iter(_queue->_queues[queue]); aoqi@0: while (!iter.is_empty()) { aoqi@0: oop* p = iter.next_addr(); aoqi@0: if (*p != NULL) { aoqi@0: if (cl->is_alive(*p)) { aoqi@0: cl->keep_alive(p); aoqi@0: } else { aoqi@0: // Clear dead reference aoqi@0: *p = NULL; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: void G1StringDedupQueue::print_statistics(outputStream* st) { aoqi@0: st->print_cr( aoqi@0: " [Queue]\n" aoqi@0: " [Dropped: "UINTX_FORMAT"]", _queue->_dropped); aoqi@0: } aoqi@0: aoqi@0: void G1StringDedupQueue::verify() { aoqi@0: for (size_t i = 0; i < _queue->_nqueues; i++) { aoqi@0: StackIterator iter(_queue->_queues[i]); aoqi@0: while (!iter.is_empty()) { aoqi@0: oop obj = iter.next(); aoqi@0: if (obj != NULL) { aoqi@0: guarantee(Universe::heap()->is_in_reserved(obj), "Object must be on the heap"); aoqi@0: guarantee(!obj->is_forwarded(), "Object must not be forwarded"); aoqi@0: guarantee(java_lang_String::is_instance(obj), "Object must be a String"); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: }