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@4400: zgu@4400: void GenerationData::reset() { zgu@4400: _number_of_classes = 0; zgu@4400: while (_recorder_list != NULL) { zgu@4400: MemRecorder* tmp = _recorder_list; zgu@4400: _recorder_list = _recorder_list->next(); zgu@4400: MemTracker::release_thread_recorder(tmp); zgu@4400: } zgu@4400: } zgu@4400: zgu@4927: MemTrackWorker::MemTrackWorker(MemSnapshot* snapshot): _snapshot(snapshot) { 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@4400: ::new ((void*)&_gen[index]) GenerationData(); 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@4400: _gen[index].reset(); 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@4927: assert(_snapshot != NULL, "Worker should not be started"); zgu@3900: MemRecorder* rec; ctornqvi@4512: unsigned long processing_generation = 0; ctornqvi@4512: bool worker_idle = false; 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@4400: rec = _gen[_head].next_recorder(); zgu@3900: } zgu@3900: if (rec != NULL) { ctornqvi@4512: if (rec->get_generation() != processing_generation || worker_idle) { ctornqvi@4512: processing_generation = rec->get_generation(); ctornqvi@4512: worker_idle = false; ctornqvi@4512: MemTracker::set_current_processing_generation(processing_generation); ctornqvi@4512: } ctornqvi@4512: zgu@3900: // merge the recorder into staging area zgu@4927: 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@4400: long number_of_classes; zgu@3900: { zgu@3900: ThreadCritical tc; zgu@4400: if (_gen[_head].has_more_recorder() || _head == _tail) { zgu@3900: continue; zgu@3900: } zgu@4400: number_of_classes = _gen[_head].number_of_classes(); zgu@4400: _gen[_head].reset(); zgu@4400: 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@4927: if (!_snapshot->promote(number_of_classes)) { zgu@4053: // failed to promote, means out of memory zgu@4053: MemTracker::shutdown(MemTracker::NMT_out_of_memory); zgu@4053: } zgu@3900: } else { ctornqvi@4512: // worker thread is idle ctornqvi@4512: worker_idle = true; ctornqvi@4512: MemTracker::report_worker_idle(); zgu@4927: _snapshot->wait(1000); zgu@3900: ThreadCritical tc; zgu@3900: // check if more data arrived zgu@4400: if (!_gen[_head].has_more_recorder()) { zgu@4400: _gen[_head].add_recorders(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@4400: void MemTrackWorker::at_sync_point(MemRecorder* rec, int number_of_classes) { 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@4400: zgu@4400: _gen[_tail].set_number_of_classes(number_of_classes); zgu@3900: // append the recorders to the end of the generation zgu@4400: _gen[_tail].add_recorders(rec); zgu@4400: assert(count_recorder(_gen[_tail].peek()) <= 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@4400: if (_gen[_tail].has_more_recorder() || _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@4400: MemRecorder* head = _gen[index].peek(); zgu@3900: if (head != NULL) { zgu@3900: count += count_recorder(head); zgu@3900: } zgu@3900: } zgu@3900: return count; zgu@3900: } zgu@3900: #endif