g1CodeCacheRemSet.cpp 11.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
 * Copyright (c) 2014, 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/nmethod.hpp"
#include "gc_implementation/g1/g1CodeCacheRemSet.hpp"
#include "memory/iterator.hpp"

31 32
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
G1CodeRootChunk::G1CodeRootChunk() : _top(NULL), _next(NULL), _prev(NULL) {
  _top = bottom();
}

void G1CodeRootChunk::reset() {
  _next = _prev = NULL;
  _top = bottom();
}

void G1CodeRootChunk::nmethods_do(CodeBlobClosure* cl) {
  nmethod** cur = bottom();
  while (cur != _top) {
    cl->do_code_blob(*cur);
    cur++;
  }
}

50 51 52
G1CodeRootChunkManager::G1CodeRootChunkManager() : _free_list(), _num_chunks_handed_out(0) {
  _free_list.initialize();
  _free_list.set_size(G1CodeRootChunk::word_size());
53 54
}

55 56
size_t G1CodeRootChunkManager::fl_mem_size() {
  return _free_list.count() * _free_list.size();
57 58
}

59 60
void G1CodeRootChunkManager::free_all_chunks(FreeList<G1CodeRootChunk>* list) {
  _num_chunks_handed_out -= list->count();
61 62 63
  _free_list.prepend(list);
}

64 65 66 67
void G1CodeRootChunkManager::free_chunk(G1CodeRootChunk* chunk) {
  _free_list.return_chunk_at_head(chunk);
  _num_chunks_handed_out--;
}
68

69 70
void G1CodeRootChunkManager::purge_chunks(size_t keep_ratio) {
  size_t keep = _num_chunks_handed_out * keep_ratio / 100;
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
  if (keep >= (size_t)_free_list.count()) {
    return;
  }

  FreeList<G1CodeRootChunk> temp;
  temp.initialize();
  temp.set_size(G1CodeRootChunk::word_size());

  _free_list.getFirstNChunksFromList((size_t)_free_list.count() - keep, &temp);

  G1CodeRootChunk* cur = temp.get_chunk_at_head();
  while (cur != NULL) {
    delete cur;
    cur = temp.get_chunk_at_head();
  }
}

88
size_t G1CodeRootChunkManager::static_mem_size() {
89
  return sizeof(G1CodeRootChunkManager);
90 91
}

92 93 94 95 96 97 98 99 100

G1CodeRootChunk* G1CodeRootChunkManager::new_chunk() {
  G1CodeRootChunk* result = _free_list.get_chunk_at_head();
  if (result == NULL) {
    result = new G1CodeRootChunk();
  }
  _num_chunks_handed_out++;
  result->reset();
  return result;
101 102
}

103 104 105 106
#ifndef PRODUCT

size_t G1CodeRootChunkManager::num_chunks_handed_out() const {
  return _num_chunks_handed_out;
107 108
}

109 110 111 112 113 114 115 116 117 118 119 120
size_t G1CodeRootChunkManager::num_free_chunks() const {
  return (size_t)_free_list.count();
}

#endif

G1CodeRootChunkManager G1CodeRootSet::_default_chunk_manager;

void G1CodeRootSet::purge_chunks(size_t keep_ratio) {
  _default_chunk_manager.purge_chunks(keep_ratio);
}

121
size_t G1CodeRootSet::free_chunks_static_mem_size() {
122 123 124 125 126 127 128 129 130 131 132
  return _default_chunk_manager.static_mem_size();
}

size_t G1CodeRootSet::free_chunks_mem_size() {
  return _default_chunk_manager.fl_mem_size();
}

G1CodeRootSet::G1CodeRootSet(G1CodeRootChunkManager* manager) : _manager(manager), _list(), _length(0) {
  if (_manager == NULL) {
    _manager = &_default_chunk_manager;
  }
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
  _list.initialize();
  _list.set_size(G1CodeRootChunk::word_size());
}

G1CodeRootSet::~G1CodeRootSet() {
  clear();
}

void G1CodeRootSet::add(nmethod* method) {
  if (!contains(method)) {
    // Try to add the nmethod. If there is not enough space, get a new chunk.
    if (_list.head() == NULL || _list.head()->is_full()) {
      G1CodeRootChunk* cur = new_chunk();
      _list.return_chunk_at_head(cur);
    }
    bool result = _list.head()->add(method);
    guarantee(result, err_msg("Not able to add nmethod "PTR_FORMAT" to newly allocated chunk.", method));
    _length++;
  }
}

