src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp

changeset 7208
7baf47cb97cb
parent 6993
870c03421152
child 7209
58925d1f325e
     1.1 --- a/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp	Fri Aug 29 13:08:01 2014 +0200
     1.2 +++ b/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp	Fri Aug 29 13:12:21 2014 +0200
     1.3 @@ -22,372 +22,375 @@
     1.4   *
     1.5   */
     1.6  
     1.7 -
     1.8  #include "precompiled.hpp"
     1.9 +#include "code/codeCache.hpp"
    1.10  #include "code/nmethod.hpp"
    1.11  #include "gc_implementation/g1/g1CodeCacheRemSet.hpp"
    1.12 +#include "gc_implementation/g1/heapRegion.hpp"
    1.13 +#include "memory/heap.hpp"
    1.14  #include "memory/iterator.hpp"
    1.15 +#include "oops/oop.inline.hpp"
    1.16 +#include "utilities/hashtable.inline.hpp"
    1.17 +#include "utilities/stack.inline.hpp"
    1.18  
    1.19  PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
    1.20  
    1.21 -G1CodeRootChunk::G1CodeRootChunk() : _top(NULL), _next(NULL), _prev(NULL), _free(NULL) {
    1.22 -  _top = bottom();
    1.23 +class CodeRootSetTable : public Hashtable<nmethod*, mtGC> {
    1.24 +  friend class G1CodeRootSetTest;
    1.25 +  typedef HashtableEntry<nmethod*, mtGC> Entry;
    1.26 +
    1.27 +  static CodeRootSetTable* volatile _purge_list;
    1.28 +
    1.29 +  CodeRootSetTable* _purge_next;
    1.30 +
    1.31 +  unsigned int compute_hash(nmethod* nm) {
    1.32 +    uintptr_t hash = (uintptr_t)nm;
    1.33 +    return hash ^ (hash >> 7); // code heap blocks are 128byte aligned
    1.34 +  }
    1.35 +
    1.36 +  Entry* new_entry(nmethod* nm);
    1.37 +
    1.38 + public:
    1.39 +  CodeRootSetTable(int size) : Hashtable<nmethod*, mtGC>(size, sizeof(Entry)), _purge_next(NULL) {}
    1.40 +  ~CodeRootSetTable();
    1.41 +
    1.42 +  // Needs to be protected locks
    1.43 +  bool add(nmethod* nm);
    1.44 +  bool remove(nmethod* nm);
    1.45 +
    1.46 +  // Can be called without locking
    1.47 +  bool contains(nmethod* nm);
    1.48 +
    1.49 +  int entry_size() const { return BasicHashtable<mtGC>::entry_size(); }
    1.50 +
    1.51 +  void copy_to(CodeRootSetTable* new_table);
    1.52 +  void nmethods_do(CodeBlobClosure* blk);
    1.53 +
    1.54 +  template<typename CB>
    1.55 +  void remove_if(CB& should_remove);
    1.56 +
    1.57 +  static void purge_list_append(CodeRootSetTable* tbl);
    1.58 +  static void purge();
    1.59 +
    1.60 +  static size_t static_mem_size() {
    1.61 +    return sizeof(_purge_list);
    1.62 +  }
    1.63 +};
    1.64 +
    1.65 +CodeRootSetTable* volatile CodeRootSetTable::_purge_list = NULL;
    1.66 +
    1.67 +CodeRootSetTable::Entry* CodeRootSetTable::new_entry(nmethod* nm) {
    1.68 +  unsigned int hash = compute_hash(nm);
    1.69 +  Entry* entry = (Entry*) new_entry_free_list();
    1.70 +  if (entry == NULL) {
    1.71 +    entry = (Entry*) NEW_C_HEAP_ARRAY2(char, entry_size(), mtGC, CURRENT_PC);
    1.72 +  }
    1.73 +  entry->set_next(NULL);
    1.74 +  entry->set_hash(hash);
    1.75 +  entry->set_literal(nm);
    1.76 +  return entry;
    1.77  }
    1.78  
    1.79 -void G1CodeRootChunk::reset() {
    1.80 -  _next = _prev = NULL;
    1.81 -  _free = NULL;
    1.82 -  _top = bottom();
    1.83 -}
    1.84 -
    1.85 -void G1CodeRootChunk::nmethods_do(CodeBlobClosure* cl) {
    1.86 -  NmethodOrLink* cur = bottom();
    1.87 -  while (cur != _top) {
    1.88 -    if (is_nmethod(cur)) {
    1.89 -      cl->do_code_blob(cur->_nmethod);
    1.90 +CodeRootSetTable::~CodeRootSetTable() {
    1.91 +  for (int index = 0; index < table_size(); ++index) {
    1.92 +    for (Entry* e = bucket(index); e != NULL; ) {
    1.93 +      Entry* to_remove = e;
    1.94 +      // read next before freeing.
    1.95 +      e = e->next();
    1.96 +      unlink_entry(to_remove);
    1.97 +      FREE_C_HEAP_ARRAY(char, to_remove, mtGC);
    1.98      }
    1.99 -    cur++;
   1.100 +  }
   1.101 +  assert(number_of_entries() == 0, "should have removed all entries");
   1.102 +  free_buckets();
   1.103 +  for (BasicHashtableEntry<mtGC>* e = new_entry_free_list(); e != NULL; e = new_entry_free_list()) {
   1.104 +    FREE_C_HEAP_ARRAY(char, e, mtGC);
   1.105    }
   1.106  }
   1.107  
   1.108 -bool G1CodeRootChunk::remove_lock_free(nmethod* method) {
   1.109 -  NmethodOrLink* cur = bottom();
   1.110 +bool CodeRootSetTable::add(nmethod* nm) {
   1.111 +  if (!contains(nm)) {
   1.112 +    Entry* e = new_entry(nm);
   1.113 +    int index = hash_to_index(e->hash());
   1.114 +    add_entry(index, e);
   1.115 +    return true;
   1.116 +  }
   1.117 +  return false;
   1.118 +}
   1.119  
   1.120 -  for (NmethodOrLink* cur = bottom(); cur != _top; cur++) {
   1.121 -    if (cur->_nmethod == method) {
   1.122 -      bool result = Atomic::cmpxchg_ptr(NULL, &cur->_nmethod, method) == method;
   1.123 -
   1.124 -      if (!result) {
   1.125 -        // Someone else cleared out this entry.
   1.126 -        return false;
   1.127 -      }
   1.128 -
   1.129 -      // The method was cleared. Time to link it into the free list.
   1.130 -      NmethodOrLink* prev_free;
   1.131 -      do {
   1.132 -        prev_free = (NmethodOrLink*)_free;
   1.133 -        cur->_link = prev_free;
   1.134 -      } while (Atomic::cmpxchg_ptr(cur, &_free, prev_free) != prev_free);
   1.135 -
   1.136 +bool CodeRootSetTable::contains(nmethod* nm) {
   1.137 +  int index = hash_to_index(compute_hash(nm));
   1.138 +  for (Entry* e = bucket(index); e != NULL; e = e->next()) {
   1.139 +    if (e->literal() == nm) {
   1.140        return true;
   1.141      }
   1.142    }
   1.143 -
   1.144    return false;
   1.145  }
   1.146  
   1.147 -G1CodeRootChunkManager::G1CodeRootChunkManager() : _free_list(), _num_chunks_handed_out(0) {
   1.148 -  _free_list.initialize();
   1.149 -  _free_list.set_size(G1CodeRootChunk::word_size());
   1.150 +bool CodeRootSetTable::remove(nmethod* nm) {
   1.151 +  int index = hash_to_index(compute_hash(nm));
   1.152 +  Entry* previous = NULL;
   1.153 +  for (Entry* e = bucket(index); e != NULL; previous = e, e = e->next()) {
   1.154 +    if (e->literal() == nm) {
   1.155 +      if (previous != NULL) {
   1.156 +        previous->set_next(e->next());
   1.157 +      } else {
   1.158 +        set_entry(index, e->next());
   1.159 +      }
   1.160 +      free_entry(e);
   1.161 +      return true;
   1.162 +    }
   1.163 +  }
   1.164 +  return false;
   1.165  }
   1.166  
   1.167 -size_t G1CodeRootChunkManager::fl_mem_size() {
   1.168 -  return _free_list.count() * _free_list.size();
   1.169 +void CodeRootSetTable::copy_to(CodeRootSetTable* new_table) {
   1.170 +  for (int index = 0; index < table_size(); ++index) {
   1.171 +    for (Entry* e = bucket(index); e != NULL; e = e->next()) {
   1.172 +      new_table->add(e->literal());
   1.173 +    }
   1.174 +  }
   1.175 +  new_table->copy_freelist(this);
   1.176  }
   1.177  
   1.178 -void G1CodeRootChunkManager::free_all_chunks(FreeList<G1CodeRootChunk>* list) {
   1.179 -  _num_chunks_handed_out -= list->count();
   1.180 -  _free_list.prepend(list);
   1.181 -}
   1.182 -
   1.183 -void G1CodeRootChunkManager::free_chunk(G1CodeRootChunk* chunk) {
   1.184 -  _free_list.return_chunk_at_head(chunk);
   1.185 -  _num_chunks_handed_out--;
   1.186 -}
   1.187 -
   1.188 -void G1CodeRootChunkManager::purge_chunks(size_t keep_ratio) {
   1.189 -  size_t keep = _num_chunks_handed_out * keep_ratio / 100;
   1.190 -  if (keep >= (size_t)_free_list.count()) {
   1.191 -    return;
   1.192 -  }
   1.193 -
   1.194 -  FreeList<G1CodeRootChunk> temp;
   1.195 -  temp.initialize();
   1.196 -  temp.set_size(G1CodeRootChunk::word_size());
   1.197 -
   1.198 -  _free_list.getFirstNChunksFromList((size_t)_free_list.count() - keep, &temp);
   1.199 -
   1.200 -  G1CodeRootChunk* cur = temp.get_chunk_at_head();
   1.201 -  while (cur != NULL) {
   1.202 -    delete cur;
   1.203 -    cur = temp.get_chunk_at_head();
   1.204 +void CodeRootSetTable::nmethods_do(CodeBlobClosure* blk) {
   1.205 +  for (int index = 0; index < table_size(); ++index) {
   1.206 +    for (Entry* e = bucket(index); e != NULL; e = e->next()) {
   1.207 +      blk->do_code_blob(e->literal());
   1.208 +    }
   1.209    }
   1.210  }
   1.211  
   1.212 -size_t G1CodeRootChunkManager::static_mem_size() {
   1.213 -  return sizeof(G1CodeRootChunkManager);
   1.214 +template<typename CB>
   1.215 +void CodeRootSetTable::remove_if(CB& should_remove) {
   1.216 +  for (int index = 0; index < table_size(); ++index) {
   1.217 +    Entry* previous = NULL;
   1.218 +    Entry* e = bucket(index);
   1.219 +    while (e != NULL) {
   1.220 +      Entry* next = e->next();
   1.221 +      if (should_remove(e->literal())) {
   1.222 +        if (previous != NULL) {
   1.223 +          previous->set_next(next);
   1.224 +        } else {
   1.225 +          set_entry(index, next);
   1.226 +        }
   1.227 +        free_entry(e);
   1.228 +      } else {
   1.229 +        previous = e;
   1.230 +      }
   1.231 +      e = next;
   1.232 +    }
   1.233 +  }
   1.234  }
   1.235  
   1.236 +G1CodeRootSet::~G1CodeRootSet() {
   1.237 +  delete _table;
   1.238 +}
   1.239  
   1.240 -G1CodeRootChunk* G1CodeRootChunkManager::new_chunk() {
   1.241 -  G1CodeRootChunk* result = _free_list.get_chunk_at_head();
   1.242 -  if (result == NULL) {
   1.243 -    result = new G1CodeRootChunk();
   1.244 +CodeRootSetTable* G1CodeRootSet::load_acquire_table() {
   1.245 +  return (CodeRootSetTable*) OrderAccess::load_ptr_acquire(&_table);
   1.246 +}
   1.247 +
   1.248 +void G1CodeRootSet::allocate_small_table() {
   1.249 +  _table = new CodeRootSetTable(SmallSize);
   1.250 +}
   1.251 +
   1.252 +void CodeRootSetTable::purge_list_append(CodeRootSetTable* table) {
   1.253 +  for (;;) {
   1.254 +    table->_purge_next = _purge_list;
   1.255 +    CodeRootSetTable* old = (CodeRootSetTable*) Atomic::cmpxchg_ptr(table, &_purge_list, table->_purge_next);
   1.256 +    if (old == table->_purge_next) {
   1.257 +      break;
   1.258 +    }
   1.259    }
   1.260 -  _num_chunks_handed_out++;
   1.261 -  result->reset();
   1.262 -  return result;
   1.263 +}
   1.264 +
   1.265 +void CodeRootSetTable::purge() {
   1.266 +  CodeRootSetTable* table = _purge_list;
   1.267 +  _purge_list = NULL;
   1.268 +  while (table != NULL) {
   1.269 +    CodeRootSetTable* to_purge = table;
   1.270 +    table = table->_purge_next;
   1.271 +    delete to_purge;
   1.272 +  }
   1.273 +}
   1.274 +
   1.275 +void G1CodeRootSet::move_to_large() {
   1.276 +  CodeRootSetTable* temp = new CodeRootSetTable(LargeSize);
   1.277 +
   1.278 +  _table->copy_to(temp);
   1.279 +
   1.280 +  CodeRootSetTable::purge_list_append(_table);
   1.281 +
   1.282 +  OrderAccess::release_store_ptr(&_table, temp);
   1.283 +}
   1.284 +
   1.285 +
   1.286 +void G1CodeRootSet::purge() {
   1.287 +  CodeRootSetTable::purge();
   1.288 +}
   1.289 +
   1.290 +size_t G1CodeRootSet::static_mem_size() {
   1.291 +  return CodeRootSetTable::static_mem_size();
   1.292 +}
   1.293 +
   1.294 +void G1CodeRootSet::add(nmethod* method) {
   1.295 +  bool added = false;
   1.296 +  if (is_empty()) {
   1.297 +    allocate_small_table();
   1.298 +  }
   1.299 +  added = _table->add(method);
   1.300 +  if (_length == Threshold) {
   1.301 +    move_to_large();
   1.302 +  }
   1.303 +  if (added) {
   1.304 +    ++_length;
   1.305 +  }
   1.306 +}
   1.307 +
   1.308 +bool G1CodeRootSet::remove(nmethod* method) {
   1.309 +  bool removed = false;
   1.310 +  if (_table != NULL) {
   1.311 +    removed = _table->remove(method);
   1.312 +  }
   1.313 +  if (removed) {
   1.314 +    _length--;
   1.315 +    if (_length == 0) {
   1.316 +      clear();
   1.317 +    }
   1.318 +  }
   1.319 +  return removed;
   1.320 +}
   1.321 +
   1.322 +bool G1CodeRootSet::contains(nmethod* method) {
   1.323 +  CodeRootSetTable* table = load_acquire_table();
   1.324 +  if (table != NULL) {
   1.325 +    return table->contains(method);
   1.326 +  }
   1.327 +  return false;
   1.328 +}
   1.329 +
   1.330 +void G1CodeRootSet::clear() {
   1.331 +  delete _table;
   1.332 +  _table = NULL;
   1.333 +  _length = 0;
   1.334 +}
   1.335 +
   1.336 +size_t G1CodeRootSet::mem_size() {
   1.337 +  return sizeof(*this) +
   1.338 +      (_table != NULL ? sizeof(CodeRootSetTable) + _table->entry_size() * _length : 0);
   1.339 +}
   1.340 +
   1.341 +void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
   1.342 +  if (_table != NULL) {
   1.343 +    _table->nmethods_do(blk);
   1.344 +  }
   1.345 +}
   1.346 +
   1.347 +class CleanCallback : public StackObj {
   1.348 +  class PointsIntoHRDetectionClosure : public OopClosure {
   1.349 +    HeapRegion* _hr;
   1.350 +   public:
   1.351 +    bool _points_into;
   1.352 +    PointsIntoHRDetectionClosure(HeapRegion* hr) : _hr(hr), _points_into(false) {}
   1.353 +
   1.354 +    void do_oop(narrowOop* o) {
   1.355 +      do_oop_work(o);
   1.356 +    }
   1.357 +
   1.358 +    void do_oop(oop* o) {
   1.359 +      do_oop_work(o);
   1.360 +    }
   1.361 +
   1.362 +    template <typename T>
   1.363 +    void do_oop_work(T* p) {
   1.364 +      if (_hr->is_in(oopDesc::load_decode_heap_oop(p))) {
   1.365 +        _points_into = true;
   1.366 +      }
   1.367 +    }
   1.368 +  };
   1.369 +
   1.370 +  PointsIntoHRDetectionClosure _detector;
   1.371 +  CodeBlobToOopClosure _blobs;
   1.372 +
   1.373 + public:
   1.374 +  CleanCallback(HeapRegion* hr) : _detector(hr), _blobs(&_detector, !CodeBlobToOopClosure::FixRelocations) {}
   1.375 +
   1.376 +  bool operator() (nmethod* nm) {
   1.377 +    _detector._points_into = false;
   1.378 +    _blobs.do_code_blob(nm);
   1.379 +    return _detector._points_into;
   1.380 +  }
   1.381 +};
   1.382 +
   1.383 +void G1CodeRootSet::clean(HeapRegion* owner) {
   1.384 +  CleanCallback should_clean(owner);
   1.385 +  if (_table != NULL) {
   1.386 +    _table->remove_if(should_clean);
   1.387 +  }
   1.388  }
   1.389  
   1.390  #ifndef PRODUCT
   1.391  
   1.392 -size_t G1CodeRootChunkManager::num_chunks_handed_out() const {
   1.393 -  return _num_chunks_handed_out;
   1.394 -}
   1.395 +class G1CodeRootSetTest {
   1.396 + public:
   1.397 +  static void test() {
   1.398 +    {
   1.399 +      G1CodeRootSet set1;
   1.400 +      assert(set1.is_empty(), "Code root set must be initially empty but is not.");
   1.401  
   1.402 -size_t G1CodeRootChunkManager::num_free_chunks() const {
   1.403 -  return (size_t)_free_list.count();
   1.404 +      assert(G1CodeRootSet::static_mem_size() == sizeof(void*),
   1.405 +          err_msg("The code root set's static memory usage is incorrect, "SIZE_FORMAT" bytes", G1CodeRootSet::static_mem_size()));
   1.406 +
   1.407 +      set1.add((nmethod*)1);
   1.408 +      assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "
   1.409 +          SIZE_FORMAT" elements", set1.length()));
   1.410 +
   1.411 +      const size_t num_to_add = (size_t)G1CodeRootSet::Threshold + 1;
   1.412 +
   1.413 +      for (size_t i = 1; i <= num_to_add; i++) {
   1.414 +        set1.add((nmethod*)1);
   1.415 +      }
   1.416 +      assert(set1.length() == 1,
   1.417 +          err_msg("Duplicate detection should not have increased the set size but "
   1.418 +              "is "SIZE_FORMAT, set1.length()));
   1.419 +
   1.420 +      for (size_t i = 2; i <= num_to_add; i++) {
   1.421 +        set1.add((nmethod*)(uintptr_t)(i));
   1.422 +      }
   1.423 +      assert(set1.length() == num_to_add,
   1.424 +          err_msg("After adding in total "SIZE_FORMAT" distinct code roots, they "
   1.425 +              "need to be in the set, but there are only "SIZE_FORMAT,
   1.426 +              num_to_add, set1.length()));
   1.427 +
   1.428 +      assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");
   1.429 +
   1.430 +      size_t num_popped = 0;
   1.431 +      for (size_t i = 1; i <= num_to_add; i++) {
   1.432 +        bool removed = set1.remove((nmethod*)i);
   1.433 +        if (removed) {
   1.434 +          num_popped += 1;
   1.435 +        } else {
   1.436 +          break;
   1.437 +        }
   1.438 +      }
   1.439 +      assert(num_popped == num_to_add,
   1.440 +          err_msg("Managed to pop "SIZE_FORMAT" code roots, but only "SIZE_FORMAT" "
   1.441 +              "were added", num_popped, num_to_add));
   1.442 +      assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");
   1.443 +
   1.444 +      G1CodeRootSet::purge();
   1.445 +
   1.446 +      assert(CodeRootSetTable::_purge_list == NULL, "should have purged old small tables");
   1.447 +
   1.448 +    }
   1.449 +
   1.450 +  }
   1.451 +};
   1.452 +
   1.453 +void TestCodeCacheRemSet_test() {
   1.454 +  G1CodeRootSetTest::test();
   1.455  }
   1.456  
   1.457  #endif
   1.458 -
   1.459 -G1CodeRootChunkManager G1CodeRootSet::_default_chunk_manager;
   1.460 -
   1.461 -void G1CodeRootSet::purge_chunks(size_t keep_ratio) {
   1.462 -  _default_chunk_manager.purge_chunks(keep_ratio);
   1.463 -}
   1.464 -
   1.465 -size_t G1CodeRootSet::free_chunks_static_mem_size() {
   1.466 -  return _default_chunk_manager.static_mem_size();
   1.467 -}
   1.468 -
   1.469 -size_t G1CodeRootSet::free_chunks_mem_size() {
   1.470 -  return _default_chunk_manager.fl_mem_size();
   1.471 -}
   1.472 -
   1.473 -G1CodeRootSet::G1CodeRootSet(G1CodeRootChunkManager* manager) : _manager(manager), _list(), _length(0) {
   1.474 -  if (_manager == NULL) {
   1.475 -    _manager = &_default_chunk_manager;
   1.476 -  }
   1.477 -  _list.initialize();
   1.478 -  _list.set_size(G1CodeRootChunk::word_size());
   1.479 -}
   1.480 -
   1.481 -G1CodeRootSet::~G1CodeRootSet() {
   1.482 -  clear();
   1.483 -}
   1.484 -
   1.485 -void G1CodeRootSet::add(nmethod* method) {
   1.486 -  if (!contains(method)) {
   1.487 -    // Find the first chunk that isn't full.
   1.488 -    G1CodeRootChunk* cur = _list.head();
   1.489 -    while (cur != NULL) {
   1.490 -      if (!cur->is_full()) {
   1.491 -        break;
   1.492 -      }
   1.493 -      cur = cur->next();
   1.494 -    }
   1.495 -
   1.496 -    // All chunks are full, get a new chunk.
   1.497 -    if (cur == NULL) {
   1.498 -      cur = new_chunk();
   1.499 -      _list.return_chunk_at_head(cur);
   1.500 -    }
   1.501 -
   1.502 -    // Add the nmethod.
   1.503 -    bool result = cur->add(method);
   1.504 -
   1.505 -    guarantee(result, err_msg("Not able to add nmethod "PTR_FORMAT" to newly allocated chunk.", method));
   1.506 -
   1.507 -    _length++;
   1.508 -  }
   1.509 -}
   1.510 -
   1.511 -void G1CodeRootSet::remove_lock_free(nmethod* method) {
   1.512 -  G1CodeRootChunk* found = find(method);
   1.513 -  if (found != NULL) {
   1.514 -    bool result = found->remove_lock_free(method);
   1.515 -    if (result) {
   1.516 -      Atomic::dec_ptr((volatile intptr_t*)&_length);
   1.517 -    }
   1.518 -  }
   1.519 -  assert(!contains(method), err_msg(PTR_FORMAT" still contains nmethod "PTR_FORMAT, this, method));
   1.520 -}
   1.521 -
   1.522 -nmethod* G1CodeRootSet::pop() {
   1.523 -  while (true) {
   1.524 -    G1CodeRootChunk* cur = _list.head();
   1.525 -    if (cur == NULL) {
   1.526 -      assert(_length == 0, "when there are no chunks, there should be no elements");
   1.527 -      return NULL;
   1.528 -    }
   1.529 -    nmethod* result = cur->pop();
   1.530 -    if (result != NULL) {
   1.531 -      _length--;
   1.532 -      return result;
   1.533 -    } else {
   1.534 -      free(_list.get_chunk_at_head());
   1.535 -    }
   1.536 -  }
   1.537 -}
   1.538 -
   1.539 -G1CodeRootChunk* G1CodeRootSet::find(nmethod* method) {
   1.540 -  G1CodeRootChunk* cur = _list.head();
   1.541 -  while (cur != NULL) {
   1.542 -    if (cur->contains(method)) {
   1.543 -      return cur;
   1.544 -    }
   1.545 -    cur = (G1CodeRootChunk*)cur->next();
   1.546 -  }
   1.547 -  return NULL;
   1.548 -}
   1.549 -
   1.550 -void G1CodeRootSet::free(G1CodeRootChunk* chunk) {
   1.551 -  free_chunk(chunk);
   1.552 -}
   1.553 -
   1.554 -bool G1CodeRootSet::contains(nmethod* method) {
   1.555 -  return find(method) != NULL;
   1.556 -}
   1.557 -
   1.558 -void G1CodeRootSet::clear() {
   1.559 -  free_all_chunks(&_list);
   1.560 -  _length = 0;
   1.561 -}
   1.562 -
   1.563 -void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
   1.564 -  G1CodeRootChunk* cur = _list.head();
   1.565 -  while (cur != NULL) {
   1.566 -    cur->nmethods_do(blk);
   1.567 -    cur = (G1CodeRootChunk*)cur->next();
   1.568 -  }
   1.569 -}
   1.570 -
   1.571 -size_t G1CodeRootSet::static_mem_size() {
   1.572 -  return sizeof(G1CodeRootSet);
   1.573 -}
   1.574 -
   1.575 -size_t G1CodeRootSet::mem_size() {
   1.576 -  return G1CodeRootSet::static_mem_size() + _list.count() * _list.size();
   1.577 -}
   1.578 -
   1.579 -#ifndef PRODUCT
   1.580 -
   1.581 -void G1CodeRootSet::test() {
   1.582 -  G1CodeRootChunkManager mgr;
   1.583 -
   1.584 -  assert(mgr.num_chunks_handed_out() == 0, "Must not have handed out chunks yet");
   1.585 -
   1.586 -  assert(G1CodeRootChunkManager::static_mem_size() > sizeof(void*),
   1.587 -         err_msg("The chunk manager's static memory usage seems too small, is only "SIZE_FORMAT" bytes.", G1CodeRootChunkManager::static_mem_size()));
   1.588 -
   1.589 -  // The number of chunks that we allocate for purge testing.
   1.590 -  size_t const num_chunks = 10;
   1.591 -
   1.592 -  {
   1.593 -    G1CodeRootSet set1(&mgr);
   1.594 -    assert(set1.is_empty(), "Code root set must be initially empty but is not.");
   1.595 -
   1.596 -    assert(G1CodeRootSet::static_mem_size() > sizeof(void*),
   1.597 -           err_msg("The code root set's static memory usage seems too small, is only "SIZE_FORMAT" bytes", G1CodeRootSet::static_mem_size()));
   1.598 -
   1.599 -    set1.add((nmethod*)1);
   1.600 -    assert(mgr.num_chunks_handed_out() == 1,
   1.601 -           err_msg("Must have allocated and handed out one chunk, but handed out "
   1.602 -                   SIZE_FORMAT" chunks", mgr.num_chunks_handed_out()));
   1.603 -    assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "
   1.604 -                                       SIZE_FORMAT" elements", set1.length()));
   1.605 -
   1.606 -    // G1CodeRootChunk::word_size() is larger than G1CodeRootChunk::num_entries which
   1.607 -    // we cannot access.
   1.608 -    for (uint i = 0; i < G1CodeRootChunk::word_size() + 1; i++) {
   1.609 -      set1.add((nmethod*)1);
   1.610 -    }
   1.611 -    assert(mgr.num_chunks_handed_out() == 1,
   1.612 -           err_msg("Duplicate detection must have prevented allocation of further "
   1.613 -                   "chunks but allocated "SIZE_FORMAT, mgr.num_chunks_handed_out()));
   1.614 -    assert(set1.length() == 1,
   1.615 -           err_msg("Duplicate detection should not have increased the set size but "
   1.616 -                   "is "SIZE_FORMAT, set1.length()));
   1.617 -
   1.618 -    size_t num_total_after_add = G1CodeRootChunk::word_size() + 1;
   1.619 -    for (size_t i = 0; i < num_total_after_add - 1; i++) {
   1.620 -      set1.add((nmethod*)(uintptr_t)(2 + i));
   1.621 -    }
   1.622 -    assert(mgr.num_chunks_handed_out() > 1,
   1.623 -           "After adding more code roots, more than one additional chunk should have been handed out");
   1.624 -    assert(set1.length() == num_total_after_add,
   1.625 -           err_msg("After adding in total "SIZE_FORMAT" distinct code roots, they "
   1.626 -                   "need to be in the set, but there are only "SIZE_FORMAT,
   1.627 -                   num_total_after_add, set1.length()));
   1.628 -
   1.629 -    size_t num_popped = 0;
   1.630 -    while (set1.pop() != NULL) {
   1.631 -      num_popped++;
   1.632 -    }
   1.633 -    assert(num_popped == num_total_after_add,
   1.634 -           err_msg("Managed to pop "SIZE_FORMAT" code roots, but only "SIZE_FORMAT" "
   1.635 -                   "were added", num_popped, num_total_after_add));
   1.636 -    assert(mgr.num_chunks_handed_out() == 0,
   1.637 -           err_msg("After popping all elements, all chunks must have been returned "
   1.638 -                   "but there are still "SIZE_FORMAT" additional", mgr.num_chunks_handed_out()));
   1.639 -
   1.640 -    mgr.purge_chunks(0);
   1.641 -    assert(mgr.num_free_chunks() == 0,
   1.642 -           err_msg("After purging everything, the free list must be empty but still "
   1.643 -                   "contains "SIZE_FORMAT" chunks", mgr.num_free_chunks()));
   1.644 -
   1.645 -    // Add some more handed out chunks.
   1.646 -    size_t i = 0;
   1.647 -    while (mgr.num_chunks_handed_out() < num_chunks) {
   1.648 -      set1.add((nmethod*)i);
   1.649 -      i++;
   1.650 -    }
   1.651 -
   1.652 -    {
   1.653 -      // Generate chunks on the free list.
   1.654 -      G1CodeRootSet set2(&mgr);
   1.655 -      size_t i = 0;
   1.656 -      while (mgr.num_chunks_handed_out() < (num_chunks * 2)) {
   1.657 -        set2.add((nmethod*)i);
   1.658 -        i++;
   1.659 -      }
   1.660 -      // Exit of the scope of the set2 object will call the destructor that generates
   1.661 -      // num_chunks elements on the free list.
   1.662 -    }
   1.663 -
   1.664 -    assert(mgr.num_chunks_handed_out() == num_chunks,
   1.665 -           err_msg("Deletion of the second set must have resulted in giving back "
   1.666 -                   "those, but there are still "SIZE_FORMAT" additional handed out, expecting "
   1.667 -                   SIZE_FORMAT, mgr.num_chunks_handed_out(), num_chunks));
   1.668 -    assert(mgr.num_free_chunks() == num_chunks,
   1.669 -           err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list "
   1.670 -                   "but there are only "SIZE_FORMAT, num_chunks, mgr.num_free_chunks()));
   1.671 -
   1.672 -    size_t const test_percentage = 50;
   1.673 -    mgr.purge_chunks(test_percentage);
   1.674 -    assert(mgr.num_chunks_handed_out() == num_chunks,
   1.675 -           err_msg("Purging must not hand out chunks but there are "SIZE_FORMAT,
   1.676 -                   mgr.num_chunks_handed_out()));
   1.677 -    assert(mgr.num_free_chunks() == (size_t)(mgr.num_chunks_handed_out() * test_percentage / 100),
   1.678 -           err_msg("Must have purged "SIZE_FORMAT" percent of "SIZE_FORMAT" chunks"
   1.679 -                   "but there are "SIZE_FORMAT, test_percentage, num_chunks,
   1.680 -                   mgr.num_free_chunks()));
   1.681 -    // Purge the remainder of the chunks on the free list.
   1.682 -    mgr.purge_chunks(0);
   1.683 -    assert(mgr.num_free_chunks() == 0, "Free List must be empty");
   1.684 -    assert(mgr.num_chunks_handed_out() == num_chunks,
   1.685 -           err_msg("Expected to be "SIZE_FORMAT" chunks handed out from the first set "
   1.686 -                   "but there are "SIZE_FORMAT, num_chunks, mgr.num_chunks_handed_out()));
   1.687 -
   1.688 -    // Exit of the scope of the set1 object will call the destructor that generates
   1.689 -    // num_chunks additional elements on the free list.
   1.690 -   }
   1.691 -
   1.692 -  assert(mgr.num_chunks_handed_out() == 0,
   1.693 -         err_msg("Deletion of the only set must have resulted in no chunks handed "
   1.694 -                 "out, but there is still "SIZE_FORMAT" handed out", mgr.num_chunks_handed_out()));
   1.695 -  assert(mgr.num_free_chunks() == num_chunks,
   1.696 -         err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list "
   1.697 -                 "but there are only "SIZE_FORMAT, num_chunks, mgr.num_free_chunks()));
   1.698 -
   1.699 -  // Restore initial state.
   1.700 -  mgr.purge_chunks(0);
   1.701 -  assert(mgr.num_free_chunks() == 0, "Free List must be empty");
   1.702 -  assert(mgr.num_chunks_handed_out() == 0, "No additional elements must have been handed out yet");
   1.703 -}
   1.704 -
   1.705 -void TestCodeCacheRemSet_test() {
   1.706 -  G1CodeRootSet::test();
   1.707 -}
   1.708 -#endif

mercurial