zgu@3900: /* zgu@3900: * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. zgu@3900: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. zgu@3900: * zgu@3900: * This code is free software; you can redistribute it and/or modify it zgu@3900: * under the terms of the GNU General Public License version 2 only, as zgu@3900: * published by the Free Software Foundation. zgu@3900: * zgu@3900: * This code is distributed in the hope that it will be useful, but WITHOUT zgu@3900: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or zgu@3900: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License zgu@3900: * version 2 for more details (a copy is included in the LICENSE file that zgu@3900: * accompanied this code). zgu@3900: * zgu@3900: * You should have received a copy of the GNU General Public License version zgu@3900: * 2 along with this work; if not, write to the Free Software Foundation, zgu@3900: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. zgu@3900: * zgu@3900: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA zgu@3900: * or visit www.oracle.com if you need additional information or have any zgu@3900: * questions. zgu@3900: * zgu@3900: */ zgu@3900: zgu@3900: #include "precompiled.hpp" zgu@3900: #include "runtime/threadCritical.hpp" zgu@3900: #include "services/memTracker.hpp" zgu@3900: #include "services/memTrackWorker.hpp" zgu@3900: #include "utilities/decoder.hpp" zgu@3900: #include "utilities/vmError.hpp" zgu@3900: zgu@3900: MemTrackWorker::MemTrackWorker() { zgu@3900: // create thread uses cgc thread type for now. We should revisit zgu@3900: // the option, or create new thread type. zgu@3900: _has_error = !os::create_thread(this, os::cgc_thread); zgu@3900: set_name("MemTrackWorker", 0); zgu@3900: zgu@3900: // initial generation circuit buffer zgu@3900: if (!has_error()) { zgu@3900: _head = _tail = 0; zgu@3900: for(int index = 0; index < MAX_GENERATIONS; index ++) { zgu@3900: _gen[index] = NULL; zgu@3900: } zgu@3900: } zgu@3900: NOT_PRODUCT(_sync_point_count = 0;) zgu@3900: NOT_PRODUCT(_merge_count = 0;) zgu@3900: NOT_PRODUCT(_last_gen_in_use = 0;) zgu@3900: } zgu@3900: zgu@3900: MemTrackWorker::~MemTrackWorker() { zgu@3900: for (int index = 0; index < MAX_GENERATIONS; index ++) { zgu@3900: MemRecorder* rc = _gen[index]; zgu@3900: if (rc != NULL) { zgu@3900: delete rc; zgu@3900: } zgu@3900: } zgu@3900: } zgu@3900: zgu@3900: void* MemTrackWorker::operator new(size_t size) { zgu@3900: assert(false, "use nothrow version"); zgu@3900: return NULL; zgu@3900: } zgu@3900: zgu@3900: void* MemTrackWorker::operator new(size_t size, const std::nothrow_t& nothrow_constant) { zgu@3900: return allocate(size, false, mtNMT); zgu@3900: } zgu@3900: zgu@3900: void MemTrackWorker::start() { zgu@3900: os::start_thread(this); zgu@3900: } zgu@3900: zgu@3900: /* zgu@3900: * Native memory tracking worker thread loop: zgu@3900: * 1. merge one generation of memory recorders to staging area zgu@3900: * 2. promote staging data to memory snapshot zgu@3900: * zgu@3900: * This thread can run through safepoint. zgu@3900: */ zgu@3900: zgu@3900: void MemTrackWorker::run() { zgu@3900: assert(MemTracker::is_on(), "native memory tracking is off"); zgu@3900: this->initialize_thread_local_storage(); zgu@3900: this->record_stack_base_and_size(); zgu@3900: MemSnapshot* snapshot = MemTracker::get_snapshot(); zgu@3900: assert(snapshot != NULL, "Worker should not be started"); zgu@3900: MemRecorder* rec; zgu@3900: zgu@3900: while (!MemTracker::shutdown_in_progress()) { zgu@3900: NOT_PRODUCT(_last_gen_in_use = generations_in_use();) zgu@3900: { zgu@3900: // take a recorder from earliest generation in buffer zgu@3900: ThreadCritical tc; zgu@3900: rec = _gen[_head]; zgu@3900: if (rec != NULL) { zgu@3900: _gen[_head] = rec->next(); zgu@3900: } zgu@3900: assert(count_recorder(_gen[_head]) <= MemRecorder::_instance_count, zgu@3900: "infinite loop after dequeue"); zgu@3900: } zgu@3900: if (rec != NULL) { zgu@3900: // merge the recorder into staging area zgu@3986: if (!snapshot->merge(rec)) { zgu@3986: MemTracker::shutdown(MemTracker::NMT_out_of_memory); zgu@3986: } else { zgu@3986: NOT_PRODUCT(_merge_count ++;) zgu@3986: } zgu@3900: MemTracker::release_thread_recorder(rec); zgu@3900: } else { zgu@3900: // no more recorder to merge, promote staging area zgu@3900: // to snapshot zgu@3900: if (_head != _tail) { zgu@3900: { zgu@3900: ThreadCritical tc; zgu@3900: if (_gen[_head] != NULL || _head == _tail) { zgu@3900: continue; zgu@3900: } zgu@3900: // done with this generation, increment _head pointer zgu@3900: _head = (_head + 1) % MAX_GENERATIONS; zgu@3900: } zgu@3900: // promote this generation data to snapshot zgu@4053: if (!snapshot->promote()) { zgu@4053: // failed to promote, means out of memory zgu@4053: MemTracker::shutdown(MemTracker::NMT_out_of_memory); zgu@4053: } zgu@3900: } else { zgu@3900: snapshot->wait(1000); zgu@3900: ThreadCritical tc; zgu@3900: // check if more data arrived zgu@3900: if (_gen[_head] == NULL) { zgu@3900: _gen[_head] = MemTracker::get_pending_recorders(); zgu@3900: } zgu@3900: } zgu@3900: } zgu@3900: } zgu@3900: assert(MemTracker::shutdown_in_progress(), "just check"); zgu@3900: zgu@3986: // transits to final shutdown zgu@3900: MemTracker::final_shutdown(); zgu@3900: } zgu@3900: zgu@3900: // at synchronization point, where 'safepoint visible' Java threads are blocked zgu@3900: // at a safepoint, and the rest of threads are blocked on ThreadCritical lock. zgu@3900: // The caller MemTracker::sync() already takes ThreadCritical before calling this zgu@3900: // method. zgu@3900: // zgu@3900: // Following tasks are performed: zgu@3900: // 1. add all recorders in pending queue to current generation zgu@3900: // 2. increase generation zgu@3900: zgu@3900: void MemTrackWorker::at_sync_point(MemRecorder* rec) { zgu@3900: NOT_PRODUCT(_sync_point_count ++;) zgu@3900: assert(count_recorder(rec) <= MemRecorder::_instance_count, zgu@3900: "pending queue has infinite loop"); zgu@3900: zgu@3900: bool out_of_generation_buffer = false; zgu@3900: // check shutdown state inside ThreadCritical zgu@3900: if (MemTracker::shutdown_in_progress()) return; zgu@3900: // append the recorders to the end of the generation zgu@3900: if( rec != NULL) { zgu@3900: MemRecorder* cur_head = _gen[_tail]; zgu@3900: if (cur_head == NULL) { zgu@3900: _gen[_tail] = rec; zgu@3900: } else { zgu@3900: while (cur_head->next() != NULL) { zgu@3900: cur_head = cur_head->next(); zgu@3900: } zgu@3900: cur_head->set_next(rec); zgu@3900: } zgu@3900: } zgu@3900: assert(count_recorder(rec) <= MemRecorder::_instance_count, zgu@3900: "after add to current generation has infinite loop"); zgu@3900: // we have collected all recorders for this generation. If there is data, zgu@3900: // we need to increment _tail to start a new generation. zgu@3900: if (_gen[_tail] != NULL || _head == _tail) { zgu@3900: _tail = (_tail + 1) % MAX_GENERATIONS; zgu@3900: out_of_generation_buffer = (_tail == _head); zgu@3900: } zgu@3900: zgu@3900: if (out_of_generation_buffer) { zgu@3900: MemTracker::shutdown(MemTracker::NMT_out_of_generation); zgu@3900: } zgu@3900: } zgu@3900: zgu@3900: #ifndef PRODUCT zgu@3900: int MemTrackWorker::count_recorder(const MemRecorder* head) { zgu@3900: int count = 0; zgu@3900: while(head != NULL) { zgu@3900: count ++; zgu@3900: head = head->next(); zgu@3900: } zgu@3900: return count; zgu@3900: } zgu@3900: zgu@3900: int MemTrackWorker::count_pending_recorders() const { zgu@3900: int count = 0; zgu@3900: for (int index = 0; index < MAX_GENERATIONS; index ++) { zgu@3900: MemRecorder* head = _gen[index]; zgu@3900: if (head != NULL) { zgu@3900: count += count_recorder(head); zgu@3900: } zgu@3900: } zgu@3900: return count; zgu@3900: } zgu@3900: #endif