void G1CodeRootSet::remove(nmethod* method) {
  G1CodeRootChunk* found = find(method);
  if (found != NULL) {
    bool result = found->remove(method);
    guarantee(result, err_msg("could not find nmethod "PTR_FORMAT" during removal although we previously found it", method));
    // eventually free completely emptied chunk
    if (found->is_empty()) {
      _list.remove_chunk(found);
      free(found);
    }
    _length--;
  }
  assert(!contains(method), err_msg(PTR_FORMAT" still contains nmethod "PTR_FORMAT, this, method));
}

nmethod* G1CodeRootSet::pop() {
  do {
    G1CodeRootChunk* cur = _list.head();
    if (cur == NULL) {
      assert(_length == 0, "when there are no chunks, there should be no elements");
      return NULL;
    }
    nmethod* result = cur->pop();
    if (result != NULL) {
      _length--;
      return result;
    } else {
      free(_list.get_chunk_at_head());
    }
  } while (true);
}

G1CodeRootChunk* G1CodeRootSet::find(nmethod* method) {
  G1CodeRootChunk* cur = _list.head();
  while (cur != NULL) {
    if (cur->contains(method)) {
      return cur;
    }
    cur = (G1CodeRootChunk*)cur->next();
  }
  return NULL;
}

void G1CodeRootSet::free(G1CodeRootChunk* chunk) {
  free_chunk(chunk);
}

bool G1CodeRootSet::contains(nmethod* method) {
  return find(method) != NULL;
}

void G1CodeRootSet::clear() {
  free_all_chunks(&_list);
  _length = 0;
}

void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
  G1CodeRootChunk* cur = _list.head();
  while (cur != NULL) {
    cur->nmethods_do(blk);
    cur = (G1CodeRootChunk*)cur->next();
  }
}

218 219 220 221
size_t G1CodeRootSet::static_mem_size() {
  return sizeof(G1CodeRootSet);
}

222
size_t G1CodeRootSet::mem_size() {
223
  return G1CodeRootSet::static_mem_size() + _list.count() * _list.size();
224 225 226 227 228
}

#ifndef PRODUCT

void G1CodeRootSet::test() {
229
  G1CodeRootChunkManager mgr;
230

231
  assert(mgr.num_chunks_handed_out() == 0, "Must not have handed out chunks yet");
232

233 234 235
  assert(G1CodeRootChunkManager::static_mem_size() > sizeof(void*),
         err_msg("The chunk manager's static memory usage seems too small, is only "SIZE_FORMAT" bytes.", G1CodeRootChunkManager::static_mem_size()));

236 237
  // The number of chunks that we allocate for purge testing.
  size_t const num_chunks = 10;
238

239
  {
240
    G1CodeRootSet set1(&mgr);
241 242
    assert(set1.is_empty(), "Code root set must be initially empty but is not.");

243 244 245
    assert(G1CodeRootSet::static_mem_size() > sizeof(void*),
           err_msg("The code root set's static memory usage seems too small, is only "SIZE_FORMAT" bytes", G1CodeRootSet::static_mem_size()));

246
    set1.add((nmethod*)1);
247
    assert(mgr.num_chunks_handed_out() == 1,
248
           err_msg("Must have allocated and handed out one chunk, but handed out "
249
                   SIZE_FORMAT" chunks", mgr.num_chunks_handed_out()));
250 251 252 253 254 255 256 257
    assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "
                                       SIZE_FORMAT" elements", set1.length()));

    // G1CodeRootChunk::word_size() is larger than G1CodeRootChunk::num_entries which
    // we cannot access.
    for (uint i = 0; i < G1CodeRootChunk::word_size() + 1; i++) {
      set1.add((nmethod*)1);
    }
258
    assert(mgr.num_chunks_handed_out() == 1,
259
           err_msg("Duplicate detection must have prevented allocation of further "
260
                   "chunks but allocated "SIZE_FORMAT, mgr.num_chunks_handed_out()));
261 262 263 264 265 266
    assert(set1.length() == 1,
           err_msg("Duplicate detection should not have increased the set size but "
                   "is "SIZE_FORMAT, set1.length()));

    size_t num_total_after_add = G1CodeRootChunk::word_size() + 1;
    for (size_t i = 0; i < num_total_after_add - 1; i++) {
267
      set1.add((nmethod*)(uintptr_t)(2 + i));
268
    }
