Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/hotspot/share/memory/metaspace/rootChunkArea.cpp
40957 views
1
/*
2
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
3
* Copyright (c) 2020, 2021 SAP SE. All rights reserved.
4
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5
*
6
* This code is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU General Public License version 2 only, as
8
* published by the Free Software Foundation.
9
*
10
* This code is distributed in the hope that it will be useful, but WITHOUT
11
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
* version 2 for more details (a copy is included in the LICENSE file that
14
* accompanied this code).
15
*
16
* You should have received a copy of the GNU General Public License version
17
* 2 along with this work; if not, write to the Free Software Foundation,
18
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
*
20
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21
* or visit www.oracle.com if you need additional information or have any
22
* questions.
23
*
24
*/
25
26
#include "precompiled.hpp"
27
#include "logging/log.hpp"
28
#include "memory/allocation.hpp"
29
#include "memory/metaspace/chunkHeaderPool.hpp"
30
#include "memory/metaspace/chunkManager.hpp"
31
#include "memory/metaspace/freeChunkList.hpp"
32
#include "memory/metaspace/metachunk.hpp"
33
#include "memory/metaspace/metaspaceCommon.hpp"
34
#include "memory/metaspace/rootChunkArea.hpp"
35
#include "runtime/mutexLocker.hpp"
36
#include "utilities/debug.hpp"
37
#include "utilities/globalDefinitions.hpp"
38
39
namespace metaspace {
40
41
RootChunkArea::RootChunkArea(const MetaWord* base) :
42
_base(base),
43
_first_chunk(NULL)
44
{}
45
46
RootChunkArea::~RootChunkArea() {
47
// This is called when a VirtualSpaceNode is destructed (purged).
48
// All chunks should be free of course. In fact, there should only
49
// be one chunk, since all free chunks should have been merged.
50
if (_first_chunk != NULL) {
51
assert(_first_chunk->is_root_chunk() && _first_chunk->is_free(),
52
"Cannot delete root chunk area if not all chunks are free.");
53
ChunkHeaderPool::pool()->return_chunk_header(_first_chunk);
54
}
55
}
56
57
// Initialize: allocate a root node and a root chunk header; return the
58
// root chunk header. It will be partly initialized.
59
// Note: this just allocates a memory-less header; memory itself is allocated inside VirtualSpaceNode.
60
Metachunk* RootChunkArea::alloc_root_chunk_header(VirtualSpaceNode* node) {
61
assert(_first_chunk == 0, "already have a root");
62
Metachunk* c = ChunkHeaderPool::pool()->allocate_chunk_header();
63
c->initialize(node, const_cast<MetaWord*>(_base), chunklevel::ROOT_CHUNK_LEVEL);
64
_first_chunk = c;
65
return c;
66
}
67
68
// Given a chunk c, split it recursively until you get a chunk of the given target_level.
69
//
70
// The resulting target chunk resides at the same address as the original chunk.
71
// The resulting splinters are added to freelists.
72
//
73
// Returns pointer to the result chunk; the splitted-off chunks are added as
74
// free chunks to the freelists.
75
void RootChunkArea::split(chunklevel_t target_level, Metachunk* c, FreeChunkListVector* freelists) {
76
// Splitting a chunk once works like this:
77
//
78
// For a given chunk we want to split:
79
// - increase the chunk level (which halves its size)
80
// - (but leave base address as it is since it will be the leader of the newly
81
// created chunk pair)
82
// - then create a new chunk header of the same level, set its memory range
83
// to cover the second half of the old chunk.
84
// - wire them up (prev_in_vs/next_in_vs)
85
// - return the follower chunk as "splinter chunk" in the splinters array.
86
87
// Doing this multiple times will create a new free splinter chunk for every
88
// level we split:
89
//
90
// A <- original chunk
91
//
92
// B B <- split into two halves
93
//
94
// C C B <- first half split again
95
//
96
// D D C B <- first half split again ...
97
//
98
99
DEBUG_ONLY(check_pointer(c->base());)
100
DEBUG_ONLY(c->verify();)
101
assert(c->is_free(), "Can only split free chunks.");
102
103
DEBUG_ONLY(chunklevel::check_valid_level(target_level));
104
assert(target_level > c->level(), "Wrong target level");
105
106
const chunklevel_t starting_level = c->level();
107
108
while (c->level() < target_level) {
109
110
log_trace(metaspace)("Splitting chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c));
111
112
c->inc_level();
113
Metachunk* splinter_chunk = ChunkHeaderPool::pool()->allocate_chunk_header();
114
splinter_chunk->initialize(c->vsnode(), c->end(), c->level());
115
116
// Fix committed words info: If over the half of the original chunk was
117
// committed, committed area spills over into the follower chunk.
118
const size_t old_committed_words = c->committed_words();
119
if (old_committed_words > c->word_size()) {
120
c->set_committed_words(c->word_size());
121
splinter_chunk->set_committed_words(old_committed_words - c->word_size());
122
} else {
123
splinter_chunk->set_committed_words(0);
124
}
125
126
// Insert splinter chunk into vs list
127
if (c->next_in_vs() != NULL) {
128
c->next_in_vs()->set_prev_in_vs(splinter_chunk);
129
}
130
splinter_chunk->set_next_in_vs(c->next_in_vs());
131
splinter_chunk->set_prev_in_vs(c);
132
c->set_next_in_vs(splinter_chunk);
133
134
log_trace(metaspace)(".. Result chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c));
135
log_trace(metaspace)(".. Splinter chunk: " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(splinter_chunk));
136
137
// Add splinter to free lists
138
freelists->add(splinter_chunk);
139
}
140
141
assert(c->level() == target_level, "Sanity");
142
143
DEBUG_ONLY(verify();)
144
DEBUG_ONLY(c->verify();)
145
}
146
147
// Given a chunk, attempt to merge it recursively with its neighboring chunks.
148
//
149
// If successful (merged at least once), returns address of
150
// the merged chunk; NULL otherwise.
151
//
152
// The merged chunks are removed from the freelists.
153
//
154
// !!! Please note that if this method returns a non-NULL value, the
155
// original chunk will be invalid and should not be accessed anymore! !!!
156
Metachunk* RootChunkArea::merge(Metachunk* c, FreeChunkListVector* freelists) {
157
// Note rules:
158
//
159
// - a chunk always has a buddy, unless it is a root chunk.
160
// - In that buddy pair, a chunk is either leader or follower.
161
// - a chunk's base address is always aligned at its size.
162
// - if chunk is leader, its base address is also aligned to the size of the next
163
// lower level, at least. A follower chunk is not.
164
165
// How we merge once:
166
//
167
// For a given chunk c, which has to be free and non-root, we do:
168
// - find out if we are the leader or the follower chunk
169
// - if we are leader, next_in_vs must be the follower; if we are follower,
170
// prev_in_vs must be the leader. Now we have the buddy chunk.
171
// - However, if the buddy chunk itself is split (of a level higher than us)
172
// we cannot merge.
173
// - we can only merge if the buddy is of the same level as we are and it is
174
// free.
175
// - Then we merge by simply removing the follower chunk from the address range
176
// linked list (returning the now useless header to the pool) and decreasing
177
// the leader chunk level by one. That makes it double the size.
178
179
// Example:
180
// (lower case chunks are free, the * indicates the chunk we want to merge):
181
//
182
// ........................
183
// d d*c b A <- we return the second (d*) chunk...
184
//
185
// c* c b A <- we merge it with its predecessor and decrease its level...
186
//
187
// b* b A <- we merge it again, since its new neighbor was free too...
188
//
189
// a* A <- we merge it again, since its new neighbor was free too...
190
//
191
// And we are done, since its new neighbor, (A), is not free. We would also be done
192
// if the new neighbor itself is splintered.
193
194
DEBUG_ONLY(check_pointer(c->base());)
195
assert(!c->is_root_chunk(), "Cannot be merged further.");
196
assert(c->is_free(), "Can only merge free chunks.");
197
198
DEBUG_ONLY(c->verify();)
199
200
log_trace(metaspace)("Attempting to merge chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c));
201
202
const chunklevel_t starting_level = c->level();
203
204
bool stop = false;
205
Metachunk* result = NULL;
206
207
do {
208
209
// First find out if this chunk is the leader of its pair
210
const bool is_leader = c->is_leader();
211
212
// Note: this is either our buddy or a splinter of the buddy.
213
Metachunk* const buddy = c->is_leader() ? c->next_in_vs() : c->prev_in_vs();
214
DEBUG_ONLY(buddy->verify();)
215
216
// A buddy chunk must be of the same or higher level (so, same size or smaller)
217
// never be larger.
218
assert(buddy->level() >= c->level(), "Sanity");
219
220
// Is this really my buddy (same level) or a splinter of it (higher level)?
221
// Also, is it free?
222
if (buddy->level() != c->level() || buddy->is_free() == false) {
223
log_trace(metaspace)("cannot merge with chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(buddy));
224
stop = true;
225
} else {
226
log_trace(metaspace)("will merge with chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(buddy));
227
228
// We can merge with the buddy.
229
// First, remove buddy from the chunk manager.
230
assert(buddy->is_free(), "Sanity");
231
freelists->remove(buddy);
232
233
// Determine current leader and follower
234
Metachunk* leader;
235
Metachunk* follower;
236
if (is_leader) {
237
leader = c; follower = buddy;
238
} else {
239
leader = buddy; follower = c;
240
}
241
242
// Last checkpoint
243
assert(leader->end() == follower->base() &&
244
leader->level() == follower->level() &&
245
leader->is_free() && follower->is_free(), "Sanity");
246
247
// The new merged chunk is as far committed as possible (if leader
248
// chunk is fully committed, as far as the follower chunk).
249
size_t merged_committed_words = leader->committed_words();
250
if (merged_committed_words == leader->word_size()) {
251
merged_committed_words += follower->committed_words();
252
}
253
254
// Leader survives, follower chunk is freed. Remove follower from vslist ..
255
leader->set_next_in_vs(follower->next_in_vs());
256
if (follower->next_in_vs() != NULL) {
257
follower->next_in_vs()->set_prev_in_vs(leader);
258
}
259
260
// .. and return follower chunk header to pool for reuse.
261
ChunkHeaderPool::pool()->return_chunk_header(follower);
262
263
// Leader level gets decreased (leader chunk doubles in size) but
264
// base address stays the same.
265
leader->dec_level();
266
267
// set commit boundary
268
leader->set_committed_words(merged_committed_words);
269
270
// If the leader is now of root chunk size, stop merging
271
if (leader->is_root_chunk()) {
272
stop = true;
273
}
274
275
result = c = leader;
276
DEBUG_ONLY(leader->verify();)
277
}
278
} while (!stop);
279
280
#ifdef ASSERT
281
verify();
282
if (result != NULL) {
283
result->verify();
284
}
285
#endif // ASSERT
286
return result;
287
}
288
289
// Given a chunk c, which must be "in use" and must not be a root chunk, attempt to
290
// enlarge it in place by claiming its trailing buddy.
291
//
292
// This will only work if c is the leader of the buddy pair and the trailing buddy is free.
293
//
294
// If successful, the follower chunk will be removed from the freelists, the leader chunk c will
295
// double in size (level decreased by one).
296
//
297
// On success, true is returned, false otherwise.
298
bool RootChunkArea::attempt_enlarge_chunk(Metachunk* c, FreeChunkListVector* freelists) {
299
DEBUG_ONLY(check_pointer(c->base());)
300
assert(!c->is_root_chunk(), "Cannot be merged further.");
301
302
// There is no real reason for this limitation other than it is not
303
// needed on free chunks since they should be merged already:
304
assert(c->is_in_use(), "Can only enlarge in use chunks.");
305
DEBUG_ONLY(c->verify();)
306
307
if (!c->is_leader()) {
308
return false;
309
}
310
311
// We are the leader, so the buddy must follow us.
312
Metachunk* const buddy = c->next_in_vs();
313
DEBUG_ONLY(buddy->verify();)
314
315
// Of course buddy cannot be larger than us.
316
assert(buddy->level() >= c->level(), "Sanity");
317
318
// We cannot merge buddy in if it is not free...
319
if (!buddy->is_free()) {
320
return false;
321
}
322
// ... nor if it is splintered.
323
if (buddy->level() != c->level()) {
324
return false;
325
}
326
327
// Okay, lets enlarge c.
328
log_trace(metaspace)("Enlarging chunk " METACHUNK_FULL_FORMAT " by merging in follower " METACHUNK_FULL_FORMAT ".",
329
METACHUNK_FULL_FORMAT_ARGS(c), METACHUNK_FULL_FORMAT_ARGS(buddy));
330
331
// the enlarged c is as far committed as possible:
332
size_t merged_committed_words = c->committed_words();
333
if (merged_committed_words == c->word_size()) {
334
merged_committed_words += buddy->committed_words();
335
}
336
337
// Remove buddy from vs list...
338
Metachunk* successor = buddy->next_in_vs();
339
if (successor != NULL) {
340
successor->set_prev_in_vs(c);
341
}
342
c->set_next_in_vs(successor);
343
344
// .. and from freelist ...
345
freelists->remove(buddy);
346
347
// .. and return its empty husk to the pool...
348
ChunkHeaderPool::pool()->return_chunk_header(buddy);
349
350
// Then decrease level of c.
351
c->dec_level();
352
353
// and correct committed words if needed.
354
c->set_committed_words(merged_committed_words);
355
356
log_debug(metaspace)("Enlarged chunk " METACHUNK_FULL_FORMAT ".", METACHUNK_FULL_FORMAT_ARGS(c));
357
358
DEBUG_ONLY(verify());
359
return true;
360
}
361
362
// Returns true if this root chunk area is completely free:
363
// In that case, it should only contain one chunk (maximally merged, so a root chunk)
364
// and it should be free.
365
bool RootChunkArea::is_free() const {
366
return _first_chunk == NULL ||
367
(_first_chunk->is_root_chunk() && _first_chunk->is_free());
368
}
369
370
#ifdef ASSERT
371
372
#define assrt_(cond, ...) \
373
if (!(cond)) { \
374
fdStream errst(2); \
375
this->print_on(&errst); \
376
vmassert(cond, __VA_ARGS__); \
377
}
378
379
void RootChunkArea::verify() const {
380
assert_lock_strong(Metaspace_lock);
381
assert_is_aligned(_base, chunklevel::MAX_CHUNK_BYTE_SIZE);
382
383
// Iterate thru all chunks in this area. They must be ordered correctly,
384
// being adjacent to each other, and cover the complete area
385
int num_chunk = 0;
386
387
if (_first_chunk != NULL) {
388
assrt_(_first_chunk->prev_in_vs() == NULL, "Sanity");
389
390
const Metachunk* c = _first_chunk;
391
const MetaWord* expected_next_base = _base;
392
const MetaWord* const area_end = _base + word_size();
393
394
while (c != NULL) {
395
assrt_(c->is_free() || c->is_in_use(),
396
"Chunk No. %d " METACHUNK_FORMAT " - invalid state.",
397
num_chunk, METACHUNK_FORMAT_ARGS(c));
398
assrt_(c->base() == expected_next_base,
399
"Chunk No. %d " METACHUNK_FORMAT " - unexpected base.",
400
num_chunk, METACHUNK_FORMAT_ARGS(c));
401
assrt_(c->base() >= base() && c->end() <= end(),
402
"chunk %d " METACHUNK_FORMAT " oob for this root area [" PTR_FORMAT ".." PTR_FORMAT ").",
403
num_chunk, METACHUNK_FORMAT_ARGS(c), p2i(base()), p2i(end()));
404
assrt_(is_aligned(c->base(), c->word_size()),
405
"misaligned chunk %d " METACHUNK_FORMAT ".", num_chunk, METACHUNK_FORMAT_ARGS(c));
406
407
c->verify_neighborhood();
408
c->verify();
409
expected_next_base = c->end();
410
num_chunk++;
411
c = c->next_in_vs();
412
}
413
assrt_(expected_next_base == _base + word_size(), "Sanity");
414
}
415
}
416
417
void RootChunkArea::verify_area_is_ideally_merged() const {
418
SOMETIMES(assert_lock_strong(Metaspace_lock);)
419
int num_chunk = 0;
420
for (const Metachunk* c = _first_chunk; c != NULL; c = c->next_in_vs()) {
421
if (!c->is_root_chunk() && c->is_free()) {
422
// If a chunk is free, it must not have a buddy which is also free, because
423
// those chunks should have been merged.
424
// In other words, a buddy shall be either in-use or splintered
425
// (which in turn would mean part of it are in use).
426
Metachunk* const buddy = c->is_leader() ? c->next_in_vs() : c->prev_in_vs();
427
assrt_(buddy->is_in_use() || buddy->level() > c->level(),
428
"Chunk No. %d " METACHUNK_FORMAT " : missed merge opportunity with neighbor " METACHUNK_FORMAT ".",
429
num_chunk, METACHUNK_FORMAT_ARGS(c), METACHUNK_FORMAT_ARGS(buddy));
430
}
431
num_chunk++;
432
}
433
}
434
435
#endif
436
437
void RootChunkArea::print_on(outputStream* st) const {
438
st->print(PTR_FORMAT ": ", p2i(base()));
439
if (_first_chunk != NULL) {
440
const Metachunk* c = _first_chunk;
441
// 01234567890123
442
const char* letters_for_levels_cap = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
443
const char* letters_for_levels = "abcdefghijklmnopqrstuvwxyz";
444
while (c != NULL) {
445
const chunklevel_t l = c->level();
446
if (l >= 0 && (size_t)l < strlen(letters_for_levels)) {
447
st->print("%c", c->is_free() ? letters_for_levels[c->level()] : letters_for_levels_cap[c->level()]);
448
} else {
449
// Obviously garbage, but lets not crash.
450
st->print("?");
451
}
452
c = c->next_in_vs();
453
}
454
} else {
455
st->print(" (no chunks)");
456
}
457
st->cr();
458
}
459
460
// Create an array of ChunkTree objects, all initialized to NULL, covering
461
// a given memory range. Memory range must be a multiple of root chunk size.
462
RootChunkAreaLUT::RootChunkAreaLUT(const MetaWord* base, size_t word_size) :
463
_base(base),
464
_num((int)(word_size / chunklevel::MAX_CHUNK_WORD_SIZE)),
465
_arr(NULL)
466
{
467
assert_is_aligned(word_size, chunklevel::MAX_CHUNK_WORD_SIZE);
468
_arr = NEW_C_HEAP_ARRAY(RootChunkArea, _num, mtClass);
469
const MetaWord* this_base = _base;
470
for (int i = 0; i < _num; i++) {
471
RootChunkArea* rca = new(_arr + i) RootChunkArea(this_base);
472
assert(rca == _arr + i, "Sanity");
473
this_base += chunklevel::MAX_CHUNK_WORD_SIZE;
474
}
475
}
476
477
RootChunkAreaLUT::~RootChunkAreaLUT() {
478
for (int i = 0; i < _num; i++) {
479
_arr[i].~RootChunkArea();
480
}
481
FREE_C_HEAP_ARRAY(RootChunkArea, _arr);
482
}
483
484
// Returns true if all areas in this area table are free (only contain free chunks).
485
bool RootChunkAreaLUT::is_free() const {
486
for (int i = 0; i < _num; i++) {
487
if (!_arr[i].is_free()) {
488
return false;
489
}
490
}
491
return true;
492
}
493
494
#ifdef ASSERT
495
496
void RootChunkAreaLUT::verify() const {
497
for (int i = 0; i < _num; i++) {
498
check_pointer(_arr[i].base());
499
_arr[i].verify();
500
}
501
}
502
503
#endif
504
505
void RootChunkAreaLUT::print_on(outputStream* st) const {
506
for (int i = 0; i < _num; i++) {
507
st->print("%2d:", i);
508
_arr[i].print_on(st);
509
}
510
}
511
512
} // end: namespace metaspace
513
514