1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 
  26 #include "precompiled.hpp"
  27 #include "code/nmethod.hpp"
  28 #include "gc_implementation/g1/g1CodeCacheRemSet.hpp"
  29 #include "memory/iterator.hpp"
  30 
  31 G1CodeRootChunk::G1CodeRootChunk() : _top(NULL), _next(NULL), _prev(NULL) {
  32   _top = bottom();
  33 }
  34 
  35 void G1CodeRootChunk::reset() {
  36   _next = _prev = NULL;
  37   _top = bottom();
  38 }
  39 
  40 void G1CodeRootChunk::nmethods_do(CodeBlobClosure* cl) {
  41   nmethod** cur = bottom();
  42   while (cur != _top) {
  43     cl->do_code_blob(*cur);
  44     cur++;
  45   }
  46 }
  47 
  48 FreeList<G1CodeRootChunk> G1CodeRootSet::_free_list;
  49 size_t G1CodeRootSet::_num_chunks_handed_out = 0;
  50 
  51 G1CodeRootChunk* G1CodeRootSet::new_chunk() {
  52   G1CodeRootChunk* result = _free_list.get_chunk_at_head();
  53   if (result == NULL) {
  54     result = new G1CodeRootChunk();
  55   }
  56   G1CodeRootSet::_num_chunks_handed_out++;
  57   result->reset();
  58   return result;
  59 }
  60 
  61 void G1CodeRootSet::free_chunk(G1CodeRootChunk* chunk) {
  62   _free_list.return_chunk_at_head(chunk);
  63   G1CodeRootSet::_num_chunks_handed_out--;
  64 }
  65 
  66 void G1CodeRootSet::free_all_chunks(FreeList<G1CodeRootChunk>* list) {
  67   _free_list.prepend(list);
  68 }
  69 
  70 void G1CodeRootSet::purge_chunks(size_t keep_ratio) {
  71   size_t keep = G1CodeRootSet::_num_chunks_handed_out * keep_ratio / 100;
  72 
  73   if (keep >= (size_t)_free_list.count()) {
  74     return;
  75   }
  76 
  77   FreeList<G1CodeRootChunk> temp;
  78   temp.initialize();
  79   temp.set_size(G1CodeRootChunk::word_size());
  80 
  81   _free_list.getFirstNChunksFromList((size_t)_free_list.count() - keep, &temp);
  82 
  83   G1CodeRootChunk* cur = temp.get_chunk_at_head();
  84   while (cur != NULL) {
  85     delete cur;
  86     cur = temp.get_chunk_at_head();
  87   }
  88 }
  89 
  90 size_t G1CodeRootSet::static_mem_size() {
  91   return sizeof(_free_list) + sizeof(_num_chunks_handed_out);
  92 }
  93 
  94 size_t G1CodeRootSet::fl_mem_size() {
  95   return _free_list.count() * _free_list.size();
  96 }
  97 
  98 void G1CodeRootSet::initialize() {
  99   _free_list.initialize();
 100   _free_list.set_size(G1CodeRootChunk::word_size());
 101 }
 102 
 103 G1CodeRootSet::G1CodeRootSet() : _list(), _length(0) {
 104   _list.initialize();
 105   _list.set_size(G1CodeRootChunk::word_size());
 106 }
 107 
 108 G1CodeRootSet::~G1CodeRootSet() {
 109   clear();
 110 }
 111 
 112 void G1CodeRootSet::add(nmethod* method) {
 113   if (!contains(method)) {
 114     // Try to add the nmethod. If there is not enough space, get a new chunk.
 115     if (_list.head() == NULL || _list.head()->is_full()) {
 116       G1CodeRootChunk* cur = new_chunk();
 117       _list.return_chunk_at_head(cur);
 118     }
 119     bool result = _list.head()->add(method);
 120     guarantee(result, err_msg("Not able to add nmethod "PTR_FORMAT" to newly allocated chunk.", method));
 121     _length++;
 122   }
 123 }
 124 
 125 void G1CodeRootSet::remove(nmethod* method) {
 126   G1CodeRootChunk* found = find(method);
 127   if (found != NULL) {
 128     bool result = found->remove(method);
 129     guarantee(result, err_msg("could not find nmethod "PTR_FORMAT" during removal although we previously found it", method));
 130     // eventually free completely emptied chunk
 131     if (found->is_empty()) {
 132       _list.remove_chunk(found);
 133       free(found);
 134     }
 135     _length--;
 136   }
 137   assert(!contains(method), err_msg(PTR_FORMAT" still contains nmethod "PTR_FORMAT, this, method));
 138 }
 139 
 140 nmethod* G1CodeRootSet::pop() {
 141   do {
 142     G1CodeRootChunk* cur = _list.head();
 143     if (cur == NULL) {
 144       assert(_length == 0, "when there are no chunks, there should be no elements");
 145       return NULL;
 146     }
 147     nmethod* result = cur->pop();
 148     if (result != NULL) {
 149       _length--;
 150       return result;
 151     } else {
 152       free(_list.get_chunk_at_head());
 153     }
 154   } while (true);
 155 }
 156 
 157 G1CodeRootChunk* G1CodeRootSet::find(nmethod* method) {
 158   G1CodeRootChunk* cur = _list.head();
 159   while (cur != NULL) {
 160     if (cur->find(method)) {
 161       return cur;
 162     }
 163     cur = (G1CodeRootChunk*)cur->next();
 164   }
 165   return NULL;
 166 }
 167 
 168 void G1CodeRootSet::free(G1CodeRootChunk* chunk) {
 169   free_chunk(chunk);
 170 }
 171 
 172 bool G1CodeRootSet::contains(nmethod* method) {
 173   return find(method) != NULL;
 174 }
 175 
 176 void G1CodeRootSet::clear() {
 177   free_all_chunks(&_list);
 178   _length = 0;
 179 }
 180 
 181 void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
 182   G1CodeRootChunk* cur = _list.head();
 183   while (cur != NULL) {
 184     cur->nmethods_do(blk);
 185     cur = (G1CodeRootChunk*)cur->next();
 186   }
 187 }
 188 
 189 size_t G1CodeRootSet::mem_size() {
 190   return sizeof(this) + _list.count() * _list.size();
 191 }