g1CodeCacheRemSet.cpp 11.1 KB
Newer Older
1
/*
2
 * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * 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"
26
#include "code/codeCache.hpp"
27 28
#include "code/nmethod.hpp"
#include "gc_implementation/g1/g1CodeCacheRemSet.hpp"
29 30
#include "gc_implementation/g1/heapRegion.hpp"
#include "memory/heap.hpp"
31
#include "memory/iterator.hpp"
32 33 34
#include "oops/oop.inline.hpp"
#include "utilities/hashtable.inline.hpp"
#include "utilities/stack.inline.hpp"
35

36 37
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC

38 39 40
class CodeRootSetTable : public Hashtable<nmethod*, mtGC> {
  friend class G1CodeRootSetTest;
  typedef HashtableEntry<nmethod*, mtGC> Entry;
41

42
  static CodeRootSetTable* volatile _purge_list;
43

44
  CodeRootSetTable* _purge_next;
45

46 47 48 49
  unsigned int compute_hash(nmethod* nm) {
    uintptr_t hash = (uintptr_t)nm;
    return hash ^ (hash >> 7); // code heap blocks are 128byte aligned
  }
50

51
  void remove_entry(Entry* e, Entry* previous);
52
  Entry* new_entry(nmethod* nm);
53

54 55 56
 public:
  CodeRootSetTable(int size) : Hashtable<nmethod*, mtGC>(size, sizeof(Entry)), _purge_next(NULL) {}
  ~CodeRootSetTable();
57

58 59 60
  // Needs to be protected locks
  bool add(nmethod* nm);
  bool remove(nmethod* nm);
61

62 63
  // Can be called without locking
  bool contains(nmethod* nm);
64

65
  int entry_size() const { return BasicHashtable<mtGC>::entry_size(); }
66

67 68
  void copy_to(CodeRootSetTable* new_table);
  void nmethods_do(CodeBlobClosure* blk);
69

70
  template<typename CB>
71
  int remove_if(CB& should_remove);
72

73 74
  static void purge_list_append(CodeRootSetTable* tbl);
  static void purge();
75

76 77
  static size_t static_mem_size() {
    return sizeof(_purge_list);
78
  }
79 80

  size_t mem_size();
81
};
82

83
CodeRootSetTable* volatile CodeRootSetTable::_purge_list = NULL;
84

85 86 87 88
size_t CodeRootSetTable::mem_size() {
  return sizeof(CodeRootSetTable) + (entry_size() * number_of_entries()) + (sizeof(HashtableBucket<mtGC>) * table_size());
}

89 90 91 92 93
CodeRootSetTable::Entry* CodeRootSetTable::new_entry(nmethod* nm) {
  unsigned int hash = compute_hash(nm);
  Entry* entry = (Entry*) new_entry_free_list();
  if (entry == NULL) {
    entry = (Entry*) NEW_C_HEAP_ARRAY2(char, entry_size(), mtGC, CURRENT_PC);
94
  }
95 96 97 98
  entry->set_next(NULL);
  entry->set_hash(hash);
  entry->set_literal(nm);
  return entry;
99 100
}

101 102 103 104 105 106 107 108 109 110 111 112
void CodeRootSetTable::remove_entry(Entry* e, Entry* previous) {
  int index = hash_to_index(e->hash());
  assert((e == bucket(index)) == (previous == NULL), "if e is the first entry then previous should be null");

  if (previous == NULL) {
    set_entry(index, e->next());
  } else {
    previous->set_next(e->next());
  }
  free_entry(e);
}

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
CodeRootSetTable::~CodeRootSetTable() {
  for (int index = 0; index < table_size(); ++index) {
    for (Entry* e = bucket(index); e != NULL; ) {
      Entry* to_remove = e;
      // read next before freeing.
      e = e->next();
      unlink_entry(to_remove);
      FREE_C_HEAP_ARRAY(char, to_remove, mtGC);
    }
  }
  assert(number_of_entries() == 0, "should have removed all entries");
  free_buckets();
  for (BasicHashtableEntry<mtGC>* e = new_entry_free_list(); e != NULL; e = new_entry_free_list()) {
    FREE_C_HEAP_ARRAY(char, e, mtGC);
  }
128 129
}

130 131 132 133 134 135
bool CodeRootSetTable::add(nmethod* nm) {
  if (!contains(nm)) {
    Entry* e = new_entry(nm);
    int index = hash_to_index(e->hash());
    add_entry(index, e);
    return true;
136
  }
137
  return false;
138 139
}

140 141 142 143 144 145 146 147 148
bool CodeRootSetTable::contains(nmethod* nm) {
  int index = hash_to_index(compute_hash(nm));
  for (Entry* e = bucket(index); e != NULL; e = e->next()) {
    if (e->literal() == nm) {
      return true;
    }
  }
  return false;
}
149

150 151 152 153 154
bool CodeRootSetTable::remove(nmethod* nm) {
  int index = hash_to_index(compute_hash(nm));
  Entry* previous = NULL;
  for (Entry* e = bucket(index); e != NULL; previous = e, e = e->next()) {
    if (e->literal() == nm) {
155
      remove_entry(e, previous);
156 157 158 159
      return true;
    }
  }
  return false;
160 161
}

162 163 164 165 166 167 168
void CodeRootSetTable::copy_to(CodeRootSetTable* new_table) {
  for (int index = 0; index < table_size(); ++index) {
    for (Entry* e = bucket(index); e != NULL; e = e->next()) {
      new_table->add(e->literal());
    }
  }
  new_table->copy_freelist(this);
169 170
}

171 172 173 174 175 176 177
void CodeRootSetTable::nmethods_do(CodeBlobClosure* blk) {
  for (int index = 0; index < table_size(); ++index) {
    for (Entry* e = bucket(index); e != NULL; e = e->next()) {
      blk->do_code_blob(e->literal());
    }
  }
}
178

179
template<typename CB>
180 181
int CodeRootSetTable::remove_if(CB& should_remove) {
  int num_removed = 0;
182 183 184 185 186 187
  for (int index = 0; index < table_size(); ++index) {
    Entry* previous = NULL;
    Entry* e = bucket(index);
    while (e != NULL) {
      Entry* next = e->next();
      if (should_remove(e->literal())) {
188 189
        remove_entry(e, previous);
        ++num_removed;
190 191 192 193 194 195
      } else {
        previous = e;
      }
      e = next;
    }
  }
196
  return num_removed;
197
}
198

199 200
G1CodeRootSet::~G1CodeRootSet() {
  delete _table;
201 202
}

203 204
CodeRootSetTable* G1CodeRootSet::load_acquire_table() {
  return (CodeRootSetTable*) OrderAccess::load_ptr_acquire(&_table);
205 206
}

207 208
void G1CodeRootSet::allocate_small_table() {
  _table = new CodeRootSetTable(SmallSize);
209 210
}

211 212 213 214 215 216 217
void CodeRootSetTable::purge_list_append(CodeRootSetTable* table) {
  for (;;) {
    table->_purge_next = _purge_list;
    CodeRootSetTable* old = (CodeRootSetTable*) Atomic::cmpxchg_ptr(table, &_purge_list, table->_purge_next);
    if (old == table->_purge_next) {
      break;
    }
218
  }
219 220
}

221 222 223 224 225 226 227 228
void CodeRootSetTable::purge() {
  CodeRootSetTable* table = _purge_list;
  _purge_list = NULL;
  while (table != NULL) {
    CodeRootSetTable* to_purge = table;
    table = table->_purge_next;
    delete to_purge;
  }
229 230
}

231 232
void G1CodeRootSet::move_to_large() {
  CodeRootSetTable* temp = new CodeRootSetTable(LargeSize);
233

234
  _table->copy_to(temp);
235

236
  CodeRootSetTable::purge_list_append(_table);
237

238 239
  OrderAccess::release_store_ptr(&_table, temp);
}
240

241 242
void G1CodeRootSet::purge() {
  CodeRootSetTable::purge();
243 244
}

245 246
size_t G1CodeRootSet::static_mem_size() {
  return CodeRootSetTable::static_mem_size();
247 248
}

249 250 251 252 253 254 255
void G1CodeRootSet::add(nmethod* method) {
  bool added = false;
  if (is_empty()) {
    allocate_small_table();
  }
  added = _table->add(method);
  if (added) {
256 257 258
    if (_length == Threshold) {
      move_to_large();
    }
259
    ++_length;
260
  }
261
  assert(_length == (size_t)_table->number_of_entries(), "sizes should match");
262 263
}

264 265 266 267 268 269 270 271 272
bool G1CodeRootSet::remove(nmethod* method) {
  bool removed = false;
  if (_table != NULL) {
    removed = _table->remove(method);
  }
  if (removed) {
    _length--;
    if (_length == 0) {
      clear();
273 274
    }
  }
275 276
  assert((_length == 0 && _table == NULL) ||
         (_length == (size_t)_table->number_of_entries()), "sizes should match");
277
  return removed;
278 279 280
}

bool G1CodeRootSet::contains(nmethod* method) {
281
  CodeRootSetTable* table = load_acquire_table(); // contains() may be called outside of lock, so ensure mem sync.
282 283 284 285
  if (table != NULL) {
    return table->contains(method);
  }
  return false;
286 287 288
}

void G1CodeRootSet::clear() {
289 290
  delete _table;
  _table = NULL;
291 292 293
  _length = 0;
}

294
size_t G1CodeRootSet::mem_size() {
295
  return sizeof(*this) + (_table != NULL ? _table->mem_size() : 0);
296 297
}

298
void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
299 300
  if (_table != NULL) {
    _table->nmethods_do(blk);
301 302 303
  }
}

304 305 306 307 308 309
class CleanCallback : public StackObj {
  class PointsIntoHRDetectionClosure : public OopClosure {
    HeapRegion* _hr;
   public:
    bool _points_into;
    PointsIntoHRDetectionClosure(HeapRegion* hr) : _hr(hr), _points_into(false) {}
310

311 312 313
    void do_oop(narrowOop* o) {
      do_oop_work(o);
    }
314

315 316 317
    void do_oop(oop* o) {
      do_oop_work(o);
    }
318

319 320 321 322 323 324 325
    template <typename T>
    void do_oop_work(T* p) {
      if (_hr->is_in(oopDesc::load_decode_heap_oop(p))) {
        _points_into = true;
      }
    }
  };
326

327 328
  PointsIntoHRDetectionClosure _detector;
  CodeBlobToOopClosure _blobs;
329

330 331
 public:
  CleanCallback(HeapRegion* hr) : _detector(hr), _blobs(&_detector, !CodeBlobToOopClosure::FixRelocations) {}
332

333 334 335
  bool operator() (nmethod* nm) {
    _detector._points_into = false;
    _blobs.do_code_blob(nm);
336
    return !_detector._points_into;
337 338
  }
};
339

340 341 342
void G1CodeRootSet::clean(HeapRegion* owner) {
  CleanCallback should_clean(owner);
  if (_table != NULL) {
343 344 345 346 347 348
    int removed = _table->remove_if(should_clean);
    assert((size_t)removed <= _length, "impossible");
    _length -= removed;
  }
  if (_length == 0) {
    clear();
349 350
  }
}
351

352 353 354 355 356 357 358 359
#ifndef PRODUCT

class G1CodeRootSetTest {
 public:
  static void test() {
    {
      G1CodeRootSet set1;
      assert(set1.is_empty(), "Code root set must be initially empty but is not.");
360

361
      assert(G1CodeRootSet::static_mem_size() == sizeof(void*),
362
          err_msg("The code root set's static memory usage is incorrect, " SIZE_FORMAT " bytes", G1CodeRootSet::static_mem_size()));
363 364

      set1.add((nmethod*)1);
365
      assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "
366
          SIZE_FORMAT " elements", set1.length()));
367

368 369 370 371 372 373 374
      const size_t num_to_add = (size_t)G1CodeRootSet::Threshold + 1;

      for (size_t i = 1; i <= num_to_add; i++) {
        set1.add((nmethod*)1);
      }
      assert(set1.length() == 1,
          err_msg("Duplicate detection should not have increased the set size but "
375
              "is " SIZE_FORMAT, set1.length()));
376 377 378 379 380

      for (size_t i = 2; i <= num_to_add; i++) {
        set1.add((nmethod*)(uintptr_t)(i));
      }
      assert(set1.length() == num_to_add,
381 382
          err_msg("After adding in total " SIZE_FORMAT " distinct code roots, they "
              "need to be in the set, but there are only " SIZE_FORMAT,
383 384 385 386 387 388 389 390 391 392 393 394
              num_to_add, set1.length()));

      assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");

      size_t num_popped = 0;
      for (size_t i = 1; i <= num_to_add; i++) {
        bool removed = set1.remove((nmethod*)i);
        if (removed) {
          num_popped += 1;
        } else {
          break;
        }
395
      }
396
      assert(num_popped == num_to_add,
397
          err_msg("Managed to pop " SIZE_FORMAT " code roots, but only " SIZE_FORMAT " "
398 399 400 401 402 403 404
              "were added", num_popped, num_to_add));
      assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");

      G1CodeRootSet::purge();

      assert(CodeRootSetTable::_purge_list == NULL, "should have purged old small tables");

405 406
    }

407 408
  }
};
409 410

void TestCodeCacheRemSet_test() {
411
  G1CodeRootSetTest::test();
412
}
413

414
#endif