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

changeset 7208
7baf47cb97cb
parent 6993
870c03421152
child 7209
58925d1f325e
equal deleted inserted replaced
7207:152cf4afc11f 7208:7baf47cb97cb
20 * or visit www.oracle.com if you need additional information or have any 20 * or visit www.oracle.com if you need additional information or have any
21 * questions. 21 * questions.
22 * 22 *
23 */ 23 */
24 24
25
26 #include "precompiled.hpp" 25 #include "precompiled.hpp"
26 #include "code/codeCache.hpp"
27 #include "code/nmethod.hpp" 27 #include "code/nmethod.hpp"
28 #include "gc_implementation/g1/g1CodeCacheRemSet.hpp" 28 #include "gc_implementation/g1/g1CodeCacheRemSet.hpp"
29 #include "gc_implementation/g1/heapRegion.hpp"
30 #include "memory/heap.hpp"
29 #include "memory/iterator.hpp" 31 #include "memory/iterator.hpp"
32 #include "oops/oop.inline.hpp"
33 #include "utilities/hashtable.inline.hpp"
34 #include "utilities/stack.inline.hpp"
30 35
31 PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC 36 PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
32 37
33 G1CodeRootChunk::G1CodeRootChunk() : _top(NULL), _next(NULL), _prev(NULL), _free(NULL) { 38 class CodeRootSetTable : public Hashtable<nmethod*, mtGC> {
34 _top = bottom(); 39 friend class G1CodeRootSetTest;
35 } 40 typedef HashtableEntry<nmethod*, mtGC> Entry;
36 41
37 void G1CodeRootChunk::reset() { 42 static CodeRootSetTable* volatile _purge_list;
38 _next = _prev = NULL; 43
39 _free = NULL; 44 CodeRootSetTable* _purge_next;
40 _top = bottom(); 45
41 } 46 unsigned int compute_hash(nmethod* nm) {
42 47 uintptr_t hash = (uintptr_t)nm;
43 void G1CodeRootChunk::nmethods_do(CodeBlobClosure* cl) { 48 return hash ^ (hash >> 7); // code heap blocks are 128byte aligned
44 NmethodOrLink* cur = bottom(); 49 }
45 while (cur != _top) { 50
46 if (is_nmethod(cur)) { 51 Entry* new_entry(nmethod* nm);
47 cl->do_code_blob(cur->_nmethod); 52
48 } 53 public:
49 cur++; 54 CodeRootSetTable(int size) : Hashtable<nmethod*, mtGC>(size, sizeof(Entry)), _purge_next(NULL) {}
50 } 55 ~CodeRootSetTable();
51 } 56
52 57 // Needs to be protected locks
53 bool G1CodeRootChunk::remove_lock_free(nmethod* method) { 58 bool add(nmethod* nm);
54 NmethodOrLink* cur = bottom(); 59 bool remove(nmethod* nm);
55 60
56 for (NmethodOrLink* cur = bottom(); cur != _top; cur++) { 61 // Can be called without locking
57 if (cur->_nmethod == method) { 62 bool contains(nmethod* nm);
58 bool result = Atomic::cmpxchg_ptr(NULL, &cur->_nmethod, method) == method; 63
59 64 int entry_size() const { return BasicHashtable<mtGC>::entry_size(); }
60 if (!result) { 65
61 // Someone else cleared out this entry. 66 void copy_to(CodeRootSetTable* new_table);
62 return false; 67 void nmethods_do(CodeBlobClosure* blk);
63 } 68
64 69 template<typename CB>
65 // The method was cleared. Time to link it into the free list. 70 void remove_if(CB& should_remove);
66 NmethodOrLink* prev_free; 71
67 do { 72 static void purge_list_append(CodeRootSetTable* tbl);
68 prev_free = (NmethodOrLink*)_free; 73 static void purge();
69 cur->_link = prev_free; 74
70 } while (Atomic::cmpxchg_ptr(cur, &_free, prev_free) != prev_free); 75 static size_t static_mem_size() {
71 76 return sizeof(_purge_list);
77 }
78 };
79
80 CodeRootSetTable* volatile CodeRootSetTable::_purge_list = NULL;
81
82 CodeRootSetTable::Entry* CodeRootSetTable::new_entry(nmethod* nm) {
83 unsigned int hash = compute_hash(nm);
84 Entry* entry = (Entry*) new_entry_free_list();
85 if (entry == NULL) {
86 entry = (Entry*) NEW_C_HEAP_ARRAY2(char, entry_size(), mtGC, CURRENT_PC);
87 }
88 entry->set_next(NULL);
89 entry->set_hash(hash);
90 entry->set_literal(nm);
91 return entry;
92 }
93
94 CodeRootSetTable::~CodeRootSetTable() {
95 for (int index = 0; index < table_size(); ++index) {
96 for (Entry* e = bucket(index); e != NULL; ) {
97 Entry* to_remove = e;
98 // read next before freeing.
99 e = e->next();
100 unlink_entry(to_remove);
101 FREE_C_HEAP_ARRAY(char, to_remove, mtGC);
102 }
103 }
104 assert(number_of_entries() == 0, "should have removed all entries");
105 free_buckets();
106 for (BasicHashtableEntry<mtGC>* e = new_entry_free_list(); e != NULL; e = new_entry_free_list()) {
107 FREE_C_HEAP_ARRAY(char, e, mtGC);
108 }
109 }
110
111 bool CodeRootSetTable::add(nmethod* nm) {
112 if (!contains(nm)) {
113 Entry* e = new_entry(nm);
114 int index = hash_to_index(e->hash());
115 add_entry(index, e);
116 return true;
117 }
118 return false;
119 }
120
121 bool CodeRootSetTable::contains(nmethod* nm) {
122 int index = hash_to_index(compute_hash(nm));
123 for (Entry* e = bucket(index); e != NULL; e = e->next()) {
124 if (e->literal() == nm) {
72 return true; 125 return true;
73 } 126 }
74 } 127 }
75
76 return false; 128 return false;
77 } 129 }
78 130
79 G1CodeRootChunkManager::G1CodeRootChunkManager() : _free_list(), _num_chunks_handed_out(0) { 131 bool CodeRootSetTable::remove(nmethod* nm) {
80 _free_list.initialize(); 132 int index = hash_to_index(compute_hash(nm));
81 _free_list.set_size(G1CodeRootChunk::word_size()); 133 Entry* previous = NULL;
82 } 134 for (Entry* e = bucket(index); e != NULL; previous = e, e = e->next()) {
83 135 if (e->literal() == nm) {
84 size_t G1CodeRootChunkManager::fl_mem_size() { 136 if (previous != NULL) {
85 return _free_list.count() * _free_list.size(); 137 previous->set_next(e->next());
86 } 138 } else {
87 139 set_entry(index, e->next());
88 void G1CodeRootChunkManager::free_all_chunks(FreeList<G1CodeRootChunk>* list) { 140 }
89 _num_chunks_handed_out -= list->count(); 141 free_entry(e);
90 _free_list.prepend(list); 142 return true;
91 } 143 }
92 144 }
93 void G1CodeRootChunkManager::free_chunk(G1CodeRootChunk* chunk) { 145 return false;
94 _free_list.return_chunk_at_head(chunk); 146 }
95 _num_chunks_handed_out--; 147
96 } 148 void CodeRootSetTable::copy_to(CodeRootSetTable* new_table) {
97 149 for (int index = 0; index < table_size(); ++index) {
98 void G1CodeRootChunkManager::purge_chunks(size_t keep_ratio) { 150 for (Entry* e = bucket(index); e != NULL; e = e->next()) {
99 size_t keep = _num_chunks_handed_out * keep_ratio / 100; 151 new_table->add(e->literal());
100 if (keep >= (size_t)_free_list.count()) { 152 }
101 return; 153 }
102 } 154 new_table->copy_freelist(this);
103 155 }
104 FreeList<G1CodeRootChunk> temp; 156
105 temp.initialize(); 157 void CodeRootSetTable::nmethods_do(CodeBlobClosure* blk) {
106 temp.set_size(G1CodeRootChunk::word_size()); 158 for (int index = 0; index < table_size(); ++index) {
107 159 for (Entry* e = bucket(index); e != NULL; e = e->next()) {
108 _free_list.getFirstNChunksFromList((size_t)_free_list.count() - keep, &temp); 160 blk->do_code_blob(e->literal());
109 161 }
110 G1CodeRootChunk* cur = temp.get_chunk_at_head(); 162 }
111 while (cur != NULL) { 163 }
112 delete cur; 164
113 cur = temp.get_chunk_at_head(); 165 template<typename CB>
114 } 166 void CodeRootSetTable::remove_if(CB& should_remove) {
115 } 167 for (int index = 0; index < table_size(); ++index) {
116 168 Entry* previous = NULL;
117 size_t G1CodeRootChunkManager::static_mem_size() { 169 Entry* e = bucket(index);
118 return sizeof(G1CodeRootChunkManager); 170 while (e != NULL) {
119 } 171 Entry* next = e->next();
120 172 if (should_remove(e->literal())) {
121 173 if (previous != NULL) {
122 G1CodeRootChunk* G1CodeRootChunkManager::new_chunk() { 174 previous->set_next(next);
123 G1CodeRootChunk* result = _free_list.get_chunk_at_head(); 175 } else {
124 if (result == NULL) { 176 set_entry(index, next);
125 result = new G1CodeRootChunk(); 177 }
126 } 178 free_entry(e);
127 _num_chunks_handed_out++; 179 } else {
128 result->reset(); 180 previous = e;
129 return result; 181 }
182 e = next;
183 }
184 }
185 }
186
187 G1CodeRootSet::~G1CodeRootSet() {
188 delete _table;
189 }
190
191 CodeRootSetTable* G1CodeRootSet::load_acquire_table() {
192 return (CodeRootSetTable*) OrderAccess::load_ptr_acquire(&_table);
193 }
194
195 void G1CodeRootSet::allocate_small_table() {
196 _table = new CodeRootSetTable(SmallSize);
197 }
198
199 void CodeRootSetTable::purge_list_append(CodeRootSetTable* table) {
200 for (;;) {
201 table->_purge_next = _purge_list;
202 CodeRootSetTable* old = (CodeRootSetTable*) Atomic::cmpxchg_ptr(table, &_purge_list, table->_purge_next);
203 if (old == table->_purge_next) {
204 break;
205 }
206 }
207 }
208
209 void CodeRootSetTable::purge() {
210 CodeRootSetTable* table = _purge_list;
211 _purge_list = NULL;
212 while (table != NULL) {
213 CodeRootSetTable* to_purge = table;
214 table = table->_purge_next;
215 delete to_purge;
216 }
217 }
218
219 void G1CodeRootSet::move_to_large() {
220 CodeRootSetTable* temp = new CodeRootSetTable(LargeSize);
221
222 _table->copy_to(temp);
223
224 CodeRootSetTable::purge_list_append(_table);
225
226 OrderAccess::release_store_ptr(&_table, temp);
227 }
228
229
230 void G1CodeRootSet::purge() {
231 CodeRootSetTable::purge();
232 }
233
234 size_t G1CodeRootSet::static_mem_size() {
235 return CodeRootSetTable::static_mem_size();
236 }
237
238 void G1CodeRootSet::add(nmethod* method) {
239 bool added = false;
240 if (is_empty()) {
241 allocate_small_table();
242 }
243 added = _table->add(method);
244 if (_length == Threshold) {
245 move_to_large();
246 }
247 if (added) {
248 ++_length;
249 }
250 }
251
252 bool G1CodeRootSet::remove(nmethod* method) {
253 bool removed = false;
254 if (_table != NULL) {
255 removed = _table->remove(method);
256 }
257 if (removed) {
258 _length--;
259 if (_length == 0) {
260 clear();
261 }
262 }
263 return removed;
264 }
265
266 bool G1CodeRootSet::contains(nmethod* method) {
267 CodeRootSetTable* table = load_acquire_table();
268 if (table != NULL) {
269 return table->contains(method);
270 }
271 return false;
272 }
273
274 void G1CodeRootSet::clear() {
275 delete _table;
276 _table = NULL;
277 _length = 0;
278 }
279
280 size_t G1CodeRootSet::mem_size() {
281 return sizeof(*this) +
282 (_table != NULL ? sizeof(CodeRootSetTable) + _table->entry_size() * _length : 0);
283 }
284
285 void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
286 if (_table != NULL) {
287 _table->nmethods_do(blk);
288 }
289 }
290
291 class CleanCallback : public StackObj {
292 class PointsIntoHRDetectionClosure : public OopClosure {
293 HeapRegion* _hr;
294 public:
295 bool _points_into;
296 PointsIntoHRDetectionClosure(HeapRegion* hr) : _hr(hr), _points_into(false) {}
297
298 void do_oop(narrowOop* o) {
299 do_oop_work(o);
300 }
301
302 void do_oop(oop* o) {
303 do_oop_work(o);
304 }
305
306 template <typename T>
307 void do_oop_work(T* p) {
308 if (_hr->is_in(oopDesc::load_decode_heap_oop(p))) {
309 _points_into = true;
310 }
311 }
312 };
313
314 PointsIntoHRDetectionClosure _detector;
315 CodeBlobToOopClosure _blobs;
316
317 public:
318 CleanCallback(HeapRegion* hr) : _detector(hr), _blobs(&_detector, !CodeBlobToOopClosure::FixRelocations) {}
319
320 bool operator() (nmethod* nm) {
321 _detector._points_into = false;
322 _blobs.do_code_blob(nm);
323 return _detector._points_into;
324 }
325 };
326
327 void G1CodeRootSet::clean(HeapRegion* owner) {
328 CleanCallback should_clean(owner);
329 if (_table != NULL) {
330 _table->remove_if(should_clean);
331 }
130 } 332 }
131 333
132 #ifndef PRODUCT 334 #ifndef PRODUCT
133 335
134 size_t G1CodeRootChunkManager::num_chunks_handed_out() const { 336 class G1CodeRootSetTest {
135 return _num_chunks_handed_out; 337 public:
136 } 338 static void test() {
137 339 {
138 size_t G1CodeRootChunkManager::num_free_chunks() const { 340 G1CodeRootSet set1;
139 return (size_t)_free_list.count(); 341 assert(set1.is_empty(), "Code root set must be initially empty but is not.");
342
343 assert(G1CodeRootSet::static_mem_size() == sizeof(void*),
344 err_msg("The code root set's static memory usage is incorrect, "SIZE_FORMAT" bytes", G1CodeRootSet::static_mem_size()));
345
346 set1.add((nmethod*)1);
347 assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "
348 SIZE_FORMAT" elements", set1.length()));
349
350 const size_t num_to_add = (size_t)G1CodeRootSet::Threshold + 1;
351
352 for (size_t i = 1; i <= num_to_add; i++) {
353 set1.add((nmethod*)1);
354 }
355 assert(set1.length() == 1,
356 err_msg("Duplicate detection should not have increased the set size but "
357 "is "SIZE_FORMAT, set1.length()));
358
359 for (size_t i = 2; i <= num_to_add; i++) {
360 set1.add((nmethod*)(uintptr_t)(i));
361 }
362 assert(set1.length() == num_to_add,
363 err_msg("After adding in total "SIZE_FORMAT" distinct code roots, they "
364 "need to be in the set, but there are only "SIZE_FORMAT,
365 num_to_add, set1.length()));
366
367 assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");
368
369 size_t num_popped = 0;
370 for (size_t i = 1; i <= num_to_add; i++) {
371 bool removed = set1.remove((nmethod*)i);
372 if (removed) {
373 num_popped += 1;
374 } else {
375 break;
376 }
377 }
378 assert(num_popped == num_to_add,
379 err_msg("Managed to pop "SIZE_FORMAT" code roots, but only "SIZE_FORMAT" "
380 "were added", num_popped, num_to_add));
381 assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");
382
383 G1CodeRootSet::purge();
384
385 assert(CodeRootSetTable::_purge_list == NULL, "should have purged old small tables");
386
387 }
388
389 }
390 };
391
392 void TestCodeCacheRemSet_test() {
393 G1CodeRootSetTest::test();
140 } 394 }
141 395
142 #endif 396 #endif
143
144 G1CodeRootChunkManager G1CodeRootSet::_default_chunk_manager;
145
146 void G1CodeRootSet::purge_chunks(size_t keep_ratio) {
147 _default_chunk_manager.purge_chunks(keep_ratio);
148 }
149
150 size_t G1CodeRootSet::free_chunks_static_mem_size() {
151 return _default_chunk_manager.static_mem_size();
152 }
153
154 size_t G1CodeRootSet::free_chunks_mem_size() {
155 return _default_chunk_manager.fl_mem_size();
156 }
157
158 G1CodeRootSet::G1CodeRootSet(G1CodeRootChunkManager* manager) : _manager(manager), _list(), _length(0) {
159 if (_manager == NULL) {
160 _manager = &_default_chunk_manager;
161 }
162 _list.initialize();
163 _list.set_size(G1CodeRootChunk::word_size());
164 }
165
166 G1CodeRootSet::~G1CodeRootSet() {
167 clear();
168 }
169
170 void G1CodeRootSet::add(nmethod* method) {
171 if (!contains(method)) {
172 // Find the first chunk that isn't full.
173 G1CodeRootChunk* cur = _list.head();
174 while (cur != NULL) {
175 if (!cur->is_full()) {
176 break;
177 }
178 cur = cur->next();
179 }
180
181 // All chunks are full, get a new chunk.
182 if (cur == NULL) {
183 cur = new_chunk();
184 _list.return_chunk_at_head(cur);
185 }
186
187 // Add the nmethod.
188 bool result = cur->add(method);
189
190 guarantee(result, err_msg("Not able to add nmethod "PTR_FORMAT" to newly allocated chunk.", method));
191
192 _length++;
193 }
194 }
195
196 void G1CodeRootSet::remove_lock_free(nmethod* method) {
197 G1CodeRootChunk* found = find(method);
198 if (found != NULL) {
199 bool result = found->remove_lock_free(method);
200 if (result) {
201 Atomic::dec_ptr((volatile intptr_t*)&_length);
202 }
203 }
204 assert(!contains(method), err_msg(PTR_FORMAT" still contains nmethod "PTR_FORMAT, this, method));
205 }
206
207 nmethod* G1CodeRootSet::pop() {
208 while (true) {
209 G1CodeRootChunk* cur = _list.head();
210 if (cur == NULL) {
211 assert(_length == 0, "when there are no chunks, there should be no elements");
212 return NULL;
213 }
214 nmethod* result = cur->pop();
215 if (result != NULL) {
216 _length--;
217 return result;
218 } else {
219 free(_list.get_chunk_at_head());
220 }
221 }
222 }
223
224 G1CodeRootChunk* G1CodeRootSet::find(nmethod* method) {
225 G1CodeRootChunk* cur = _list.head();
226 while (cur != NULL) {
227 if (cur->contains(method)) {
228 return cur;
229 }
230 cur = (G1CodeRootChunk*)cur->next();
231 }
232 return NULL;
233 }
234
235 void G1CodeRootSet::free(G1CodeRootChunk* chunk) {
236 free_chunk(chunk);
237 }
238
239 bool G1CodeRootSet::contains(nmethod* method) {
240 return find(method) != NULL;
241 }
242
243 void G1CodeRootSet::clear() {
244 free_all_chunks(&_list);
245 _length = 0;
246 }
247
248 void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
249 G1CodeRootChunk* cur = _list.head();
250 while (cur != NULL) {
251 cur->nmethods_do(blk);
252 cur = (G1CodeRootChunk*)cur->next();
253 }
254 }
255
256 size_t G1CodeRootSet::static_mem_size() {
257 return sizeof(G1CodeRootSet);
258 }
259
260 size_t G1CodeRootSet::mem_size() {
261 return G1CodeRootSet::static_mem_size() + _list.count() * _list.size();
262 }
263
264 #ifndef PRODUCT
265
266 void G1CodeRootSet::test() {
267 G1CodeRootChunkManager mgr;
268
269 assert(mgr.num_chunks_handed_out() == 0, "Must not have handed out chunks yet");
270
271 assert(G1CodeRootChunkManager::static_mem_size() > sizeof(void*),
272 err_msg("The chunk manager's static memory usage seems too small, is only "SIZE_FORMAT" bytes.", G1CodeRootChunkManager::static_mem_size()));
273
274 // The number of chunks that we allocate for purge testing.
275 size_t const num_chunks = 10;
276
277 {
278 G1CodeRootSet set1(&mgr);
279 assert(set1.is_empty(), "Code root set must be initially empty but is not.");
280
281 assert(G1CodeRootSet::static_mem_size() > sizeof(void*),
282 err_msg("The code root set's static memory usage seems too small, is only "SIZE_FORMAT" bytes", G1CodeRootSet::static_mem_size()));
283
284 set1.add((nmethod*)1);
285 assert(mgr.num_chunks_handed_out() == 1,
286 err_msg("Must have allocated and handed out one chunk, but handed out "
287 SIZE_FORMAT" chunks", mgr.num_chunks_handed_out()));
288 assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "
289 SIZE_FORMAT" elements", set1.length()));
290
291 // G1CodeRootChunk::word_size() is larger than G1CodeRootChunk::num_entries which
292 // we cannot access.
293 for (uint i = 0; i < G1CodeRootChunk::word_size() + 1; i++) {
294 set1.add((nmethod*)1);
295 }
296 assert(mgr.num_chunks_handed_out() == 1,
297 err_msg("Duplicate detection must have prevented allocation of further "
298 "chunks but allocated "SIZE_FORMAT, mgr.num_chunks_handed_out()));
299 assert(set1.length() == 1,
300 err_msg("Duplicate detection should not have increased the set size but "
301 "is "SIZE_FORMAT, set1.length()));
302
303 size_t num_total_after_add = G1CodeRootChunk::word_size() + 1;
304 for (size_t i = 0; i < num_total_after_add - 1; i++) {
305 set1.add((nmethod*)(uintptr_t)(2 + i));
306 }
307 assert(mgr.num_chunks_handed_out() > 1,
308 "After adding more code roots, more than one additional chunk should have been handed out");
309 assert(set1.length() == num_total_after_add,
310 err_msg("After adding in total "SIZE_FORMAT" distinct code roots, they "
311 "need to be in the set, but there are only "SIZE_FORMAT,
312 num_total_after_add, set1.length()));
313
314 size_t num_popped = 0;
315 while (set1.pop() != NULL) {
316 num_popped++;
317 }
318 assert(num_popped == num_total_after_add,
319 err_msg("Managed to pop "SIZE_FORMAT" code roots, but only "SIZE_FORMAT" "
320 "were added", num_popped, num_total_after_add));
321 assert(mgr.num_chunks_handed_out() == 0,
322 err_msg("After popping all elements, all chunks must have been returned "
323 "but there are still "SIZE_FORMAT" additional", mgr.num_chunks_handed_out()));
324
325 mgr.purge_chunks(0);
326 assert(mgr.num_free_chunks() == 0,
327 err_msg("After purging everything, the free list must be empty but still "
328 "contains "SIZE_FORMAT" chunks", mgr.num_free_chunks()));
329
330 // Add some more handed out chunks.
331 size_t i = 0;
332 while (mgr.num_chunks_handed_out() < num_chunks) {
333 set1.add((nmethod*)i);
334 i++;
335 }
336
337 {
338 // Generate chunks on the free list.
339 G1CodeRootSet set2(&mgr);
340 size_t i = 0;
341 while (mgr.num_chunks_handed_out() < (num_chunks * 2)) {
342 set2.add((nmethod*)i);
343 i++;
344 }
345 // Exit of the scope of the set2 object will call the destructor that generates
346 // num_chunks elements on the free list.
347 }
348
349 assert(mgr.num_chunks_handed_out() == num_chunks,
350 err_msg("Deletion of the second set must have resulted in giving back "
351 "those, but there are still "SIZE_FORMAT" additional handed out, expecting "
352 SIZE_FORMAT, mgr.num_chunks_handed_out(), num_chunks));
353 assert(mgr.num_free_chunks() == num_chunks,
354 err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list "
355 "but there are only "SIZE_FORMAT, num_chunks, mgr.num_free_chunks()));
356
357 size_t const test_percentage = 50;
358 mgr.purge_chunks(test_percentage);
359 assert(mgr.num_chunks_handed_out() == num_chunks,
360 err_msg("Purging must not hand out chunks but there are "SIZE_FORMAT,
361 mgr.num_chunks_handed_out()));
362 assert(mgr.num_free_chunks() == (size_t)(mgr.num_chunks_handed_out() * test_percentage / 100),
363 err_msg("Must have purged "SIZE_FORMAT" percent of "SIZE_FORMAT" chunks"
364 "but there are "SIZE_FORMAT, test_percentage, num_chunks,
365 mgr.num_free_chunks()));
366 // Purge the remainder of the chunks on the free list.
367 mgr.purge_chunks(0);
368 assert(mgr.num_free_chunks() == 0, "Free List must be empty");
369 assert(mgr.num_chunks_handed_out() == num_chunks,
370 err_msg("Expected to be "SIZE_FORMAT" chunks handed out from the first set "
371 "but there are "SIZE_FORMAT, num_chunks, mgr.num_chunks_handed_out()));
372
373 // Exit of the scope of the set1 object will call the destructor that generates
374 // num_chunks additional elements on the free list.
375 }
376
377 assert(mgr.num_chunks_handed_out() == 0,
378 err_msg("Deletion of the only set must have resulted in no chunks handed "
379 "out, but there is still "SIZE_FORMAT" handed out", mgr.num_chunks_handed_out()));
380 assert(mgr.num_free_chunks() == num_chunks,
381 err_msg("After freeing "SIZE_FORMAT" chunks, they must be on the free list "
382 "but there are only "SIZE_FORMAT, num_chunks, mgr.num_free_chunks()));
383
384 // Restore initial state.
385 mgr.purge_chunks(0);
386 assert(mgr.num_free_chunks() == 0, "Free List must be empty");
387 assert(mgr.num_chunks_handed_out() == 0, "No additional elements must have been handed out yet");
388 }
389
390 void TestCodeCacheRemSet_test() {
391 G1CodeRootSet::test();
392 }
393 #endif

mercurial