diff -r fce463e39f72 -r 4b1426ed1c44 src/hotspot/share/gc/shared/scavengableNMethods.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shared/scavengableNMethods.cpp Thu Mar 14 09:15:51 2019 +0100 @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2019, 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 "code/codeCache.hpp" +#include "code/nmethod.hpp" +#include "compiler/compileTask.hpp" +#include "gc/shared/collectedHeap.hpp" +#include "gc/shared/scavengableNMethods.hpp" +#include "gc/shared/scavengableNMethodsData.hpp" +#include "logging/log.hpp" +#include "logging/logStream.hpp" +#include "memory/universe.hpp" +#include "utilities/debug.hpp" + +static ScavengableNMethodsData gc_data(nmethod* nm) { + return ScavengableNMethodsData(nm); +} + +nmethod* ScavengableNMethods::_head = NULL; +BoolObjectClosure* ScavengableNMethods::_is_scavengable = NULL; + +void ScavengableNMethods::initialize(BoolObjectClosure* is_scavengable) { + _is_scavengable = is_scavengable; +} + +// Conditionally adds the nmethod to the list if it is +// not already on the list and has a scavengeable root. +void ScavengableNMethods::register_nmethod(nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + + ScavengableNMethodsData data = gc_data(nm); + + if (data.on_list() || !has_scavengable_oops(nm)) { + return; + } + + data.set_on_list(); + data.set_next(_head); + + _head = nm; + + CodeCache::print_trace("register_nmethod", nm); +} + +void ScavengableNMethods::unregister_nmethod(nmethod* nm) { + // Do nothing. Unlinking is currently delayed until the purge phase. +} + +#ifndef PRODUCT + +class DebugScavengableOops: public OopClosure { + BoolObjectClosure* _is_scavengable; + nmethod* _nm; + bool _ok; +public: + DebugScavengableOops(BoolObjectClosure* is_scavengable, nmethod* nm) : + _is_scavengable(is_scavengable), + _nm(nm), + _ok(true) { } + + bool ok() { return _ok; } + virtual void do_oop(oop* p) { + if (*p == NULL || !_is_scavengable->do_object_b(*p)) { + return; + } + + if (_ok) { + _nm->print_nmethod(true); + _ok = false; + } + tty->print_cr("*** scavengable oop " PTR_FORMAT " found at " PTR_FORMAT " (offset %d)", + p2i(*p), p2i(p), (int)((intptr_t)p - (intptr_t)_nm)); + (*p)->print(); + } + virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); } +}; + +#endif // PRODUCT + +void ScavengableNMethods::verify_nmethod(nmethod* nm) { +#ifndef PRODUCT + if (!gc_data(nm).on_list()) { + // Actually look inside, to verify the claim that it's clean. + DebugScavengableOops cl(_is_scavengable, nm); + nm->oops_do(&cl); + if (!cl.ok()) + fatal("found an unadvertised bad scavengable oop in the code cache"); + } + assert(gc_data(nm).not_marked(), ""); +#endif // PRODUCT +} + +void ScavengableNMethods::flush_nmethod(nmethod* nm) { + assert_locked_or_safepoint(CodeCache_lock); + + // TODO: Should be done in unregister_nmethod, during the "unlink" phase. + if (gc_data(nm).on_list()) { + CodeCache::print_trace("flush_nmethod", nm); + nmethod* prev = NULL; + for (nmethod* cur = _head; cur != NULL; cur = gc_data(cur).next()) { + if (cur == nm) { + unlist_nmethod(cur, prev); + return; + } + prev = cur; + } + } +} + +class HasScavengableOops: public OopClosure { + BoolObjectClosure* _is_scavengable; + bool _found; + nmethod* _print_nm; +public: + HasScavengableOops(BoolObjectClosure* is_scavengable, nmethod* nm) : + _is_scavengable(is_scavengable), + _found(false), + _print_nm(nm) {} + + bool found() { return _found; } + virtual void do_oop(oop* p) { + if (*p != NULL && _is_scavengable->do_object_b(*p)) { + NOT_PRODUCT(maybe_print(p)); + _found = true; + } + } + virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); } + +#ifndef PRODUCT + void maybe_print(oop* p) { + LogTarget(Trace, gc, nmethod) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + if (!_found) { + CompileTask::print(&ls, _print_nm, "new scavengable oop", /*short_form:*/ true); + } + ls.print("" PTR_FORMAT "[offset=%d] found scavengable oop " PTR_FORMAT " (found at " PTR_FORMAT ") ", + p2i(_print_nm), (int)((intptr_t)p - (intptr_t)_print_nm), + p2i(*p), p2i(p)); + ls.cr(); + } + } +#endif //PRODUCT +}; + +bool ScavengableNMethods::has_scavengable_oops(nmethod* nm) { + HasScavengableOops cl(_is_scavengable, nm); + nm->oops_do(&cl); + return cl.found(); +} + +// Walk the list of methods which might contain oops to the java heap. +void ScavengableNMethods::scavengable_nmethods_do(CodeBlobToOopClosure* f) { + assert_locked_or_safepoint(CodeCache_lock); + + const bool fix_relocations = f->fix_relocations(); + debug_only(mark_on_list_nmethods()); + + nmethod* prev = NULL; + nmethod* cur = _head; + while (cur != NULL) { + ScavengableNMethodsData data = gc_data(cur); + debug_only(data.clear_marked()); + assert(data.not_marked(), ""); + assert(data.on_list(), "else shouldn't be on this list"); + + bool is_live = (!cur->is_zombie() && !cur->is_unloaded()); + LogTarget(Trace, gc, nmethod) lt; + if (lt.is_enabled()) { + LogStream ls(lt); + CompileTask::print(&ls, cur, + is_live ? "scavengable root " : "dead scavengable root", /*short_form:*/ true); + } + if (is_live) { + // Perform cur->oops_do(f), maybe just once per nmethod. + f->do_code_blob(cur); + } + nmethod* const next = data.next(); + // The scavengable nmethod list must contain all methods with scavengable + // oops. It is safe to include more nmethod on the list, but we do not + // expect any live non-scavengable nmethods on the list. + if (fix_relocations) { + if (!is_live || !has_scavengable_oops(cur)) { + unlist_nmethod(cur, prev); + } else { + prev = cur; + } + } + cur = next; + } + + // Check for stray marks. + debug_only(verify_unlisted_nmethods(NULL)); +} + +#ifndef PRODUCT +void ScavengableNMethods::asserted_non_scavengable_nmethods_do(CodeBlobClosure* f) { + // While we are here, verify the integrity of the list. + mark_on_list_nmethods(); + for (nmethod* cur = _head; cur != NULL; cur = gc_data(cur).next()) { + assert(gc_data(cur).on_list(), "else shouldn't be on this list"); + gc_data(cur).clear_marked(); + } + verify_unlisted_nmethods(f); +} +#endif // PRODUCT + +void ScavengableNMethods::unlist_nmethod(nmethod* nm, nmethod* prev) { + assert_locked_or_safepoint(CodeCache_lock); + + assert((prev == NULL && _head == nm) || + (prev != NULL && gc_data(prev).next() == nm), "precondition"); + + CodeCache::print_trace("unlist_nmethod", nm); + + ScavengableNMethodsData data = gc_data(nm); + + if (prev == NULL) { + _head = data.next(); + } else { + gc_data(prev).set_next(data.next()); + } + data.set_next(NULL); + data.clear_on_list(); +} + +void ScavengableNMethods::prune_nmethods() { + assert_locked_or_safepoint(CodeCache_lock); + + debug_only(mark_on_list_nmethods()); + + nmethod* last = NULL; + nmethod* cur = _head; + while (cur != NULL) { + nmethod* next = gc_data(cur).next(); + debug_only(gc_data(cur).clear_marked()); + assert(gc_data(cur).on_list(), "else shouldn't be on this list"); + + if (!cur->is_zombie() && !cur->is_unloaded() && has_scavengable_oops(cur)) { + // Keep it. Advance 'last' to prevent deletion. + last = cur; + } else { + // Prune it from the list, so we don't have to look at it any more. + CodeCache::print_trace("prune_nmethods", cur); + unlist_nmethod(cur, last); + } + cur = next; + } + + // Check for stray marks. + debug_only(verify_unlisted_nmethods(NULL)); +} + +#ifndef PRODUCT +// Temporarily mark nmethods that are claimed to be on the scavenge list. +void ScavengableNMethods::mark_on_list_nmethods() { + NMethodIterator iter(NMethodIterator::only_alive); + while(iter.next()) { + nmethod* nm = iter.method(); + ScavengableNMethodsData data = gc_data(nm); + assert(data.not_marked(), "clean state"); + if (data.on_list()) + data.set_marked(); + } +} + +// If the closure is given, run it on the unlisted nmethods. +// Also make sure that the effects of mark_on_list_nmethods is gone. +void ScavengableNMethods::verify_unlisted_nmethods(CodeBlobClosure* f_or_null) { + NMethodIterator iter(NMethodIterator::only_alive); + while(iter.next()) { + nmethod* nm = iter.method(); + + verify_nmethod(nm); + + if (f_or_null != NULL && !gc_data(nm).on_list()) { + f_or_null->do_code_blob(nm); + } + } +} + +#endif //PRODUCT