Mercurial > hg > graal-jvmci-8
comparison src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp @ 20494:7baf47cb97cb
8048268: G1 Code Root Migration performs poorly
Summary: Replace G1CodeRootSet with a Hashtable based implementation, merge Code Root Migration phase into Code Root Scanning
Reviewed-by: jmasa, brutisso, tschatzl
author | mgerdin |
---|---|
date | Fri, 29 Aug 2014 13:12:21 +0200 |
parents | 870c03421152 |
children | 58925d1f325e |
comparison
equal
deleted
inserted
replaced
20493:152cf4afc11f | 20494: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 |