Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/hotspot/src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp
38920 views
1
/*
2
* Copyright (c) 2014, 2018, 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
#include "precompiled.hpp"
26
#include "code/codeCache.hpp"
27
#include "code/nmethod.hpp"
28
#include "gc_implementation/g1/g1CodeCacheRemSet.hpp"
29
#include "gc_implementation/g1/heapRegion.hpp"
30
#include "memory/heap.hpp"
31
#include "memory/iterator.hpp"
32
#include "oops/oop.inline.hpp"
33
#include "utilities/hashtable.inline.hpp"
34
#include "utilities/stack.inline.hpp"
35
36
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
37
38
class CodeRootSetTable : public Hashtable<nmethod*, mtGC> {
39
friend class G1CodeRootSetTest;
40
typedef HashtableEntry<nmethod*, mtGC> Entry;
41
42
static CodeRootSetTable* volatile _purge_list;
43
44
CodeRootSetTable* _purge_next;
45
46
unsigned int compute_hash(nmethod* nm) {
47
uintptr_t hash = (uintptr_t)nm;
48
return hash ^ (hash >> 7); // code heap blocks are 128byte aligned
49
}
50
51
void remove_entry(Entry* e, Entry* previous);
52
Entry* new_entry(nmethod* nm);
53
54
public:
55
CodeRootSetTable(int size) : Hashtable<nmethod*, mtGC>(size, sizeof(Entry)), _purge_next(NULL) {}
56
~CodeRootSetTable();
57
58
// Needs to be protected locks
59
bool add(nmethod* nm);
60
bool remove(nmethod* nm);
61
62
// Can be called without locking
63
bool contains(nmethod* nm);
64
65
int entry_size() const { return BasicHashtable<mtGC>::entry_size(); }
66
67
void copy_to(CodeRootSetTable* new_table);
68
void nmethods_do(CodeBlobClosure* blk);
69
70
template<typename CB>
71
int remove_if(CB& should_remove);
72
73
static void purge_list_append(CodeRootSetTable* tbl);
74
static void purge();
75
76
static size_t static_mem_size() {
77
return sizeof(_purge_list);
78
}
79
80
size_t mem_size();
81
};
82
83
CodeRootSetTable* volatile CodeRootSetTable::_purge_list = NULL;
84
85
size_t CodeRootSetTable::mem_size() {
86
return sizeof(CodeRootSetTable) + (entry_size() * number_of_entries()) + (sizeof(HashtableBucket<mtGC>) * table_size());
87
}
88
89
CodeRootSetTable::Entry* CodeRootSetTable::new_entry(nmethod* nm) {
90
unsigned int hash = compute_hash(nm);
91
Entry* entry = (Entry*) new_entry_free_list();
92
if (entry == NULL) {
93
entry = (Entry*) NEW_C_HEAP_ARRAY2(char, entry_size(), mtGC, CURRENT_PC);
94
}
95
entry->set_next(NULL);
96
entry->set_hash(hash);
97
entry->set_literal(nm);
98
return entry;
99
}
100
101
void CodeRootSetTable::remove_entry(Entry* e, Entry* previous) {
102
int index = hash_to_index(e->hash());
103
assert((e == bucket(index)) == (previous == NULL), "if e is the first entry then previous should be null");
104
105
if (previous == NULL) {
106
set_entry(index, e->next());
107
} else {
108
previous->set_next(e->next());
109
}
110
free_entry(e);
111
}
112
113
CodeRootSetTable::~CodeRootSetTable() {
114
for (int index = 0; index < table_size(); ++index) {
115
for (Entry* e = bucket(index); e != NULL; ) {
116
Entry* to_remove = e;
117
// read next before freeing.
118
e = e->next();
119
unlink_entry(to_remove);
120
FREE_C_HEAP_ARRAY(char, to_remove, mtGC);
121
}
122
}
123
assert(number_of_entries() == 0, "should have removed all entries");
124
free_buckets();
125
for (BasicHashtableEntry<mtGC>* e = new_entry_free_list(); e != NULL; e = new_entry_free_list()) {
126
FREE_C_HEAP_ARRAY(char, e, mtGC);
127
}
128
}
129
130
bool CodeRootSetTable::add(nmethod* nm) {
131
if (!contains(nm)) {
132
Entry* e = new_entry(nm);
133
int index = hash_to_index(e->hash());
134
add_entry(index, e);
135
return true;
136
}
137
return false;
138
}
139
140
bool CodeRootSetTable::contains(nmethod* nm) {
141
int index = hash_to_index(compute_hash(nm));
142
for (Entry* e = bucket(index); e != NULL; e = e->next()) {
143
if (e->literal() == nm) {
144
return true;
145
}
146
}
147
return false;
148
}
149
150
bool CodeRootSetTable::remove(nmethod* nm) {
151
int index = hash_to_index(compute_hash(nm));
152
Entry* previous = NULL;
153
for (Entry* e = bucket(index); e != NULL; previous = e, e = e->next()) {
154
if (e->literal() == nm) {
155
remove_entry(e, previous);
156
return true;
157
}
158
}
159
return false;
160
}
161
162
void CodeRootSetTable::copy_to(CodeRootSetTable* new_table) {
163
for (int index = 0; index < table_size(); ++index) {
164
for (Entry* e = bucket(index); e != NULL; e = e->next()) {
165
new_table->add(e->literal());
166
}
167
}
168
new_table->copy_freelist(this);
169
}
170
171
void CodeRootSetTable::nmethods_do(CodeBlobClosure* blk) {
172
for (int index = 0; index < table_size(); ++index) {
173
for (Entry* e = bucket(index); e != NULL; e = e->next()) {
174
blk->do_code_blob(e->literal());
175
}
176
}
177
}
178
179
template<typename CB>
180
int CodeRootSetTable::remove_if(CB& should_remove) {
181
int num_removed = 0;
182
for (int index = 0; index < table_size(); ++index) {
183
Entry* previous = NULL;
184
Entry* e = bucket(index);
185
while (e != NULL) {
186
Entry* next = e->next();
187
if (should_remove(e->literal())) {
188
remove_entry(e, previous);
189
++num_removed;
190
} else {
191
previous = e;
192
}
193
e = next;
194
}
195
}
196
return num_removed;
197
}
198
199
G1CodeRootSet::~G1CodeRootSet() {
200
delete _table;
201
}
202
203
CodeRootSetTable* G1CodeRootSet::load_acquire_table() {
204
return (CodeRootSetTable*) OrderAccess::load_ptr_acquire(&_table);
205
}
206
207
void G1CodeRootSet::allocate_small_table() {
208
CodeRootSetTable* temp = new CodeRootSetTable(SmallSize);
209
210
OrderAccess::release_store_ptr(&_table, temp);
211
}
212
213
void CodeRootSetTable::purge_list_append(CodeRootSetTable* table) {
214
for (;;) {
215
table->_purge_next = _purge_list;
216
CodeRootSetTable* old = (CodeRootSetTable*) Atomic::cmpxchg_ptr(table, &_purge_list, table->_purge_next);
217
if (old == table->_purge_next) {
218
break;
219
}
220
}
221
}
222
223
void CodeRootSetTable::purge() {
224
CodeRootSetTable* table = _purge_list;
225
_purge_list = NULL;
226
while (table != NULL) {
227
CodeRootSetTable* to_purge = table;
228
table = table->_purge_next;
229
delete to_purge;
230
}
231
}
232
233
void G1CodeRootSet::move_to_large() {
234
CodeRootSetTable* temp = new CodeRootSetTable(LargeSize);
235
236
_table->copy_to(temp);
237
238
CodeRootSetTable::purge_list_append(_table);
239
240
OrderAccess::release_store_ptr(&_table, temp);
241
}
242
243
void G1CodeRootSet::purge() {
244
CodeRootSetTable::purge();
245
}
246
247
size_t G1CodeRootSet::static_mem_size() {
248
return CodeRootSetTable::static_mem_size();
249
}
250
251
void G1CodeRootSet::add(nmethod* method) {
252
bool added = false;
253
if (is_empty()) {
254
allocate_small_table();
255
}
256
added = _table->add(method);
257
if (added) {
258
if (_length == Threshold) {
259
move_to_large();
260
}
261
++_length;
262
}
263
assert(_length == (size_t)_table->number_of_entries(), "sizes should match");
264
}
265
266
bool G1CodeRootSet::remove(nmethod* method) {
267
bool removed = false;
268
if (_table != NULL) {
269
removed = _table->remove(method);
270
}
271
if (removed) {
272
_length--;
273
if (_length == 0) {
274
clear();
275
}
276
}
277
assert((_length == 0 && _table == NULL) ||
278
(_length == (size_t)_table->number_of_entries()), "sizes should match");
279
return removed;
280
}
281
282
bool G1CodeRootSet::contains(nmethod* method) {
283
CodeRootSetTable* table = load_acquire_table(); // contains() may be called outside of lock, so ensure mem sync.
284
if (table != NULL) {
285
return table->contains(method);
286
}
287
return false;
288
}
289
290
void G1CodeRootSet::clear() {
291
delete _table;
292
_table = NULL;
293
_length = 0;
294
}
295
296
size_t G1CodeRootSet::mem_size() {
297
return sizeof(*this) + (_table != NULL ? _table->mem_size() : 0);
298
}
299
300
void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
301
if (_table != NULL) {
302
_table->nmethods_do(blk);
303
}
304
}
305
306
class CleanCallback : public StackObj {
307
class PointsIntoHRDetectionClosure : public OopClosure {
308
HeapRegion* _hr;
309
public:
310
bool _points_into;
311
PointsIntoHRDetectionClosure(HeapRegion* hr) : _hr(hr), _points_into(false) {}
312
313
void do_oop(narrowOop* o) {
314
do_oop_work(o);
315
}
316
317
void do_oop(oop* o) {
318
do_oop_work(o);
319
}
320
321
template <typename T>
322
void do_oop_work(T* p) {
323
if (_hr->is_in(oopDesc::load_decode_heap_oop(p))) {
324
_points_into = true;
325
}
326
}
327
};
328
329
PointsIntoHRDetectionClosure _detector;
330
CodeBlobToOopClosure _blobs;
331
332
public:
333
CleanCallback(HeapRegion* hr) : _detector(hr), _blobs(&_detector, !CodeBlobToOopClosure::FixRelocations) {}
334
335
bool operator() (nmethod* nm) {
336
_detector._points_into = false;
337
_blobs.do_code_blob(nm);
338
return !_detector._points_into;
339
}
340
};
341
342
void G1CodeRootSet::clean(HeapRegion* owner) {
343
CleanCallback should_clean(owner);
344
if (_table != NULL) {
345
int removed = _table->remove_if(should_clean);
346
assert((size_t)removed <= _length, "impossible");
347
_length -= removed;
348
}
349
if (_length == 0) {
350
clear();
351
}
352
}
353
354
#ifndef PRODUCT
355
356
class G1CodeRootSetTest {
357
public:
358
static void test() {
359
{
360
G1CodeRootSet set1;
361
assert(set1.is_empty(), "Code root set must be initially empty but is not.");
362
363
assert(G1CodeRootSet::static_mem_size() == sizeof(void*),
364
err_msg("The code root set's static memory usage is incorrect, " SIZE_FORMAT " bytes", G1CodeRootSet::static_mem_size()));
365
366
set1.add((nmethod*)1);
367
assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "
368
SIZE_FORMAT " elements", set1.length()));
369
370
const size_t num_to_add = (size_t)G1CodeRootSet::Threshold + 1;
371
372
for (size_t i = 1; i <= num_to_add; i++) {
373
set1.add((nmethod*)1);
374
}
375
assert(set1.length() == 1,
376
err_msg("Duplicate detection should not have increased the set size but "
377
"is " SIZE_FORMAT, set1.length()));
378
379
for (size_t i = 2; i <= num_to_add; i++) {
380
set1.add((nmethod*)(uintptr_t)(i));
381
}
382
assert(set1.length() == num_to_add,
383
err_msg("After adding in total " SIZE_FORMAT " distinct code roots, they "
384
"need to be in the set, but there are only " SIZE_FORMAT,
385
num_to_add, set1.length()));
386
387
assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");
388
389
size_t num_popped = 0;
390
for (size_t i = 1; i <= num_to_add; i++) {
391
bool removed = set1.remove((nmethod*)i);
392
if (removed) {
393
num_popped += 1;
394
} else {
395
break;
396
}
397
}
398
assert(num_popped == num_to_add,
399
err_msg("Managed to pop " SIZE_FORMAT " code roots, but only " SIZE_FORMAT " "
400
"were added", num_popped, num_to_add));
401
assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");
402
403
G1CodeRootSet::purge();
404
405
assert(CodeRootSetTable::_purge_list == NULL, "should have purged old small tables");
406
407
}
408
409
}
410
};
411
412
void TestCodeCacheRemSet_test() {
413
G1CodeRootSetTest::test();
414
}
415
416
#endif
417
418