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

Mon, 23 Jun 2014 16:43:41 +0200

author
pliden
date
Mon, 23 Jun 2014 16:43:41 +0200
changeset 6905
fd81a5764900
parent 6680
78bbf4d43a14
child 6876
710a3c8b516e
child 6925
82693fb204a5
permissions
-rw-r--r--

8046231: G1: Code root location ... from nmethod ... not in strong code roots for region
Reviewed-by: tschatzl, ehelin

     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  */
    26 #include "precompiled.hpp"
    27 #include "code/nmethod.hpp"
    28 #include "gc_implementation/g1/g1CodeCacheRemSet.hpp"
    29 #include "memory/iterator.hpp"
    31 PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
    33 G1CodeRootChunk::G1CodeRootChunk() : _top(NULL), _next(NULL), _prev(NULL) {
    34   _top = bottom();
    35 }
    37 void G1CodeRootChunk::reset() {
    38   _next = _prev = NULL;
    39   _top = bottom();
    40 }
    42 void G1CodeRootChunk::nmethods_do(CodeBlobClosure* cl) {
    43   nmethod** cur = bottom();
    44   while (cur != _top) {
    45     cl->do_code_blob(*cur);
    46     cur++;
    47   }
    48 }
    50 FreeList<G1CodeRootChunk> G1CodeRootSet::_free_list;
    51 size_t G1CodeRootSet::_num_chunks_handed_out = 0;
    53 G1CodeRootChunk* G1CodeRootSet::new_chunk() {
    54   G1CodeRootChunk* result = _free_list.get_chunk_at_head();
    55   if (result == NULL) {
    56     result = new G1CodeRootChunk();
    57   }
    58   G1CodeRootSet::_num_chunks_handed_out++;
    59   result->reset();
    60   return result;
    61 }
    63 void G1CodeRootSet::free_chunk(G1CodeRootChunk* chunk) {
    64   _free_list.return_chunk_at_head(chunk);
    65   G1CodeRootSet::_num_chunks_handed_out--;
    66 }
    68 void G1CodeRootSet::free_all_chunks(FreeList<G1CodeRootChunk>* list) {
    69   G1CodeRootSet::_num_chunks_handed_out -= list->count();
    70   _free_list.prepend(list);
    71 }
    73 void G1CodeRootSet::purge_chunks(size_t keep_ratio) {
    74   size_t keep = G1CodeRootSet::_num_chunks_handed_out * keep_ratio / 100;
    76   if (keep >= (size_t)_free_list.count()) {
    77     return;
    78   }
    80   FreeList<G1CodeRootChunk> temp;
    81   temp.initialize();
    82   temp.set_size(G1CodeRootChunk::word_size());
    84   _free_list.getFirstNChunksFromList((size_t)_free_list.count() - keep, &temp);
    86   G1CodeRootChunk* cur = temp.get_chunk_at_head();
    87   while (cur != NULL) {
    88     delete cur;
    89     cur = temp.get_chunk_at_head();
    90   }
    91 }
    93 size_t G1CodeRootSet::static_mem_size() {
    94   return sizeof(_free_list) + sizeof(_num_chunks_handed_out);
    95 }
    97 size_t G1CodeRootSet::fl_mem_size() {
    98   return _free_list.count() * _free_list.size();
    99 }
   101 void G1CodeRootSet::initialize() {
   102   _free_list.initialize();
   103   _free_list.set_size(G1CodeRootChunk::word_size());
   104 }
   106 G1CodeRootSet::G1CodeRootSet() : _list(), _length(0) {
   107   _list.initialize();
   108   _list.set_size(G1CodeRootChunk::word_size());
   109 }
   111 G1CodeRootSet::~G1CodeRootSet() {
   112   clear();
   113 }
   115 void G1CodeRootSet::add(nmethod* method) {
   116   if (!contains(method)) {
   117     // Try to add the nmethod. If there is not enough space, get a new chunk.
   118     if (_list.head() == NULL || _list.head()->is_full()) {
   119       G1CodeRootChunk* cur = new_chunk();
   120       _list.return_chunk_at_head(cur);
   121     }
   122     bool result = _list.head()->add(method);
   123     guarantee(result, err_msg("Not able to add nmethod "PTR_FORMAT" to newly allocated chunk.", method));
   124     _length++;
   125   }
   126 }
   128 void G1CodeRootSet::remove(nmethod* method) {
   129   G1CodeRootChunk* found = find(method);
   130   if (found != NULL) {
   131     bool result = found->remove(method);
   132     guarantee(result, err_msg("could not find nmethod "PTR_FORMAT" during removal although we previously found it", method));
   133     // eventually free completely emptied chunk
   134     if (found->is_empty()) {
   135       _list.remove_chunk(found);
   136       free(found);
   137     }
   138     _length--;
   139   }
   140   assert(!contains(method), err_msg(PTR_FORMAT" still contains nmethod "PTR_FORMAT, this, method));
   141 }
   143 nmethod* G1CodeRootSet::pop() {
   144   do {
   145     G1CodeRootChunk* cur = _list.head();
   146     if (cur == NULL) {
   147       assert(_length == 0, "when there are no chunks, there should be no elements");
   148       return NULL;
   149     }
   150     nmethod* result = cur->pop();
   151     if (result != NULL) {
   152       _length--;
   153       return result;
   154     } else {
   155       free(_list.get_chunk_at_head());
   156     }
   157   } while (true);
   158 }
   160 G1CodeRootChunk* G1CodeRootSet::find(nmethod* method) {
   161   G1CodeRootChunk* cur = _list.head();
   162   while (cur != NULL) {
   163     if (cur->contains(method)) {
   164       return cur;
   165     }
   166     cur = (G1CodeRootChunk*)cur->next();
   167   }
   168   return NULL;
   169 }
   171 void G1CodeRootSet::free(G1CodeRootChunk* chunk) {
   172   free_chunk(chunk);
   173 }
   175 bool G1CodeRootSet::contains(nmethod* method) {
   176   return find(method) != NULL;
   177 }
   179 void G1CodeRootSet::clear() {
   180   free_all_chunks(&_list);
   181   _length = 0;
   182 }
   184 void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
   185   G1CodeRootChunk* cur = _list.head();
   186   while (cur != NULL) {
   187     cur->nmethods_do(blk);
   188     cur = (G1CodeRootChunk*)cur->next();
   189   }
   190 }
   192 size_t G1CodeRootSet::mem_size() {
   193   return sizeof(this) + _list.count() * _list.size();
   194 }
   196 #ifndef PRODUCT
   198 void G1CodeRootSet::test() {
   199   initialize();
   201   assert(_free_list.count() == 0, "Free List must be empty");
   202   assert(_num_chunks_handed_out == 0, "No elements must have been handed out yet");
   204   // The number of chunks that we allocate for purge testing.
   205   size_t const num_chunks = 10;
   206   {
   207     G1CodeRootSet set1;
   208     assert(set1.is_empty(), "Code root set must be initially empty but is not.");
   210     set1.add((nmethod*)1);
   211     assert(_num_chunks_handed_out == 1,
   212            err_msg("Must have allocated and handed out one chunk, but handed out "
   213                    SIZE_FORMAT" chunks", _num_chunks_handed_out));
   214     assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "
   215                                        SIZE_FORMAT" elements", set1.length()));
   217     // G1CodeRootChunk::word_size() is larger than G1CodeRootChunk::num_entries which
   218     // we cannot access.
   219     for (uint i = 0; i < G1CodeRootChunk::word_size() + 1; i++) {
   220       set1.add((nmethod*)1);
   221     }
   222     assert(_num_chunks_handed_out == 1,
   223            err_msg("Duplicate detection must have prevented allocation of further "
   224                    "chunks but contains "SIZE_FORMAT, _num_chunks_handed_out));
   225     assert(set1.length() == 1,
   226            err_msg("Duplicate detection should not have increased the set size but "
   227                    "is "SIZE_FORMAT, set1.length()));
   229     size_t num_total_after_add = G1CodeRootChunk::word_size() + 1;
   230     for (size_t i = 0; i < num_total_after_add - 1; i++) {
   231       set1.add((nmethod*)(2 + i));
   232     }
   233     assert(_num_chunks_handed_out > 1,
   234            "After adding more code roots, more than one chunks should have been handed out");
   235     assert(set1.length() == num_total_after_add,
   236            err_msg("After adding in total "SIZE_FORMAT" distinct code roots, they "
   237                    "need to be in the set, but there are only "SIZE_FORMAT,
   238                    num_total_after_add, set1.length()));
   240     size_t num_popped = 0;
   241     while (set1.pop() != NULL) {
   242       num_popped++;
   243     }
   244     assert(num_popped == num_total_after_add,
   245            err_msg("Managed to pop "SIZE_FORMAT" code roots, but only "SIZE_FORMAT" "
   246                    "were added", num_popped, num_total_after_add));
   247     assert(_num_chunks_handed_out == 0,
   248            err_msg("After popping all elements, all chunks must have been returned "
   249                    "but are still "SIZE_FORMAT, _num_chunks_handed_out));
   251     purge_chunks(0);
   252     assert(_free_list.count() == 0,
   253            err_msg("After purging everything, the free list must be empty but still "
   254                    "contains "SIZE_FORMAT" chunks", _free_list.count()));
   256     // Add some more handed out chunks.
   257     size_t i = 0;
   258     while (_num_chunks_handed_out < num_chunks) {
   259       set1.add((nmethod*)i);
   260       i++;
   261     }
   263     {
   264       // Generate chunks on the free list.
   265       G1CodeRootSet set2;
   266       size_t i = 0;
   267       while (_num_chunks_handed_out < num_chunks * 2) {
   268         set2.add((nmethod*)i);
   269         i++;
   270       }
   271       // Exit of the scope of the set2 object will call the destructor that generates
   272       // num_chunks elements on the free list.
   273     }
   275     assert(_num_chunks_handed_out == num_chunks,
   276            err_msg("Deletion of the second set must have resulted in giving back "
   277                    "those, but there is still "SIZE_FORMAT" handed out, expecting "
   278                    SIZE_FORMAT, _num_chunks_handed_out, num_chunks));
   279     assert((size_t)_free_list.count() == num_chunks,
   280            err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list "
   281                    "but there are only "SIZE_FORMAT, num_chunks, _free_list.count()));
   283     size_t const test_percentage = 50;
   284     purge_chunks(test_percentage);
   285     assert(_num_chunks_handed_out == num_chunks,
   286            err_msg("Purging must not hand out chunks but there are "SIZE_FORMAT,
   287                    _num_chunks_handed_out));
   288     assert((size_t)_free_list.count() == (ssize_t)(num_chunks * test_percentage / 100),
   289            err_msg("Must have purged "SIZE_FORMAT" percent of "SIZE_FORMAT" chunks"
   290                    "but there are "SSIZE_FORMAT, test_percentage, num_chunks,
   291                    _free_list.count()));
   292     // Purge the remainder of the chunks on the free list.
   293     purge_chunks(0);
   294     assert(_free_list.count() == 0, "Free List must be empty");
   295     assert(_num_chunks_handed_out == num_chunks,
   296            err_msg("Expected to be "SIZE_FORMAT" chunks handed out from the first set "
   297                    "but there are "SIZE_FORMAT, num_chunks, _num_chunks_handed_out));
   299     // Exit of the scope of the set1 object will call the destructor that generates
   300     // num_chunks additional elements on the free list.
   301   }
   303   assert(_num_chunks_handed_out == 0,
   304          err_msg("Deletion of the only set must have resulted in no chunks handed "
   305                  "out, but there is still "SIZE_FORMAT" handed out", _num_chunks_handed_out));
   306   assert((size_t)_free_list.count() == num_chunks,
   307          err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list "
   308                  "but there are only "SSIZE_FORMAT, num_chunks, _free_list.count()));
   310   // Restore initial state.
   311   purge_chunks(0);
   312   assert(_free_list.count() == 0, "Free List must be empty");
   313   assert(_num_chunks_handed_out == 0, "No elements must have been handed out yet");
   314 }
   316 void TestCodeCacheRemSet_test() {
   317   G1CodeRootSet::test();
   318 }
   319 #endif

mercurial