author | stuefe |
Sun, 10 Feb 2019 09:10:42 +0100 | |
changeset 53970 | 1ad7c590a6e7 |
parent 50811 | f533eb5e7430 |
child 54623 | 1126f0607c70 |
permissions | -rw-r--r-- |
50193 | 1 |
/* |
2 |
* Copyright (c) 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 |
#include "precompiled.hpp" |
|
25 |
||
26 |
#include "logging/log.hpp" |
|
27 |
#include "logging/logStream.hpp" |
|
28 |
#include "memory/binaryTreeDictionary.inline.hpp" |
|
29 |
#include "memory/freeList.inline.hpp" |
|
30 |
#include "memory/metaspace/chunkManager.hpp" |
|
31 |
#include "memory/metaspace/metachunk.hpp" |
|
53970 | 32 |
#include "memory/metaspace/metaDebug.hpp" |
50193 | 33 |
#include "memory/metaspace/metaspaceCommon.hpp" |
34 |
#include "memory/metaspace/metaspaceStatistics.hpp" |
|
35 |
#include "memory/metaspace/occupancyMap.hpp" |
|
36 |
#include "memory/metaspace/virtualSpaceNode.hpp" |
|
37 |
#include "runtime/mutexLocker.hpp" |
|
38 |
#include "utilities/debug.hpp" |
|
39 |
#include "utilities/globalDefinitions.hpp" |
|
40 |
#include "utilities/ostream.hpp" |
|
41 |
||
42 |
namespace metaspace { |
|
43 |
||
50380
bec342339138
8204195: Clean up macroAssembler.inline.hpp and other inline.hpp files included in .hpp files
coleenp
parents:
50193
diff
changeset
|
44 |
ChunkManager::ChunkManager(bool is_class) |
bec342339138
8204195: Clean up macroAssembler.inline.hpp and other inline.hpp files included in .hpp files
coleenp
parents:
50193
diff
changeset
|
45 |
: _is_class(is_class), _free_chunks_total(0), _free_chunks_count(0) { |
bec342339138
8204195: Clean up macroAssembler.inline.hpp and other inline.hpp files included in .hpp files
coleenp
parents:
50193
diff
changeset
|
46 |
_free_chunks[SpecializedIndex].set_size(get_size_for_nonhumongous_chunktype(SpecializedIndex, is_class)); |
bec342339138
8204195: Clean up macroAssembler.inline.hpp and other inline.hpp files included in .hpp files
coleenp
parents:
50193
diff
changeset
|
47 |
_free_chunks[SmallIndex].set_size(get_size_for_nonhumongous_chunktype(SmallIndex, is_class)); |
bec342339138
8204195: Clean up macroAssembler.inline.hpp and other inline.hpp files included in .hpp files
coleenp
parents:
50193
diff
changeset
|
48 |
_free_chunks[MediumIndex].set_size(get_size_for_nonhumongous_chunktype(MediumIndex, is_class)); |
bec342339138
8204195: Clean up macroAssembler.inline.hpp and other inline.hpp files included in .hpp files
coleenp
parents:
50193
diff
changeset
|
49 |
} |
bec342339138
8204195: Clean up macroAssembler.inline.hpp and other inline.hpp files included in .hpp files
coleenp
parents:
50193
diff
changeset
|
50 |
|
50193 | 51 |
void ChunkManager::remove_chunk(Metachunk* chunk) { |
52 |
size_t word_size = chunk->word_size(); |
|
53 |
ChunkIndex index = list_index(word_size); |
|
54 |
if (index != HumongousIndex) { |
|
55 |
free_chunks(index)->remove_chunk(chunk); |
|
56 |
} else { |
|
57 |
humongous_dictionary()->remove_chunk(chunk); |
|
58 |
} |
|
59 |
||
60 |
// Chunk has been removed from the chunks free list, update counters. |
|
61 |
account_for_removed_chunk(chunk); |
|
62 |
} |
|
63 |
||
64 |
bool ChunkManager::attempt_to_coalesce_around_chunk(Metachunk* chunk, ChunkIndex target_chunk_type) { |
|
65 |
assert_lock_strong(MetaspaceExpand_lock); |
|
66 |
assert(chunk != NULL, "invalid chunk pointer"); |
|
67 |
// Check for valid merge combinations. |
|
68 |
assert((chunk->get_chunk_type() == SpecializedIndex && |
|
69 |
(target_chunk_type == SmallIndex || target_chunk_type == MediumIndex)) || |
|
70 |
(chunk->get_chunk_type() == SmallIndex && target_chunk_type == MediumIndex), |
|
71 |
"Invalid chunk merge combination."); |
|
72 |
||
73 |
const size_t target_chunk_word_size = |
|
74 |
get_size_for_nonhumongous_chunktype(target_chunk_type, this->is_class()); |
|
75 |
||
76 |
// [ prospective merge region ) |
|
77 |
MetaWord* const p_merge_region_start = |
|
78 |
(MetaWord*) align_down(chunk, target_chunk_word_size * sizeof(MetaWord)); |
|
79 |
MetaWord* const p_merge_region_end = |
|
80 |
p_merge_region_start + target_chunk_word_size; |
|
81 |
||
82 |
// We need the VirtualSpaceNode containing this chunk and its occupancy map. |
|
83 |
VirtualSpaceNode* const vsn = chunk->container(); |
|
84 |
OccupancyMap* const ocmap = vsn->occupancy_map(); |
|
85 |
||
86 |
// The prospective chunk merge range must be completely contained by the |
|
87 |
// committed range of the virtual space node. |
|
88 |
if (p_merge_region_start < vsn->bottom() || p_merge_region_end > vsn->top()) { |
|
89 |
return false; |
|
90 |
} |
|
91 |
||
92 |
// Only attempt to merge this range if at its start a chunk starts and at its end |
|
93 |
// a chunk ends. If a chunk (can only be humongous) straddles either start or end |
|
94 |
// of that range, we cannot merge. |
|
95 |
if (!ocmap->chunk_starts_at_address(p_merge_region_start)) { |
|
96 |
return false; |
|
97 |
} |
|
98 |
if (p_merge_region_end < vsn->top() && |
|
99 |
!ocmap->chunk_starts_at_address(p_merge_region_end)) { |
|
100 |
return false; |
|
101 |
} |
|
102 |
||
103 |
// Now check if the prospective merge area contains live chunks. If it does we cannot merge. |
|
104 |
if (ocmap->is_region_in_use(p_merge_region_start, target_chunk_word_size)) { |
|
105 |
return false; |
|
106 |
} |
|
107 |
||
108 |
// Success! Remove all chunks in this region... |
|
109 |
log_trace(gc, metaspace, freelist)("%s: coalescing chunks in area [%p-%p)...", |
|
110 |
(is_class() ? "class space" : "metaspace"), |
|
111 |
p_merge_region_start, p_merge_region_end); |
|
112 |
||
113 |
const int num_chunks_removed = |
|
114 |
remove_chunks_in_area(p_merge_region_start, target_chunk_word_size); |
|
115 |
||
116 |
// ... and create a single new bigger chunk. |
|
117 |
Metachunk* const p_new_chunk = |
|
118 |
::new (p_merge_region_start) Metachunk(target_chunk_type, is_class(), target_chunk_word_size, vsn); |
|
119 |
assert(p_new_chunk == (Metachunk*)p_merge_region_start, "Sanity"); |
|
120 |
p_new_chunk->set_origin(origin_merge); |
|
121 |
||
122 |
log_trace(gc, metaspace, freelist)("%s: created coalesced chunk at %p, size " SIZE_FORMAT_HEX ".", |
|
123 |
(is_class() ? "class space" : "metaspace"), |
|
124 |
p_new_chunk, p_new_chunk->word_size() * sizeof(MetaWord)); |
|
125 |
||
126 |
// Fix occupancy map: remove old start bits of the small chunks and set new start bit. |
|
127 |
ocmap->wipe_chunk_start_bits_in_region(p_merge_region_start, target_chunk_word_size); |
|
128 |
ocmap->set_chunk_starts_at_address(p_merge_region_start, true); |
|
129 |
||
130 |
// Mark chunk as free. Note: it is not necessary to update the occupancy |
|
131 |
// map in-use map, because the old chunks were also free, so nothing |
|
132 |
// should have changed. |
|
133 |
p_new_chunk->set_is_tagged_free(true); |
|
134 |
||
135 |
// Add new chunk to its freelist. |
|
136 |
ChunkList* const list = free_chunks(target_chunk_type); |
|
137 |
list->return_chunk_at_head(p_new_chunk); |
|
138 |
||
139 |
// And adjust ChunkManager:: _free_chunks_count (_free_chunks_total |
|
140 |
// should not have changed, because the size of the space should be the same) |
|
141 |
_free_chunks_count -= num_chunks_removed; |
|
142 |
_free_chunks_count ++; |
|
143 |
||
53970 | 144 |
// VirtualSpaceNode::chunk_count does not have to be modified: |
50193 | 145 |
// it means "number of active (non-free) chunks", so merging free chunks |
146 |
// should not affect that count. |
|
147 |
||
148 |
// At the end of a chunk merge, run verification tests. |
|
53970 | 149 |
#ifdef ASSERT |
150 |
||
151 |
EVERY_NTH(VerifyMetaspaceInterval) |
|
152 |
locked_verify(true); |
|
153 |
vsn->verify(true); |
|
154 |
END_EVERY_NTH |
|
155 |
||
156 |
g_internal_statistics.num_chunk_merges ++; |
|
157 |
||
158 |
#endif |
|
50193 | 159 |
|
160 |
return true; |
|
161 |
} |
|
162 |
||
163 |
// Remove all chunks in the given area - the chunks are supposed to be free - |
|
164 |
// from their corresponding freelists. Mark them as invalid. |
|
165 |
// - This does not correct the occupancy map. |
|
166 |
// - This does not adjust the counters in ChunkManager. |
|
167 |
// - Does not adjust container count counter in containing VirtualSpaceNode |
|
168 |
// Returns number of chunks removed. |
|
169 |
int ChunkManager::remove_chunks_in_area(MetaWord* p, size_t word_size) { |
|
170 |
assert(p != NULL && word_size > 0, "Invalid range."); |
|
171 |
const size_t smallest_chunk_size = get_size_for_nonhumongous_chunktype(SpecializedIndex, is_class()); |
|
172 |
assert_is_aligned(word_size, smallest_chunk_size); |
|
173 |
||
174 |
Metachunk* const start = (Metachunk*) p; |
|
175 |
const Metachunk* const end = (Metachunk*)(p + word_size); |
|
176 |
Metachunk* cur = start; |
|
177 |
int num_removed = 0; |
|
178 |
while (cur < end) { |
|
179 |
Metachunk* next = (Metachunk*)(((MetaWord*)cur) + cur->word_size()); |
|
180 |
DEBUG_ONLY(do_verify_chunk(cur)); |
|
181 |
assert(cur->get_chunk_type() != HumongousIndex, "Unexpected humongous chunk found at %p.", cur); |
|
182 |
assert(cur->is_tagged_free(), "Chunk expected to be free (%p)", cur); |
|
183 |
log_trace(gc, metaspace, freelist)("%s: removing chunk %p, size " SIZE_FORMAT_HEX ".", |
|
184 |
(is_class() ? "class space" : "metaspace"), |
|
185 |
cur, cur->word_size() * sizeof(MetaWord)); |
|
186 |
cur->remove_sentinel(); |
|
187 |
// Note: cannot call ChunkManager::remove_chunk, because that |
|
188 |
// modifies the counters in ChunkManager, which we do not want. So |
|
189 |
// we call remove_chunk on the freelist directly (see also the |
|
190 |
// splitting function which does the same). |
|
191 |
ChunkList* const list = free_chunks(list_index(cur->word_size())); |
|
192 |
list->remove_chunk(cur); |
|
193 |
num_removed ++; |
|
194 |
cur = next; |
|
195 |
} |
|
196 |
return num_removed; |
|
197 |
} |
|
198 |
||
199 |
// Update internal accounting after a chunk was added |
|
200 |
void ChunkManager::account_for_added_chunk(const Metachunk* c) { |
|
201 |
assert_lock_strong(MetaspaceExpand_lock); |
|
202 |
_free_chunks_count ++; |
|
203 |
_free_chunks_total += c->word_size(); |
|
204 |
} |
|
205 |
||
206 |
// Update internal accounting after a chunk was removed |
|
207 |
void ChunkManager::account_for_removed_chunk(const Metachunk* c) { |
|
208 |
assert_lock_strong(MetaspaceExpand_lock); |
|
209 |
assert(_free_chunks_count >= 1, |
|
210 |
"ChunkManager::_free_chunks_count: about to go negative (" SIZE_FORMAT ").", _free_chunks_count); |
|
211 |
assert(_free_chunks_total >= c->word_size(), |
|
212 |
"ChunkManager::_free_chunks_total: about to go negative" |
|
213 |
"(now: " SIZE_FORMAT ", decrement value: " SIZE_FORMAT ").", _free_chunks_total, c->word_size()); |
|
214 |
_free_chunks_count --; |
|
215 |
_free_chunks_total -= c->word_size(); |
|
216 |
} |
|
217 |
||
218 |
ChunkIndex ChunkManager::list_index(size_t size) { |
|
219 |
return get_chunk_type_by_size(size, is_class()); |
|
220 |
} |
|
221 |
||
222 |
size_t ChunkManager::size_by_index(ChunkIndex index) const { |
|
223 |
index_bounds_check(index); |
|
224 |
assert(index != HumongousIndex, "Do not call for humongous chunks."); |
|
225 |
return get_size_for_nonhumongous_chunktype(index, is_class()); |
|
226 |
} |
|
227 |
||
53970 | 228 |
#ifdef ASSERT |
229 |
void ChunkManager::verify(bool slow) const { |
|
230 |
MutexLockerEx cl(MetaspaceExpand_lock, |
|
231 |
Mutex::_no_safepoint_check_flag); |
|
232 |
locked_verify(slow); |
|
50193 | 233 |
} |
234 |
||
53970 | 235 |
void ChunkManager::locked_verify(bool slow) const { |
236 |
log_trace(gc, metaspace, freelist)("verifying %s chunkmanager (%s).", |
|
237 |
(is_class() ? "class space" : "metaspace"), (slow ? "slow" : "quick")); |
|
50193 | 238 |
|
53970 | 239 |
assert_lock_strong(MetaspaceExpand_lock); |
50193 | 240 |
|
53970 | 241 |
size_t chunks_counted = 0; |
242 |
size_t wordsize_chunks_counted = 0; |
|
50193 | 243 |
for (ChunkIndex i = ZeroIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { |
53970 | 244 |
const ChunkList* list = _free_chunks + i; |
50193 | 245 |
if (list != NULL) { |
246 |
Metachunk* chunk = list->head(); |
|
247 |
while (chunk) { |
|
53970 | 248 |
if (slow) { |
249 |
do_verify_chunk(chunk); |
|
250 |
} |
|
50193 | 251 |
assert(chunk->is_tagged_free(), "Chunk should be tagged as free."); |
53970 | 252 |
chunks_counted ++; |
253 |
wordsize_chunks_counted += chunk->size(); |
|
50193 | 254 |
chunk = chunk->next(); |
255 |
} |
|
256 |
} |
|
257 |
} |
|
53970 | 258 |
|
259 |
chunks_counted += humongous_dictionary()->total_free_blocks(); |
|
260 |
wordsize_chunks_counted += humongous_dictionary()->total_size(); |
|
261 |
||
262 |
assert(chunks_counted == _free_chunks_count && wordsize_chunks_counted == _free_chunks_total, |
|
263 |
"freelist accounting mismatch: " |
|
264 |
"we think: " SIZE_FORMAT " chunks, total " SIZE_FORMAT " words, " |
|
265 |
"reality: " SIZE_FORMAT " chunks, total " SIZE_FORMAT " words.", |
|
266 |
_free_chunks_count, _free_chunks_total, |
|
267 |
chunks_counted, wordsize_chunks_counted); |
|
50193 | 268 |
} |
53970 | 269 |
#endif // ASSERT |
50193 | 270 |
|
271 |
void ChunkManager::locked_print_free_chunks(outputStream* st) { |
|
272 |
assert_lock_strong(MetaspaceExpand_lock); |
|
273 |
st->print_cr("Free chunk total " SIZE_FORMAT " count " SIZE_FORMAT, |
|
274 |
_free_chunks_total, _free_chunks_count); |
|
275 |
} |
|
276 |
||
277 |
ChunkList* ChunkManager::free_chunks(ChunkIndex index) { |
|
278 |
assert(index == SpecializedIndex || index == SmallIndex || index == MediumIndex, |
|
279 |
"Bad index: %d", (int)index); |
|
280 |
return &_free_chunks[index]; |
|
281 |
} |
|
282 |
||
283 |
ChunkList* ChunkManager::find_free_chunks_list(size_t word_size) { |
|
284 |
ChunkIndex index = list_index(word_size); |
|
285 |
assert(index < HumongousIndex, "No humongous list"); |
|
286 |
return free_chunks(index); |
|
287 |
} |
|
288 |
||
289 |
// Helper for chunk splitting: given a target chunk size and a larger free chunk, |
|
290 |
// split up the larger chunk into n smaller chunks, at least one of which should be |
|
291 |
// the target chunk of target chunk size. The smaller chunks, including the target |
|
292 |
// chunk, are returned to the freelist. The pointer to the target chunk is returned. |
|
293 |
// Note that this chunk is supposed to be removed from the freelist right away. |
|
294 |
Metachunk* ChunkManager::split_chunk(size_t target_chunk_word_size, Metachunk* larger_chunk) { |
|
295 |
assert(larger_chunk->word_size() > target_chunk_word_size, "Sanity"); |
|
296 |
||
297 |
const ChunkIndex larger_chunk_index = larger_chunk->get_chunk_type(); |
|
298 |
const ChunkIndex target_chunk_index = get_chunk_type_by_size(target_chunk_word_size, is_class()); |
|
299 |
||
300 |
MetaWord* const region_start = (MetaWord*)larger_chunk; |
|
301 |
const size_t region_word_len = larger_chunk->word_size(); |
|
302 |
MetaWord* const region_end = region_start + region_word_len; |
|
303 |
VirtualSpaceNode* const vsn = larger_chunk->container(); |
|
304 |
OccupancyMap* const ocmap = vsn->occupancy_map(); |
|
305 |
||
306 |
// Any larger non-humongous chunk size is a multiple of any smaller chunk size. |
|
307 |
// Since non-humongous chunks are aligned to their chunk size, the larger chunk should start |
|
308 |
// at an address suitable to place the smaller target chunk. |
|
309 |
assert_is_aligned(region_start, target_chunk_word_size); |
|
310 |
||
311 |
// Remove old chunk. |
|
312 |
free_chunks(larger_chunk_index)->remove_chunk(larger_chunk); |
|
313 |
larger_chunk->remove_sentinel(); |
|
314 |
||
315 |
// Prevent access to the old chunk from here on. |
|
316 |
larger_chunk = NULL; |
|
317 |
// ... and wipe it. |
|
318 |
DEBUG_ONLY(memset(region_start, 0xfe, region_word_len * BytesPerWord)); |
|
319 |
||
320 |
// In its place create first the target chunk... |
|
321 |
MetaWord* p = region_start; |
|
322 |
Metachunk* target_chunk = ::new (p) Metachunk(target_chunk_index, is_class(), target_chunk_word_size, vsn); |
|
323 |
assert(target_chunk == (Metachunk*)p, "Sanity"); |
|
324 |
target_chunk->set_origin(origin_split); |
|
325 |
||
326 |
// Note: we do not need to mark its start in the occupancy map |
|
327 |
// because it coincides with the old chunk start. |
|
328 |
||
329 |
// Mark chunk as free and return to the freelist. |
|
330 |
do_update_in_use_info_for_chunk(target_chunk, false); |
|
331 |
free_chunks(target_chunk_index)->return_chunk_at_head(target_chunk); |
|
332 |
||
333 |
// This chunk should now be valid and can be verified. |
|
334 |
DEBUG_ONLY(do_verify_chunk(target_chunk)); |
|
335 |
||
336 |
// In the remaining space create the remainder chunks. |
|
337 |
p += target_chunk->word_size(); |
|
338 |
assert(p < region_end, "Sanity"); |
|
339 |
||
340 |
while (p < region_end) { |
|
341 |
||
342 |
// Find the largest chunk size which fits the alignment requirements at address p. |
|
343 |
ChunkIndex this_chunk_index = prev_chunk_index(larger_chunk_index); |
|
344 |
size_t this_chunk_word_size = 0; |
|
345 |
for(;;) { |
|
346 |
this_chunk_word_size = get_size_for_nonhumongous_chunktype(this_chunk_index, is_class()); |
|
347 |
if (is_aligned(p, this_chunk_word_size * BytesPerWord)) { |
|
348 |
break; |
|
349 |
} else { |
|
350 |
this_chunk_index = prev_chunk_index(this_chunk_index); |
|
351 |
assert(this_chunk_index >= target_chunk_index, "Sanity"); |
|
352 |
} |
|
353 |
} |
|
354 |
||
355 |
assert(this_chunk_word_size >= target_chunk_word_size, "Sanity"); |
|
356 |
assert(is_aligned(p, this_chunk_word_size * BytesPerWord), "Sanity"); |
|
357 |
assert(p + this_chunk_word_size <= region_end, "Sanity"); |
|
358 |
||
359 |
// Create splitting chunk. |
|
360 |
Metachunk* this_chunk = ::new (p) Metachunk(this_chunk_index, is_class(), this_chunk_word_size, vsn); |
|
361 |
assert(this_chunk == (Metachunk*)p, "Sanity"); |
|
362 |
this_chunk->set_origin(origin_split); |
|
363 |
ocmap->set_chunk_starts_at_address(p, true); |
|
364 |
do_update_in_use_info_for_chunk(this_chunk, false); |
|
365 |
||
366 |
// This chunk should be valid and can be verified. |
|
367 |
DEBUG_ONLY(do_verify_chunk(this_chunk)); |
|
368 |
||
369 |
// Return this chunk to freelist and correct counter. |
|
370 |
free_chunks(this_chunk_index)->return_chunk_at_head(this_chunk); |
|
371 |
_free_chunks_count ++; |
|
372 |
||
373 |
log_trace(gc, metaspace, freelist)("Created chunk at " PTR_FORMAT ", word size " |
|
374 |
SIZE_FORMAT_HEX " (%s), in split region [" PTR_FORMAT "..." PTR_FORMAT ").", |
|
375 |
p2i(this_chunk), this_chunk->word_size(), chunk_size_name(this_chunk_index), |
|
376 |
p2i(region_start), p2i(region_end)); |
|
377 |
||
378 |
p += this_chunk_word_size; |
|
379 |
||
380 |
} |
|
381 |
||
53970 | 382 |
// Note: at this point, the VirtualSpaceNode is invalid since we split a chunk and |
383 |
// did not yet hand out part of that split; so, vsn->verify_free_chunks_are_ideally_merged() |
|
384 |
// would assert. Instead, do all verifications in the caller. |
|
385 |
||
386 |
DEBUG_ONLY(g_internal_statistics.num_chunk_splits ++); |
|
387 |
||
50193 | 388 |
return target_chunk; |
389 |
} |
|
390 |
||
391 |
Metachunk* ChunkManager::free_chunks_get(size_t word_size) { |
|
392 |
assert_lock_strong(MetaspaceExpand_lock); |
|
393 |
||
394 |
Metachunk* chunk = NULL; |
|
395 |
bool we_did_split_a_chunk = false; |
|
396 |
||
397 |
if (list_index(word_size) != HumongousIndex) { |
|
398 |
||
399 |
ChunkList* free_list = find_free_chunks_list(word_size); |
|
400 |
assert(free_list != NULL, "Sanity check"); |
|
401 |
||
402 |
chunk = free_list->head(); |
|
403 |
||
404 |
if (chunk == NULL) { |
|
405 |
// Split large chunks into smaller chunks if there are no smaller chunks, just large chunks. |
|
406 |
// This is the counterpart of the coalescing-upon-chunk-return. |
|
407 |
||
408 |
ChunkIndex target_chunk_index = get_chunk_type_by_size(word_size, is_class()); |
|
409 |
||
410 |
// Is there a larger chunk we could split? |
|
411 |
Metachunk* larger_chunk = NULL; |
|
412 |
ChunkIndex larger_chunk_index = next_chunk_index(target_chunk_index); |
|
413 |
while (larger_chunk == NULL && larger_chunk_index < NumberOfFreeLists) { |
|
414 |
larger_chunk = free_chunks(larger_chunk_index)->head(); |
|
415 |
if (larger_chunk == NULL) { |
|
416 |
larger_chunk_index = next_chunk_index(larger_chunk_index); |
|
417 |
} |
|
418 |
} |
|
419 |
||
420 |
if (larger_chunk != NULL) { |
|
421 |
assert(larger_chunk->word_size() > word_size, "Sanity"); |
|
422 |
assert(larger_chunk->get_chunk_type() == larger_chunk_index, "Sanity"); |
|
423 |
||
424 |
// We found a larger chunk. Lets split it up: |
|
425 |
// - remove old chunk |
|
426 |
// - in its place, create new smaller chunks, with at least one chunk |
|
427 |
// being of target size, the others sized as large as possible. This |
|
428 |
// is to make sure the resulting chunks are "as coalesced as possible" |
|
429 |
// (similar to VirtualSpaceNode::retire()). |
|
430 |
// Note: during this operation both ChunkManager and VirtualSpaceNode |
|
431 |
// are temporarily invalid, so be careful with asserts. |
|
432 |
||
433 |
log_trace(gc, metaspace, freelist)("%s: splitting chunk " PTR_FORMAT |
|
434 |
", word size " SIZE_FORMAT_HEX " (%s), to get a chunk of word size " SIZE_FORMAT_HEX " (%s)...", |
|
435 |
(is_class() ? "class space" : "metaspace"), p2i(larger_chunk), larger_chunk->word_size(), |
|
436 |
chunk_size_name(larger_chunk_index), word_size, chunk_size_name(target_chunk_index)); |
|
437 |
||
438 |
chunk = split_chunk(word_size, larger_chunk); |
|
439 |
||
440 |
// This should have worked. |
|
441 |
assert(chunk != NULL, "Sanity"); |
|
442 |
assert(chunk->word_size() == word_size, "Sanity"); |
|
443 |
assert(chunk->is_tagged_free(), "Sanity"); |
|
444 |
||
445 |
we_did_split_a_chunk = true; |
|
446 |
||
447 |
} |
|
448 |
} |
|
449 |
||
450 |
if (chunk == NULL) { |
|
451 |
return NULL; |
|
452 |
} |
|
453 |
||
454 |
// Remove the chunk as the head of the list. |
|
455 |
free_list->remove_chunk(chunk); |
|
456 |
||
457 |
log_trace(gc, metaspace, freelist)("ChunkManager::free_chunks_get: free_list: " PTR_FORMAT " chunks left: " SSIZE_FORMAT ".", |
|
458 |
p2i(free_list), free_list->count()); |
|
459 |
||
460 |
} else { |
|
461 |
chunk = humongous_dictionary()->get_chunk(word_size); |
|
462 |
||
463 |
if (chunk == NULL) { |
|
464 |
return NULL; |
|
465 |
} |
|
466 |
||
50811
f533eb5e7430
8205664: Move detailed metaspace logging from debug to trace
pliden
parents:
50380
diff
changeset
|
467 |
log_trace(gc, metaspace, alloc)("Free list allocate humongous chunk size " SIZE_FORMAT " for requested size " SIZE_FORMAT " waste " SIZE_FORMAT, |
50193 | 468 |
chunk->word_size(), word_size, chunk->word_size() - word_size); |
469 |
} |
|
470 |
||
471 |
// Chunk has been removed from the chunk manager; update counters. |
|
472 |
account_for_removed_chunk(chunk); |
|
473 |
do_update_in_use_info_for_chunk(chunk, true); |
|
474 |
chunk->container()->inc_container_count(); |
|
475 |
chunk->inc_use_count(); |
|
476 |
||
477 |
// Remove it from the links to this freelist |
|
478 |
chunk->set_next(NULL); |
|
479 |
chunk->set_prev(NULL); |
|
480 |
||
481 |
// Run some verifications (some more if we did a chunk split) |
|
482 |
#ifdef ASSERT |
|
53970 | 483 |
|
484 |
EVERY_NTH(VerifyMetaspaceInterval) |
|
485 |
// Be extra verify-y when chunk split happened. |
|
486 |
locked_verify(true); |
|
50193 | 487 |
VirtualSpaceNode* const vsn = chunk->container(); |
53970 | 488 |
vsn->verify(true); |
50193 | 489 |
if (we_did_split_a_chunk) { |
490 |
vsn->verify_free_chunks_are_ideally_merged(); |
|
491 |
} |
|
53970 | 492 |
END_EVERY_NTH |
493 |
||
494 |
g_internal_statistics.num_chunks_removed_from_freelist ++; |
|
495 |
||
50193 | 496 |
#endif |
497 |
||
498 |
return chunk; |
|
499 |
} |
|
500 |
||
501 |
Metachunk* ChunkManager::chunk_freelist_allocate(size_t word_size) { |
|
502 |
assert_lock_strong(MetaspaceExpand_lock); |
|
503 |
||
504 |
// Take from the beginning of the list |
|
505 |
Metachunk* chunk = free_chunks_get(word_size); |
|
506 |
if (chunk == NULL) { |
|
507 |
return NULL; |
|
508 |
} |
|
509 |
||
510 |
assert((word_size <= chunk->word_size()) || |
|
511 |
(list_index(chunk->word_size()) == HumongousIndex), |
|
512 |
"Non-humongous variable sized chunk"); |
|
50811
f533eb5e7430
8205664: Move detailed metaspace logging from debug to trace
pliden
parents:
50380
diff
changeset
|
513 |
LogTarget(Trace, gc, metaspace, freelist) lt; |
50193 | 514 |
if (lt.is_enabled()) { |
515 |
size_t list_count; |
|
516 |
if (list_index(word_size) < HumongousIndex) { |
|
517 |
ChunkList* list = find_free_chunks_list(word_size); |
|
518 |
list_count = list->count(); |
|
519 |
} else { |
|
520 |
list_count = humongous_dictionary()->total_count(); |
|
521 |
} |
|
522 |
LogStream ls(lt); |
|
523 |
ls.print("ChunkManager::chunk_freelist_allocate: " PTR_FORMAT " chunk " PTR_FORMAT " size " SIZE_FORMAT " count " SIZE_FORMAT " ", |
|
524 |
p2i(this), p2i(chunk), chunk->word_size(), list_count); |
|
525 |
ResourceMark rm; |
|
526 |
locked_print_free_chunks(&ls); |
|
527 |
} |
|
528 |
||
529 |
return chunk; |
|
530 |
} |
|
531 |
||
532 |
void ChunkManager::return_single_chunk(Metachunk* chunk) { |
|
53970 | 533 |
|
534 |
#ifdef ASSERT |
|
535 |
EVERY_NTH(VerifyMetaspaceInterval) |
|
536 |
this->locked_verify(false); |
|
537 |
do_verify_chunk(chunk); |
|
538 |
END_EVERY_NTH |
|
539 |
#endif |
|
540 |
||
50193 | 541 |
const ChunkIndex index = chunk->get_chunk_type(); |
542 |
assert_lock_strong(MetaspaceExpand_lock); |
|
53970 | 543 |
DEBUG_ONLY(g_internal_statistics.num_chunks_added_to_freelist ++;) |
50193 | 544 |
assert(chunk != NULL, "Expected chunk."); |
545 |
assert(chunk->container() != NULL, "Container should have been set."); |
|
546 |
assert(chunk->is_tagged_free() == false, "Chunk should be in use."); |
|
547 |
index_bounds_check(index); |
|
548 |
||
549 |
// Note: mangle *before* returning the chunk to the freelist or dictionary. It does not |
|
550 |
// matter for the freelist (non-humongous chunks), but the humongous chunk dictionary |
|
551 |
// keeps tree node pointers in the chunk payload area which mangle will overwrite. |
|
552 |
DEBUG_ONLY(chunk->mangle(badMetaWordVal);) |
|
553 |
||
53970 | 554 |
// may need node for verification later after chunk may have been merged away. |
555 |
DEBUG_ONLY(VirtualSpaceNode* vsn = chunk->container(); ) |
|
556 |
||
50193 | 557 |
if (index != HumongousIndex) { |
558 |
// Return non-humongous chunk to freelist. |
|
559 |
ChunkList* list = free_chunks(index); |
|
560 |
assert(list->size() == chunk->word_size(), "Wrong chunk type."); |
|
561 |
list->return_chunk_at_head(chunk); |
|
562 |
log_trace(gc, metaspace, freelist)("returned one %s chunk at " PTR_FORMAT " to freelist.", |
|
563 |
chunk_size_name(index), p2i(chunk)); |
|
564 |
} else { |
|
565 |
// Return humongous chunk to dictionary. |
|
566 |
assert(chunk->word_size() > free_chunks(MediumIndex)->size(), "Wrong chunk type."); |
|
567 |
assert(chunk->word_size() % free_chunks(SpecializedIndex)->size() == 0, |
|
568 |
"Humongous chunk has wrong alignment."); |
|
569 |
_humongous_dictionary.return_chunk(chunk); |
|
570 |
log_trace(gc, metaspace, freelist)("returned one %s chunk at " PTR_FORMAT " (word size " SIZE_FORMAT ") to freelist.", |
|
571 |
chunk_size_name(index), p2i(chunk), chunk->word_size()); |
|
572 |
} |
|
573 |
chunk->container()->dec_container_count(); |
|
574 |
do_update_in_use_info_for_chunk(chunk, false); |
|
575 |
||
576 |
// Chunk has been added; update counters. |
|
577 |
account_for_added_chunk(chunk); |
|
578 |
||
579 |
// Attempt coalesce returned chunks with its neighboring chunks: |
|
580 |
// if this chunk is small or special, attempt to coalesce to a medium chunk. |
|
581 |
if (index == SmallIndex || index == SpecializedIndex) { |
|
582 |
if (!attempt_to_coalesce_around_chunk(chunk, MediumIndex)) { |
|
583 |
// This did not work. But if this chunk is special, we still may form a small chunk? |
|
584 |
if (index == SpecializedIndex) { |
|
585 |
if (!attempt_to_coalesce_around_chunk(chunk, SmallIndex)) { |
|
586 |
// give up. |
|
587 |
} |
|
588 |
} |
|
589 |
} |
|
590 |
} |
|
591 |
||
53970 | 592 |
// From here on do not access chunk anymore, it may have been merged with another chunk. |
593 |
||
594 |
#ifdef ASSERT |
|
595 |
EVERY_NTH(VerifyMetaspaceInterval) |
|
596 |
this->locked_verify(true); |
|
597 |
vsn->verify(true); |
|
598 |
vsn->verify_free_chunks_are_ideally_merged(); |
|
599 |
END_EVERY_NTH |
|
600 |
#endif |
|
601 |
||
50193 | 602 |
} |
603 |
||
604 |
void ChunkManager::return_chunk_list(Metachunk* chunks) { |
|
605 |
if (chunks == NULL) { |
|
606 |
return; |
|
607 |
} |
|
608 |
LogTarget(Trace, gc, metaspace, freelist) log; |
|
609 |
if (log.is_enabled()) { // tracing |
|
610 |
log.print("returning list of chunks..."); |
|
611 |
} |
|
612 |
unsigned num_chunks_returned = 0; |
|
613 |
size_t size_chunks_returned = 0; |
|
614 |
Metachunk* cur = chunks; |
|
615 |
while (cur != NULL) { |
|
616 |
// Capture the next link before it is changed |
|
617 |
// by the call to return_chunk_at_head(); |
|
618 |
Metachunk* next = cur->next(); |
|
619 |
if (log.is_enabled()) { // tracing |
|
620 |
num_chunks_returned ++; |
|
621 |
size_chunks_returned += cur->word_size(); |
|
622 |
} |
|
623 |
return_single_chunk(cur); |
|
624 |
cur = next; |
|
625 |
} |
|
626 |
if (log.is_enabled()) { // tracing |
|
627 |
log.print("returned %u chunks to freelist, total word size " SIZE_FORMAT ".", |
|
628 |
num_chunks_returned, size_chunks_returned); |
|
629 |
} |
|
630 |
} |
|
631 |
||
632 |
void ChunkManager::collect_statistics(ChunkManagerStatistics* out) const { |
|
633 |
MutexLockerEx cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); |
|
634 |
for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { |
|
635 |
out->chunk_stats(i).add(num_free_chunks(i), size_free_chunks_in_bytes(i) / sizeof(MetaWord)); |
|
636 |
} |
|
637 |
} |
|
638 |
||
639 |
} // namespace metaspace |
|
640 |
||
641 |
||
642 |