Path: blob/aarch64-shenandoah-jdk8u272-b10/hotspot/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp
38920 views
/*1* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#include "precompiled.hpp"25#include "code/codeCache.hpp"26#include "code/nmethod.hpp"27#include "gc_implementation/g1/g1CodeCacheRemSet.hpp"28#include "gc_implementation/g1/heapRegion.hpp"29#include "memory/heap.hpp"30#include "memory/iterator.hpp"31#include "oops/oop.inline.hpp"32#include "utilities/hashtable.inline.hpp"33#include "utilities/stack.inline.hpp"3435PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC3637class CodeRootSetTable : public Hashtable<nmethod*, mtGC> {38friend class G1CodeRootSetTest;39typedef HashtableEntry<nmethod*, mtGC> Entry;4041static CodeRootSetTable* volatile _purge_list;4243CodeRootSetTable* _purge_next;4445unsigned int compute_hash(nmethod* nm) {46uintptr_t hash = (uintptr_t)nm;47return hash ^ (hash >> 7); // code heap blocks are 128byte aligned48}4950void remove_entry(Entry* e, Entry* previous);51Entry* new_entry(nmethod* nm);5253public:54CodeRootSetTable(int size) : Hashtable<nmethod*, mtGC>(size, sizeof(Entry)), _purge_next(NULL) {}55~CodeRootSetTable();5657// Needs to be protected locks58bool add(nmethod* nm);59bool remove(nmethod* nm);6061// Can be called without locking62bool contains(nmethod* nm);6364int entry_size() const { return BasicHashtable<mtGC>::entry_size(); }6566void copy_to(CodeRootSetTable* new_table);67void nmethods_do(CodeBlobClosure* blk);6869template<typename CB>70int remove_if(CB& should_remove);7172static void purge_list_append(CodeRootSetTable* tbl);73static void purge();7475static size_t static_mem_size() {76return sizeof(_purge_list);77}7879size_t mem_size();80};8182CodeRootSetTable* volatile CodeRootSetTable::_purge_list = NULL;8384size_t CodeRootSetTable::mem_size() {85return sizeof(CodeRootSetTable) + (entry_size() * number_of_entries()) + (sizeof(HashtableBucket<mtGC>) * table_size());86}8788CodeRootSetTable::Entry* CodeRootSetTable::new_entry(nmethod* nm) {89unsigned int hash = compute_hash(nm);90Entry* entry = (Entry*) new_entry_free_list();91if (entry == NULL) {92entry = (Entry*) NEW_C_HEAP_ARRAY2(char, entry_size(), mtGC, CURRENT_PC);93}94entry->set_next(NULL);95entry->set_hash(hash);96entry->set_literal(nm);97return entry;98}99100void CodeRootSetTable::remove_entry(Entry* e, Entry* previous) {101int index = hash_to_index(e->hash());102assert((e == bucket(index)) == (previous == NULL), "if e is the first entry then previous should be null");103104if (previous == NULL) {105set_entry(index, e->next());106} else {107previous->set_next(e->next());108}109free_entry(e);110}111112CodeRootSetTable::~CodeRootSetTable() {113for (int index = 0; index < table_size(); ++index) {114for (Entry* e = bucket(index); e != NULL; ) {115Entry* to_remove = e;116// read next before freeing.117e = e->next();118unlink_entry(to_remove);119FREE_C_HEAP_ARRAY(char, to_remove, mtGC);120}121}122assert(number_of_entries() == 0, "should have removed all entries");123free_buckets();124for (BasicHashtableEntry<mtGC>* e = new_entry_free_list(); e != NULL; e = new_entry_free_list()) {125FREE_C_HEAP_ARRAY(char, e, mtGC);126}127}128129bool CodeRootSetTable::add(nmethod* nm) {130if (!contains(nm)) {131Entry* e = new_entry(nm);132int index = hash_to_index(e->hash());133add_entry(index, e);134return true;135}136return false;137}138139bool CodeRootSetTable::contains(nmethod* nm) {140int index = hash_to_index(compute_hash(nm));141for (Entry* e = bucket(index); e != NULL; e = e->next()) {142if (e->literal() == nm) {143return true;144}145}146return false;147}148149bool CodeRootSetTable::remove(nmethod* nm) {150int index = hash_to_index(compute_hash(nm));151Entry* previous = NULL;152for (Entry* e = bucket(index); e != NULL; previous = e, e = e->next()) {153if (e->literal() == nm) {154remove_entry(e, previous);155return true;156}157}158return false;159}160161void CodeRootSetTable::copy_to(CodeRootSetTable* new_table) {162for (int index = 0; index < table_size(); ++index) {163for (Entry* e = bucket(index); e != NULL; e = e->next()) {164new_table->add(e->literal());165}166}167new_table->copy_freelist(this);168}169170void CodeRootSetTable::nmethods_do(CodeBlobClosure* blk) {171for (int index = 0; index < table_size(); ++index) {172for (Entry* e = bucket(index); e != NULL; e = e->next()) {173blk->do_code_blob(e->literal());174}175}176}177178template<typename CB>179int CodeRootSetTable::remove_if(CB& should_remove) {180int num_removed = 0;181for (int index = 0; index < table_size(); ++index) {182Entry* previous = NULL;183Entry* e = bucket(index);184while (e != NULL) {185Entry* next = e->next();186if (should_remove(e->literal())) {187remove_entry(e, previous);188++num_removed;189} else {190previous = e;191}192e = next;193}194}195return num_removed;196}197198G1CodeRootSet::~G1CodeRootSet() {199delete _table;200}201202CodeRootSetTable* G1CodeRootSet::load_acquire_table() {203return (CodeRootSetTable*) OrderAccess::load_ptr_acquire(&_table);204}205206void G1CodeRootSet::allocate_small_table() {207CodeRootSetTable* temp = new CodeRootSetTable(SmallSize);208209OrderAccess::release_store_ptr(&_table, temp);210}211212void CodeRootSetTable::purge_list_append(CodeRootSetTable* table) {213for (;;) {214table->_purge_next = _purge_list;215CodeRootSetTable* old = (CodeRootSetTable*) Atomic::cmpxchg_ptr(table, &_purge_list, table->_purge_next);216if (old == table->_purge_next) {217break;218}219}220}221222void CodeRootSetTable::purge() {223CodeRootSetTable* table = _purge_list;224_purge_list = NULL;225while (table != NULL) {226CodeRootSetTable* to_purge = table;227table = table->_purge_next;228delete to_purge;229}230}231232void G1CodeRootSet::move_to_large() {233CodeRootSetTable* temp = new CodeRootSetTable(LargeSize);234235_table->copy_to(temp);236237CodeRootSetTable::purge_list_append(_table);238239OrderAccess::release_store_ptr(&_table, temp);240}241242void G1CodeRootSet::purge() {243CodeRootSetTable::purge();244}245246size_t G1CodeRootSet::static_mem_size() {247return CodeRootSetTable::static_mem_size();248}249250void G1CodeRootSet::add(nmethod* method) {251bool added = false;252if (is_empty()) {253allocate_small_table();254}255added = _table->add(method);256if (added) {257if (_length == Threshold) {258move_to_large();259}260++_length;261}262assert(_length == (size_t)_table->number_of_entries(), "sizes should match");263}264265bool G1CodeRootSet::remove(nmethod* method) {266bool removed = false;267if (_table != NULL) {268removed = _table->remove(method);269}270if (removed) {271_length--;272if (_length == 0) {273clear();274}275}276assert((_length == 0 && _table == NULL) ||277(_length == (size_t)_table->number_of_entries()), "sizes should match");278return removed;279}280281bool G1CodeRootSet::contains(nmethod* method) {282CodeRootSetTable* table = load_acquire_table(); // contains() may be called outside of lock, so ensure mem sync.283if (table != NULL) {284return table->contains(method);285}286return false;287}288289void G1CodeRootSet::clear() {290delete _table;291_table = NULL;292_length = 0;293}294295size_t G1CodeRootSet::mem_size() {296return sizeof(*this) + (_table != NULL ? _table->mem_size() : 0);297}298299void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {300if (_table != NULL) {301_table->nmethods_do(blk);302}303}304305class CleanCallback : public StackObj {306class PointsIntoHRDetectionClosure : public OopClosure {307HeapRegion* _hr;308public:309bool _points_into;310PointsIntoHRDetectionClosure(HeapRegion* hr) : _hr(hr), _points_into(false) {}311312void do_oop(narrowOop* o) {313do_oop_work(o);314}315316void do_oop(oop* o) {317do_oop_work(o);318}319320template <typename T>321void do_oop_work(T* p) {322if (_hr->is_in(oopDesc::load_decode_heap_oop(p))) {323_points_into = true;324}325}326};327328PointsIntoHRDetectionClosure _detector;329CodeBlobToOopClosure _blobs;330331public:332CleanCallback(HeapRegion* hr) : _detector(hr), _blobs(&_detector, !CodeBlobToOopClosure::FixRelocations) {}333334bool operator() (nmethod* nm) {335_detector._points_into = false;336_blobs.do_code_blob(nm);337return !_detector._points_into;338}339};340341void G1CodeRootSet::clean(HeapRegion* owner) {342CleanCallback should_clean(owner);343if (_table != NULL) {344int removed = _table->remove_if(should_clean);345assert((size_t)removed <= _length, "impossible");346_length -= removed;347}348if (_length == 0) {349clear();350}351}352353#ifndef PRODUCT354355class G1CodeRootSetTest {356public:357static void test() {358{359G1CodeRootSet set1;360assert(set1.is_empty(), "Code root set must be initially empty but is not.");361362assert(G1CodeRootSet::static_mem_size() == sizeof(void*),363err_msg("The code root set's static memory usage is incorrect, " SIZE_FORMAT " bytes", G1CodeRootSet::static_mem_size()));364365set1.add((nmethod*)1);366assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "367SIZE_FORMAT " elements", set1.length()));368369const size_t num_to_add = (size_t)G1CodeRootSet::Threshold + 1;370371for (size_t i = 1; i <= num_to_add; i++) {372set1.add((nmethod*)1);373}374assert(set1.length() == 1,375err_msg("Duplicate detection should not have increased the set size but "376"is " SIZE_FORMAT, set1.length()));377378for (size_t i = 2; i <= num_to_add; i++) {379set1.add((nmethod*)(uintptr_t)(i));380}381assert(set1.length() == num_to_add,382err_msg("After adding in total " SIZE_FORMAT " distinct code roots, they "383"need to be in the set, but there are only " SIZE_FORMAT,384num_to_add, set1.length()));385386assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");387388size_t num_popped = 0;389for (size_t i = 1; i <= num_to_add; i++) {390bool removed = set1.remove((nmethod*)i);391if (removed) {392num_popped += 1;393} else {394break;395}396}397assert(num_popped == num_to_add,398err_msg("Managed to pop " SIZE_FORMAT " code roots, but only " SIZE_FORMAT " "399"were added", num_popped, num_to_add));400assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");401402G1CodeRootSet::purge();403404assert(CodeRootSetTable::_purge_list == NULL, "should have purged old small tables");405406}407408}409};410411void TestCodeCacheRemSet_test() {412G1CodeRootSetTest::test();413}414415#endif416417418