Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/hotspot/share/memory/metaspace/metachunk.cpp
40957 views
1
/*
2
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
3
* Copyright (c) 2017, 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/metaspace/metachunk.hpp"
29
#include "memory/metaspace/metaspaceCommon.hpp"
30
#include "memory/metaspace/metaspaceSettings.hpp"
31
#include "memory/metaspace/virtualSpaceNode.hpp"
32
#include "runtime/mutexLocker.hpp"
33
#include "utilities/align.hpp"
34
#include "utilities/copy.hpp"
35
#include "utilities/debug.hpp"
36
37
namespace metaspace {
38
39
// Return a single char presentation of the state ('f', 'u', 'd')
40
char Metachunk::get_state_char() const {
41
switch (_state) {
42
case State::Free: return 'f';
43
case State::InUse: return 'u';
44
case State::Dead: return 'd';
45
}
46
return '?';
47
}
48
49
#ifdef ASSERT
50
void Metachunk::assert_have_expand_lock() {
51
assert_lock_strong(Metaspace_lock);
52
}
53
#endif
54
55
// Commit uncommitted section of the chunk.
56
// Fails if we hit a commit limit.
57
bool Metachunk::commit_up_to(size_t new_committed_words) {
58
// Please note:
59
//
60
// VirtualSpaceNode::ensure_range_is_committed(), when called over a range containing both committed and uncommitted parts,
61
// will replace the whole range with a new mapping, thus erasing the existing content in the committed parts. Therefore
62
// we must make sure never to call VirtualSpaceNode::ensure_range_is_committed() over a range containing live data.
63
//
64
// Luckily, this cannot happen by design. We have two cases:
65
//
66
// 1) chunks equal or larger than a commit granule.
67
// In this case, due to chunk geometry, the chunk should cover whole commit granules (in other words, a chunk equal or larger than
68
// a commit granule will never share a granule with a neighbor). That means whatever we commit or uncommit here does not affect
69
// neighboring chunks. We only have to take care not to re-commit used parts of ourself. We do this by moving the committed_words
70
// limit in multiple of commit granules.
71
//
72
// 2) chunks smaller than a commit granule.
73
// In this case, a chunk shares a single commit granule with its neighbors. But this never can be a problem:
74
// - Either the commit granule is already committed (and maybe the neighbors contain live data). In that case calling
75
// ensure_range_is_committed() will do nothing.
76
// - Or the commit granule is not committed, but in this case, the neighbors are uncommitted too and cannot contain live data.
77
78
#ifdef ASSERT
79
if (word_size() >= Settings::commit_granule_words()) {
80
// case (1)
81
assert(is_aligned(base(), Settings::commit_granule_bytes()) &&
82
is_aligned(end(), Settings::commit_granule_bytes()),
83
"Chunks larger than a commit granule must cover whole granules.");
84
assert(is_aligned(_committed_words, Settings::commit_granule_words()),
85
"The commit boundary must be aligned to commit granule size");
86
assert(_used_words <= _committed_words, "Sanity");
87
} else {
88
// case (2)
89
assert(_committed_words == 0 || _committed_words == word_size(), "Sanity");
90
}
91
#endif
92
93
// We should hold the expand lock at this point.
94
assert_lock_strong(Metaspace_lock);
95
96
const size_t commit_from = _committed_words;
97
const size_t commit_to = MIN2(align_up(new_committed_words, Settings::commit_granule_words()), word_size());
98
assert(commit_from >= used_words(), "Sanity");
99
assert(commit_to <= word_size(), "Sanity");
100
if (commit_to > commit_from) {
101
log_debug(metaspace)("Chunk " METACHUNK_FORMAT ": attempting to move commit line to "
102
SIZE_FORMAT " words.", METACHUNK_FORMAT_ARGS(this), commit_to);
103
if (!_vsnode->ensure_range_is_committed(base() + commit_from, commit_to - commit_from)) {
104
DEBUG_ONLY(verify();)
105
return false;
106
}
107
}
108
109
// Remember how far we have committed.
110
_committed_words = commit_to;
111
DEBUG_ONLY(verify();)
112
return true;
113
}
114
115
// Ensure that chunk is committed up to at least new_committed_words words.
116
// Fails if we hit a commit limit.
117
bool Metachunk::ensure_committed(size_t new_committed_words) {
118
bool rc = true;
119
if (new_committed_words > committed_words()) {
120
MutexLocker cl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
121
rc = commit_up_to(new_committed_words);
122
}
123
return rc;
124
}
125
126
bool Metachunk::ensure_committed_locked(size_t new_committed_words) {
127
// the .._locked() variant should be called if we own the lock already.
128
assert_lock_strong(Metaspace_lock);
129
bool rc = true;
130
if (new_committed_words > committed_words()) {
131
rc = commit_up_to(new_committed_words);
132
}
133
return rc;
134
}
135
136
// Uncommit chunk area. The area must be a common multiple of the
137
// commit granule size (in other words, we cannot uncommit chunks smaller than
138
// a commit granule size).
139
void Metachunk::uncommit() {
140
MutexLocker cl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
141
uncommit_locked();
142
}
143
144
void Metachunk::uncommit_locked() {
145
// Only uncommit chunks which are free, have no used words set (extra precaution) and are equal or larger in size than a single commit granule.
146
assert_lock_strong(Metaspace_lock);
147
assert(_state == State::Free && _used_words == 0 && word_size() >= Settings::commit_granule_words(),
148
"Only free chunks equal or larger than commit granule size can be uncommitted "
149
"(chunk " METACHUNK_FULL_FORMAT ").", METACHUNK_FULL_FORMAT_ARGS(this));
150
if (word_size() >= Settings::commit_granule_words()) {
151
_vsnode->uncommit_range(base(), word_size());
152
_committed_words = 0;
153
}
154
}
155
void Metachunk::set_committed_words(size_t v) {
156
// Set committed words. Since we know that we only commit whole commit granules, we can round up v here.
157
v = MIN2(align_up(v, Settings::commit_granule_words()), word_size());
158
_committed_words = v;
159
}
160
161
// Allocate word_size words from this chunk (word_size must be aligned to
162
// allocation_alignment_words).
163
//
164
// Caller must make sure the chunk is both large enough and committed far enough
165
// to hold the allocation. Will always work.
166
//
167
MetaWord* Metachunk::allocate(size_t request_word_size) {
168
// Caller must have made sure this works
169
assert(free_words() >= request_word_size, "Chunk too small.");
170
assert(free_below_committed_words() >= request_word_size, "Chunk not committed.");
171
MetaWord* const p = top();
172
_used_words += request_word_size;
173
SOMETIMES(verify();)
174
return p;
175
}
176
177
#ifdef ASSERT
178
179
// Zap this structure.
180
void Metachunk::zap_header(uint8_t c) {
181
memset(this, c, sizeof(Metachunk));
182
}
183
184
// Verifies linking with neighbors in virtual space.
185
// Can only be done under expand lock protection.
186
void Metachunk::verify_neighborhood() const {
187
assert_lock_strong(Metaspace_lock);
188
assert(!is_dead(), "Do not call on dead chunks.");
189
if (is_root_chunk()) {
190
// Root chunks are all alone in the world.
191
assert(next_in_vs() == NULL || prev_in_vs() == NULL, "Root chunks should have no neighbors");
192
} else {
193
// Non-root chunks have neighbors, at least one, possibly two.
194
assert(next_in_vs() != NULL || prev_in_vs() != NULL,
195
"A non-root chunk should have neighbors (chunk @" PTR_FORMAT
196
", base " PTR_FORMAT ", level " CHKLVL_FORMAT ".",
197
p2i(this), p2i(base()), level());
198
if (prev_in_vs() != NULL) {
199
assert(prev_in_vs()->end() == base(),
200
"Chunk " METACHUNK_FULL_FORMAT ": should be adjacent to predecessor: " METACHUNK_FULL_FORMAT ".",
201
METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(prev_in_vs()));
202
assert(prev_in_vs()->next_in_vs() == this,
203
"Chunk " METACHUNK_FULL_FORMAT ": broken link to left neighbor: " METACHUNK_FULL_FORMAT " (" PTR_FORMAT ").",
204
METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(prev_in_vs()), p2i(prev_in_vs()->next_in_vs()));
205
}
206
if (next_in_vs() != NULL) {
207
assert(end() == next_in_vs()->base(),
208
"Chunk " METACHUNK_FULL_FORMAT ": should be adjacent to successor: " METACHUNK_FULL_FORMAT ".",
209
METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(next_in_vs()));
210
assert(next_in_vs()->prev_in_vs() == this,
211
"Chunk " METACHUNK_FULL_FORMAT ": broken link to right neighbor: " METACHUNK_FULL_FORMAT " (" PTR_FORMAT ").",
212
METACHUNK_FULL_FORMAT_ARGS(this), METACHUNK_FULL_FORMAT_ARGS(next_in_vs()), p2i(next_in_vs()->prev_in_vs()));
213
}
214
215
// One of the neighbors must be the buddy. It can be whole or splintered.
216
217
// The chunk following us or preceeding us may be our buddy or a splintered part of it.
218
Metachunk* buddy = is_leader() ? next_in_vs() : prev_in_vs();
219
assert(buddy != NULL, "Missing neighbor.");
220
assert(!buddy->is_dead(), "Invalid buddy state.");
221
222
// This neighbor is either or buddy (same level) or a splinter of our buddy - hence
223
// the level can never be smaller (aka the chunk size cannot be larger).
224
assert(buddy->level() >= level(), "Wrong level.");
225
226
if (buddy->level() == level()) {
227
// If the buddy is of the same size as us, it is unsplintered.
228
assert(buddy->is_leader() == !is_leader(),
229
"Only one chunk can be leader in a pair");
230
231
// When direct buddies are neighbors, one or both should be in use, otherwise they should
232
// have been merged.
233
// But since we call this verification function from internal functions where we are about to merge or just did split,
234
// do not test this. We have RootChunkArea::verify_area_is_ideally_merged() for testing that.
235
if (is_leader()) {
236
assert(buddy->base() == end(), "Sanity");
237
assert(is_aligned(base(), word_size() * 2 * BytesPerWord), "Sanity");
238
} else {
239
assert(buddy->end() == base(), "Sanity");
240
assert(is_aligned(buddy->base(), word_size() * 2 * BytesPerWord), "Sanity");
241
}
242
} else {
243
// Buddy, but splintered, and this is a part of it.
244
if (is_leader()) {
245
assert(buddy->base() == end(), "Sanity");
246
} else {
247
assert(buddy->end() > (base() - word_size()), "Sanity");
248
}
249
}
250
}
251
}
252
253
volatile MetaWord dummy = 0;
254
255
void Metachunk::verify() const {
256
// Note. This should be called under CLD lock protection.
257
258
// We can verify everything except the _prev_in_vs/_next_in_vs pair.
259
// This is because neighbor chunks may be added concurrently, so we cannot rely
260
// on the content of _next_in_vs/_prev_in_vs unless we have the expand lock.
261
assert(!is_dead(), "Do not call on dead chunks.");
262
if (is_free()) {
263
assert(used_words() == 0, "free chunks are not used.");
264
}
265
266
// Note: only call this on a life Metachunk.
267
chunklevel::check_valid_level(level());
268
269
assert(base() != NULL, "No base ptr");
270
assert(committed_words() >= used_words(),
271
"mismatch: committed: " SIZE_FORMAT ", used: " SIZE_FORMAT ".",
272
committed_words(), used_words());
273
assert(word_size() >= committed_words(),
274
"mismatch: word_size: " SIZE_FORMAT ", committed: " SIZE_FORMAT ".",
275
word_size(), committed_words());
276
277
// Test base pointer
278
assert(base() != NULL, "Base pointer NULL");
279
assert(vsnode() != NULL, "No space");
280
vsnode()->check_pointer(base());
281
282
// Starting address shall be aligned to chunk size.
283
const size_t required_alignment = word_size() * sizeof(MetaWord);
284
assert_is_aligned(base(), required_alignment);
285
286
// Test accessing the committed area.
287
SOMETIMES(
288
if (_committed_words > 0) {
289
for (const MetaWord* p = _base; p < _base + _committed_words; p += os::vm_page_size()) {
290
dummy = *p;
291
}
292
dummy = *(_base + _committed_words - 1);
293
}
294
)
295
}
296
#endif // ASSERT
297
298
void Metachunk::print_on(outputStream* st) const {
299
// Note: must also work with invalid/random data. (e.g. do not call word_size())
300
st->print("Chunk @" PTR_FORMAT ", state %c, base " PTR_FORMAT ", "
301
"level " CHKLVL_FORMAT " (" SIZE_FORMAT " words), "
302
"used " SIZE_FORMAT " words, committed " SIZE_FORMAT " words.",
303
p2i(this), get_state_char(), p2i(base()), level(),
304
(chunklevel::is_valid_level(level()) ? chunklevel::word_size_for_level(level()) : (size_t)-1),
305
used_words(), committed_words());
306
}
307
308
} // namespace metaspace
309
310
311