269 270
    assert(mgr.num_chunks_handed_out() > 1,
           "After adding more code roots, more than one additional chunk should have been handed out");
271 272 273 274 275 276 277 278 279 280 281 282
    assert(set1.length() == num_total_after_add,
           err_msg("After adding in total "SIZE_FORMAT" distinct code roots, they "
                   "need to be in the set, but there are only "SIZE_FORMAT,
                   num_total_after_add, set1.length()));

    size_t num_popped = 0;
    while (set1.pop() != NULL) {
      num_popped++;
    }
    assert(num_popped == num_total_after_add,
           err_msg("Managed to pop "SIZE_FORMAT" code roots, but only "SIZE_FORMAT" "
                   "were added", num_popped, num_total_after_add));
283
    assert(mgr.num_chunks_handed_out() == 0,
284
           err_msg("After popping all elements, all chunks must have been returned "
285
                   "but there are still "SIZE_FORMAT" additional", mgr.num_chunks_handed_out()));
286

287 288
    mgr.purge_chunks(0);
    assert(mgr.num_free_chunks() == 0,
289
           err_msg("After purging everything, the free list must be empty but still "
290
                   "contains "SIZE_FORMAT" chunks", mgr.num_free_chunks()));
291 292 293

    // Add some more handed out chunks.
    size_t i = 0;
294
    while (mgr.num_chunks_handed_out() < num_chunks) {
295 296 297 298 299 300
      set1.add((nmethod*)i);
      i++;
    }

    {
      // Generate chunks on the free list.
301
      G1CodeRootSet set2(&mgr);
302
      size_t i = 0;
303
      while (mgr.num_chunks_handed_out() < (num_chunks * 2)) {
304 305 306 307 308 309 310
        set2.add((nmethod*)i);
        i++;
      }
      // Exit of the scope of the set2 object will call the destructor that generates
      // num_chunks elements on the free list.
    }

311
    assert(mgr.num_chunks_handed_out() == num_chunks,
312
           err_msg("Deletion of the second set must have resulted in giving back "
313 314 315
                   "those, but there are still "SIZE_FORMAT" additional handed out, expecting "
                   SIZE_FORMAT, mgr.num_chunks_handed_out(), num_chunks));
    assert(mgr.num_free_chunks() == num_chunks,
316
           err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list "
317
                   "but there are only "SIZE_FORMAT, num_chunks, mgr.num_free_chunks()));
318 319

    size_t const test_percentage = 50;
320 321
    mgr.purge_chunks(test_percentage);
    assert(mgr.num_chunks_handed_out() == num_chunks,
322
           err_msg("Purging must not hand out chunks but there are "SIZE_FORMAT,
323 324
                   mgr.num_chunks_handed_out()));
    assert(mgr.num_free_chunks() == (size_t)(mgr.num_chunks_handed_out() * test_percentage / 100),
325
           err_msg("Must have purged "SIZE_FORMAT" percent of "SIZE_FORMAT" chunks"
326 327
                   "but there are "SIZE_FORMAT, test_percentage, num_chunks,
                   mgr.num_free_chunks()));
328
    // Purge the remainder of the chunks on the free list.
329 330 331
    mgr.purge_chunks(0);
    assert(mgr.num_free_chunks() == 0, "Free List must be empty");
    assert(mgr.num_chunks_handed_out() == num_chunks,
332
           err_msg("Expected to be "SIZE_FORMAT" chunks handed out from the first set "
333
                   "but there are "SIZE_FORMAT, num_chunks, mgr.num_chunks_handed_out()));
334 335 336

    // Exit of the scope of the set1 object will call the destructor that generates
    // num_chunks additional elements on the free list.
337
   }
338

339
  assert(mgr.num_chunks_handed_out() == 0,
340
         err_msg("Deletion of the only set must have resulted in no chunks handed "
341 342
                 "out, but there is still "SIZE_FORMAT" handed out", mgr.num_chunks_handed_out()));
  assert(mgr.num_free_chunks() == num_chunks,
343
         err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list "
344
                 "but there are only "SIZE_FORMAT, num_chunks, mgr.num_free_chunks()));
345 346

  // Restore initial state.
347 348 349
  mgr.purge_chunks(0);
  assert(mgr.num_free_chunks() == 0, "Free List must be empty");
  assert(mgr.num_chunks_handed_out() == 0, "No additional elements must have been handed out yet");
350 351 352 353 354 355
}

void TestCodeCacheRemSet_test() {
  G1CodeRootSet::test();
}
#endif