| /* | |
| * Copyright (c) 2019, 2025, 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 "code/codeCache.hpp" | |
| #include "code/nmethod.hpp" | |
| #include "gc/shared/scavengableNMethods.hpp" | |
| #include "gc/shared/scavengableNMethodsData.hpp" | |
| #include "runtime/mutexLocker.hpp" | |
| #include "utilities/debug.hpp" | |
| static ScavengableNMethodsData gc_data(nmethod* nm) { | |
| return ScavengableNMethodsData(nm); | |
| } | |
| nmethod* ScavengableNMethods::_head = nullptr; | |
| BoolObjectClosure* ScavengableNMethods::_is_scavengable = nullptr; | |
| 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; | |
| } | |
| void ScavengableNMethods::unregister_nmethod(nmethod* nm) { | |
| // All users of this method only unregister in bulk during code unloading. | |
| ShouldNotReachHere(); | |
| } | |
| #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 == nullptr || !_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 | |
| } | |
| bool ScavengableNMethods::has_scavengable_oops(nmethod* nm) { | |
| struct HasScavengableOops: public OopClosure { | |
| BoolObjectClosure* _is_scavengable; | |
| bool _found; | |
| explicit HasScavengableOops(BoolObjectClosure* is_scavengable) : | |
| _is_scavengable(is_scavengable), | |
| _found(false) {} | |
| virtual void do_oop(oop* p) { | |
| if (!_found && *p != nullptr && _is_scavengable->do_object_b(*p)) { | |
| _found = true; | |
| } | |
| } | |
| virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); } | |
| } cl {_is_scavengable}; | |
| nm->oops_do(&cl); | |
| return cl._found; | |
| } | |
| // Walk the list of methods which might contain oops to the java heap. | |
| void ScavengableNMethods::nmethods_do_and_prune(NMethodToOopClosure* cl) { | |
| assert_locked_or_safepoint(CodeCache_lock); | |
| DEBUG_ONLY(mark_on_list_nmethods()); | |
| nmethod* prev = nullptr; | |
| nmethod* cur = _head; | |
| while (cur != nullptr) { | |
| ScavengableNMethodsData data = gc_data(cur); | |
| DEBUG_ONLY(data.clear_marked()); | |
| assert(data.on_list(), "else shouldn't be on this list"); | |
| if (cl != nullptr) { | |
| cl->do_nmethod(cur); | |
| } | |
| nmethod* const next = data.next(); | |
| if (!has_scavengable_oops(cur)) { | |
| unlist_nmethod(cur, prev); | |
| } else { | |
| prev = cur; | |
| } | |
| cur = next; | |
| } | |
| // Check for stray marks. | |
| DEBUG_ONLY(verify_nmethods()); | |
| } | |
| void ScavengableNMethods::prune_nmethods_not_into_young() { | |
| nmethods_do_and_prune(nullptr /* No closure */); | |
| } | |
| void ScavengableNMethods::prune_unlinked_nmethods() { | |
| assert_locked_or_safepoint(CodeCache_lock); | |
| DEBUG_ONLY(mark_on_list_nmethods()); | |
| nmethod* prev = nullptr; | |
| nmethod* cur = _head; | |
| while (cur != nullptr) { | |
| ScavengableNMethodsData data = gc_data(cur); | |
| DEBUG_ONLY(data.clear_marked()); | |
| assert(data.on_list(), "else shouldn't be on this list"); | |
| nmethod* const next = data.next(); | |
| if (cur->is_unlinked()) { | |
| unlist_nmethod(cur, prev); | |
| } else { | |
| prev = cur; | |
| } | |
| cur = next; | |
| } | |
| // Check for stray marks. | |
| DEBUG_ONLY(verify_nmethods()); | |
| } | |
| // Walk the list of methods which might contain oops to the java heap. | |
| void ScavengableNMethods::nmethods_do(NMethodToOopClosure* cl) { | |
| nmethods_do_and_prune(cl); | |
| } | |
| void ScavengableNMethods::unlist_nmethod(nmethod* nm, nmethod* prev) { | |
| assert_locked_or_safepoint(CodeCache_lock); | |
| assert((prev == nullptr && _head == nm) || | |
| (prev != nullptr && gc_data(prev).next() == nm), "precondition"); | |
| ScavengableNMethodsData data = gc_data(nm); | |
| if (prev == nullptr) { | |
| _head = data.next(); | |
| } else { | |
| gc_data(prev).set_next(data.next()); | |
| } | |
| data.set_next(nullptr); | |
| data.clear_on_list(); | |
| } | |
| #ifndef PRODUCT | |
| // Temporarily mark nmethods that are claimed to be on the scavenge list. | |
| void ScavengableNMethods::mark_on_list_nmethods() { | |
| NMethodIterator iter(NMethodIterator::all); | |
| 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(); | |
| } | |
| } | |
| } | |
| // Make sure that the effects of mark_on_list_nmethods is gone. | |
| void ScavengableNMethods::verify_nmethods() { | |
| NMethodIterator iter(NMethodIterator::all); | |
| while(iter.next()) { | |
| nmethod* nm = iter.method(); | |
| // Can not verify already unlinked nmethods as they are partially invalid already. | |
| if (!nm->is_unlinked()) { | |
| verify_nmethod(nm); | |
| } | |
| } | |
| } | |
| #endif //PRODUCT |