Fri, 11 Apr 2014 11:00:12 +0200
8037112: gc/g1/TestHumongousAllocInitialMark.java caused SIGSEGV
Reviewed-by: brutisso, mgerdin
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