author | coleenp |
Wed, 06 Jun 2018 10:45:40 -0400 | |
changeset 50429 | 83aec1d357d4 |
parent 50193 | 49c3e91c424f |
child 51334 | cc2c79d22508 |
permissions | -rw-r--r-- |
50193 | 1 |
/* |
2 |
* virtualSpaceList.cpp |
|
3 |
* |
|
4 |
* Created on: May 6, 2018 |
|
5 |
* Author: thomas |
|
6 |
*/ |
|
7 |
||
8 |
||
9 |
#include "precompiled.hpp" |
|
10 |
#include "logging/log.hpp" |
|
11 |
#include "logging/logStream.hpp" |
|
12 |
#include "memory/metaspace.hpp" |
|
13 |
#include "memory/metaspace/chunkManager.hpp" |
|
14 |
#include "memory/metaspace/metachunk.hpp" |
|
15 |
#include "memory/metaspace/metaspaceCommon.hpp" |
|
16 |
#include "memory/metaspace/virtualSpaceList.hpp" |
|
17 |
#include "memory/metaspace/virtualSpaceNode.hpp" |
|
50429
83aec1d357d4
8204301: Make OrderAccess functions available to hpp rather than inline.hpp files
coleenp
parents:
50193
diff
changeset
|
18 |
#include "runtime/orderAccess.hpp" |
50193 | 19 |
#include "runtime/mutexLocker.hpp" |
20 |
#include "runtime/safepoint.hpp" |
|
21 |
||
22 |
namespace metaspace { |
|
23 |
||
24 |
||
25 |
VirtualSpaceList::~VirtualSpaceList() { |
|
26 |
VirtualSpaceListIterator iter(virtual_space_list()); |
|
27 |
while (iter.repeat()) { |
|
28 |
VirtualSpaceNode* vsl = iter.get_next(); |
|
29 |
delete vsl; |
|
30 |
} |
|
31 |
} |
|
32 |
||
33 |
void VirtualSpaceList::inc_reserved_words(size_t v) { |
|
34 |
assert_lock_strong(MetaspaceExpand_lock); |
|
35 |
_reserved_words = _reserved_words + v; |
|
36 |
} |
|
37 |
void VirtualSpaceList::dec_reserved_words(size_t v) { |
|
38 |
assert_lock_strong(MetaspaceExpand_lock); |
|
39 |
_reserved_words = _reserved_words - v; |
|
40 |
} |
|
41 |
||
42 |
#define assert_committed_below_limit() \ |
|
43 |
assert(MetaspaceUtils::committed_bytes() <= MaxMetaspaceSize, \ |
|
44 |
"Too much committed memory. Committed: " SIZE_FORMAT \ |
|
45 |
" limit (MaxMetaspaceSize): " SIZE_FORMAT, \ |
|
46 |
MetaspaceUtils::committed_bytes(), MaxMetaspaceSize); |
|
47 |
||
48 |
void VirtualSpaceList::inc_committed_words(size_t v) { |
|
49 |
assert_lock_strong(MetaspaceExpand_lock); |
|
50 |
_committed_words = _committed_words + v; |
|
51 |
||
52 |
assert_committed_below_limit(); |
|
53 |
} |
|
54 |
void VirtualSpaceList::dec_committed_words(size_t v) { |
|
55 |
assert_lock_strong(MetaspaceExpand_lock); |
|
56 |
_committed_words = _committed_words - v; |
|
57 |
||
58 |
assert_committed_below_limit(); |
|
59 |
} |
|
60 |
||
61 |
void VirtualSpaceList::inc_virtual_space_count() { |
|
62 |
assert_lock_strong(MetaspaceExpand_lock); |
|
63 |
_virtual_space_count++; |
|
64 |
} |
|
65 |
||
66 |
void VirtualSpaceList::dec_virtual_space_count() { |
|
67 |
assert_lock_strong(MetaspaceExpand_lock); |
|
68 |
_virtual_space_count--; |
|
69 |
} |
|
70 |
||
71 |
// Walk the list of VirtualSpaceNodes and delete |
|
72 |
// nodes with a 0 container_count. Remove Metachunks in |
|
73 |
// the node from their respective freelists. |
|
74 |
void VirtualSpaceList::purge(ChunkManager* chunk_manager) { |
|
75 |
assert(SafepointSynchronize::is_at_safepoint(), "must be called at safepoint for contains to work"); |
|
76 |
assert_lock_strong(MetaspaceExpand_lock); |
|
77 |
// Don't use a VirtualSpaceListIterator because this |
|
78 |
// list is being changed and a straightforward use of an iterator is not safe. |
|
79 |
VirtualSpaceNode* purged_vsl = NULL; |
|
80 |
VirtualSpaceNode* prev_vsl = virtual_space_list(); |
|
81 |
VirtualSpaceNode* next_vsl = prev_vsl; |
|
82 |
while (next_vsl != NULL) { |
|
83 |
VirtualSpaceNode* vsl = next_vsl; |
|
84 |
DEBUG_ONLY(vsl->verify_container_count();) |
|
85 |
next_vsl = vsl->next(); |
|
86 |
// Don't free the current virtual space since it will likely |
|
87 |
// be needed soon. |
|
88 |
if (vsl->container_count() == 0 && vsl != current_virtual_space()) { |
|
89 |
log_trace(gc, metaspace, freelist)("Purging VirtualSpaceNode " PTR_FORMAT " (capacity: " SIZE_FORMAT |
|
90 |
", used: " SIZE_FORMAT ").", p2i(vsl), vsl->capacity_words_in_vs(), vsl->used_words_in_vs()); |
|
91 |
DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_purged)); |
|
92 |
// Unlink it from the list |
|
93 |
if (prev_vsl == vsl) { |
|
94 |
// This is the case of the current node being the first node. |
|
95 |
assert(vsl == virtual_space_list(), "Expected to be the first node"); |
|
96 |
set_virtual_space_list(vsl->next()); |
|
97 |
} else { |
|
98 |
prev_vsl->set_next(vsl->next()); |
|
99 |
} |
|
100 |
||
101 |
vsl->purge(chunk_manager); |
|
102 |
dec_reserved_words(vsl->reserved_words()); |
|
103 |
dec_committed_words(vsl->committed_words()); |
|
104 |
dec_virtual_space_count(); |
|
105 |
purged_vsl = vsl; |
|
106 |
delete vsl; |
|
107 |
} else { |
|
108 |
prev_vsl = vsl; |
|
109 |
} |
|
110 |
} |
|
111 |
#ifdef ASSERT |
|
112 |
if (purged_vsl != NULL) { |
|
113 |
// List should be stable enough to use an iterator here. |
|
114 |
VirtualSpaceListIterator iter(virtual_space_list()); |
|
115 |
while (iter.repeat()) { |
|
116 |
VirtualSpaceNode* vsl = iter.get_next(); |
|
117 |
assert(vsl != purged_vsl, "Purge of vsl failed"); |
|
118 |
} |
|
119 |
} |
|
120 |
#endif |
|
121 |
} |
|
122 |
||
123 |
||
124 |
// This function looks at the mmap regions in the metaspace without locking. |
|
125 |
// The chunks are added with store ordering and not deleted except for at |
|
126 |
// unloading time during a safepoint. |
|
127 |
bool VirtualSpaceList::contains(const void* ptr) { |
|
128 |
// List should be stable enough to use an iterator here because removing virtual |
|
129 |
// space nodes is only allowed at a safepoint. |
|
130 |
VirtualSpaceListIterator iter(virtual_space_list()); |
|
131 |
while (iter.repeat()) { |
|
132 |
VirtualSpaceNode* vsn = iter.get_next(); |
|
133 |
if (vsn->contains(ptr)) { |
|
134 |
return true; |
|
135 |
} |
|
136 |
} |
|
137 |
return false; |
|
138 |
} |
|
139 |
||
140 |
void VirtualSpaceList::retire_current_virtual_space() { |
|
141 |
assert_lock_strong(MetaspaceExpand_lock); |
|
142 |
||
143 |
VirtualSpaceNode* vsn = current_virtual_space(); |
|
144 |
||
145 |
ChunkManager* cm = is_class() ? Metaspace::chunk_manager_class() : |
|
146 |
Metaspace::chunk_manager_metadata(); |
|
147 |
||
148 |
vsn->retire(cm); |
|
149 |
} |
|
150 |
||
151 |
VirtualSpaceList::VirtualSpaceList(size_t word_size) : |
|
152 |
_is_class(false), |
|
153 |
_virtual_space_list(NULL), |
|
154 |
_current_virtual_space(NULL), |
|
155 |
_reserved_words(0), |
|
156 |
_committed_words(0), |
|
157 |
_virtual_space_count(0) { |
|
158 |
MutexLockerEx cl(MetaspaceExpand_lock, |
|
159 |
Mutex::_no_safepoint_check_flag); |
|
160 |
create_new_virtual_space(word_size); |
|
161 |
} |
|
162 |
||
163 |
VirtualSpaceList::VirtualSpaceList(ReservedSpace rs) : |
|
164 |
_is_class(true), |
|
165 |
_virtual_space_list(NULL), |
|
166 |
_current_virtual_space(NULL), |
|
167 |
_reserved_words(0), |
|
168 |
_committed_words(0), |
|
169 |
_virtual_space_count(0) { |
|
170 |
MutexLockerEx cl(MetaspaceExpand_lock, |
|
171 |
Mutex::_no_safepoint_check_flag); |
|
172 |
VirtualSpaceNode* class_entry = new VirtualSpaceNode(is_class(), rs); |
|
173 |
bool succeeded = class_entry->initialize(); |
|
174 |
if (succeeded) { |
|
175 |
link_vs(class_entry); |
|
176 |
} |
|
177 |
} |
|
178 |
||
179 |
size_t VirtualSpaceList::free_bytes() { |
|
180 |
return current_virtual_space()->free_words_in_vs() * BytesPerWord; |
|
181 |
} |
|
182 |
||
183 |
// Allocate another meta virtual space and add it to the list. |
|
184 |
bool VirtualSpaceList::create_new_virtual_space(size_t vs_word_size) { |
|
185 |
assert_lock_strong(MetaspaceExpand_lock); |
|
186 |
||
187 |
if (is_class()) { |
|
188 |
assert(false, "We currently don't support more than one VirtualSpace for" |
|
189 |
" the compressed class space. The initialization of the" |
|
190 |
" CCS uses another code path and should not hit this path."); |
|
191 |
return false; |
|
192 |
} |
|
193 |
||
194 |
if (vs_word_size == 0) { |
|
195 |
assert(false, "vs_word_size should always be at least _reserve_alignment large."); |
|
196 |
return false; |
|
197 |
} |
|
198 |
||
199 |
// Reserve the space |
|
200 |
size_t vs_byte_size = vs_word_size * BytesPerWord; |
|
201 |
assert_is_aligned(vs_byte_size, Metaspace::reserve_alignment()); |
|
202 |
||
203 |
// Allocate the meta virtual space and initialize it. |
|
204 |
VirtualSpaceNode* new_entry = new VirtualSpaceNode(is_class(), vs_byte_size); |
|
205 |
if (!new_entry->initialize()) { |
|
206 |
delete new_entry; |
|
207 |
return false; |
|
208 |
} else { |
|
209 |
assert(new_entry->reserved_words() == vs_word_size, |
|
210 |
"Reserved memory size differs from requested memory size"); |
|
211 |
// ensure lock-free iteration sees fully initialized node |
|
212 |
OrderAccess::storestore(); |
|
213 |
link_vs(new_entry); |
|
214 |
DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_created)); |
|
215 |
return true; |
|
216 |
} |
|
217 |
} |
|
218 |
||
219 |
void VirtualSpaceList::link_vs(VirtualSpaceNode* new_entry) { |
|
220 |
if (virtual_space_list() == NULL) { |
|
221 |
set_virtual_space_list(new_entry); |
|
222 |
} else { |
|
223 |
current_virtual_space()->set_next(new_entry); |
|
224 |
} |
|
225 |
set_current_virtual_space(new_entry); |
|
226 |
inc_reserved_words(new_entry->reserved_words()); |
|
227 |
inc_committed_words(new_entry->committed_words()); |
|
228 |
inc_virtual_space_count(); |
|
229 |
#ifdef ASSERT |
|
230 |
new_entry->mangle(); |
|
231 |
#endif |
|
232 |
LogTarget(Trace, gc, metaspace) lt; |
|
233 |
if (lt.is_enabled()) { |
|
234 |
LogStream ls(lt); |
|
235 |
VirtualSpaceNode* vsl = current_virtual_space(); |
|
236 |
ResourceMark rm; |
|
237 |
vsl->print_on(&ls); |
|
238 |
} |
|
239 |
} |
|
240 |
||
241 |
bool VirtualSpaceList::expand_node_by(VirtualSpaceNode* node, |
|
242 |
size_t min_words, |
|
243 |
size_t preferred_words) { |
|
244 |
size_t before = node->committed_words(); |
|
245 |
||
246 |
bool result = node->expand_by(min_words, preferred_words); |
|
247 |
||
248 |
size_t after = node->committed_words(); |
|
249 |
||
250 |
// after and before can be the same if the memory was pre-committed. |
|
251 |
assert(after >= before, "Inconsistency"); |
|
252 |
inc_committed_words(after - before); |
|
253 |
||
254 |
return result; |
|
255 |
} |
|
256 |
||
257 |
bool VirtualSpaceList::expand_by(size_t min_words, size_t preferred_words) { |
|
258 |
assert_is_aligned(min_words, Metaspace::commit_alignment_words()); |
|
259 |
assert_is_aligned(preferred_words, Metaspace::commit_alignment_words()); |
|
260 |
assert(min_words <= preferred_words, "Invalid arguments"); |
|
261 |
||
262 |
const char* const class_or_not = (is_class() ? "class" : "non-class"); |
|
263 |
||
264 |
if (!MetaspaceGC::can_expand(min_words, this->is_class())) { |
|
265 |
log_trace(gc, metaspace, freelist)("Cannot expand %s virtual space list.", |
|
266 |
class_or_not); |
|
267 |
return false; |
|
268 |
} |
|
269 |
||
270 |
size_t allowed_expansion_words = MetaspaceGC::allowed_expansion(); |
|
271 |
if (allowed_expansion_words < min_words) { |
|
272 |
log_trace(gc, metaspace, freelist)("Cannot expand %s virtual space list (must try gc first).", |
|
273 |
class_or_not); |
|
274 |
return false; |
|
275 |
} |
|
276 |
||
277 |
size_t max_expansion_words = MIN2(preferred_words, allowed_expansion_words); |
|
278 |
||
279 |
// Commit more memory from the the current virtual space. |
|
280 |
bool vs_expanded = expand_node_by(current_virtual_space(), |
|
281 |
min_words, |
|
282 |
max_expansion_words); |
|
283 |
if (vs_expanded) { |
|
284 |
log_trace(gc, metaspace, freelist)("Expanded %s virtual space list.", |
|
285 |
class_or_not); |
|
286 |
return true; |
|
287 |
} |
|
288 |
log_trace(gc, metaspace, freelist)("%s virtual space list: retire current node.", |
|
289 |
class_or_not); |
|
290 |
retire_current_virtual_space(); |
|
291 |
||
292 |
// Get another virtual space. |
|
293 |
size_t grow_vs_words = MAX2((size_t)VirtualSpaceSize, preferred_words); |
|
294 |
grow_vs_words = align_up(grow_vs_words, Metaspace::reserve_alignment_words()); |
|
295 |
||
296 |
if (create_new_virtual_space(grow_vs_words)) { |
|
297 |
if (current_virtual_space()->is_pre_committed()) { |
|
298 |
// The memory was pre-committed, so we are done here. |
|
299 |
assert(min_words <= current_virtual_space()->committed_words(), |
|
300 |
"The new VirtualSpace was pre-committed, so it" |
|
301 |
"should be large enough to fit the alloc request."); |
|
302 |
return true; |
|
303 |
} |
|
304 |
||
305 |
return expand_node_by(current_virtual_space(), |
|
306 |
min_words, |
|
307 |
max_expansion_words); |
|
308 |
} |
|
309 |
||
310 |
return false; |
|
311 |
} |
|
312 |
||
313 |
// Given a chunk, calculate the largest possible padding space which |
|
314 |
// could be required when allocating it. |
|
315 |
static size_t largest_possible_padding_size_for_chunk(size_t chunk_word_size, bool is_class) { |
|
316 |
const ChunkIndex chunk_type = get_chunk_type_by_size(chunk_word_size, is_class); |
|
317 |
if (chunk_type != HumongousIndex) { |
|
318 |
// Normal, non-humongous chunks are allocated at chunk size |
|
319 |
// boundaries, so the largest padding space required would be that |
|
320 |
// minus the smallest chunk size. |
|
321 |
const size_t smallest_chunk_size = is_class ? ClassSpecializedChunk : SpecializedChunk; |
|
322 |
return chunk_word_size - smallest_chunk_size; |
|
323 |
} else { |
|
324 |
// Humongous chunks are allocated at smallest-chunksize |
|
325 |
// boundaries, so there is no padding required. |
|
326 |
return 0; |
|
327 |
} |
|
328 |
} |
|
329 |
||
330 |
||
331 |
Metachunk* VirtualSpaceList::get_new_chunk(size_t chunk_word_size, size_t suggested_commit_granularity) { |
|
332 |
||
333 |
// Allocate a chunk out of the current virtual space. |
|
334 |
Metachunk* next = current_virtual_space()->get_chunk_vs(chunk_word_size); |
|
335 |
||
336 |
if (next != NULL) { |
|
337 |
return next; |
|
338 |
} |
|
339 |
||
340 |
// The expand amount is currently only determined by the requested sizes |
|
341 |
// and not how much committed memory is left in the current virtual space. |
|
342 |
||
343 |
// We must have enough space for the requested size and any |
|
344 |
// additional reqired padding chunks. |
|
345 |
const size_t size_for_padding = largest_possible_padding_size_for_chunk(chunk_word_size, this->is_class()); |
|
346 |
||
347 |
size_t min_word_size = align_up(chunk_word_size + size_for_padding, Metaspace::commit_alignment_words()); |
|
348 |
size_t preferred_word_size = align_up(suggested_commit_granularity, Metaspace::commit_alignment_words()); |
|
349 |
if (min_word_size >= preferred_word_size) { |
|
350 |
// Can happen when humongous chunks are allocated. |
|
351 |
preferred_word_size = min_word_size; |
|
352 |
} |
|
353 |
||
354 |
bool expanded = expand_by(min_word_size, preferred_word_size); |
|
355 |
if (expanded) { |
|
356 |
next = current_virtual_space()->get_chunk_vs(chunk_word_size); |
|
357 |
assert(next != NULL, "The allocation was expected to succeed after the expansion"); |
|
358 |
} |
|
359 |
||
360 |
return next; |
|
361 |
} |
|
362 |
||
363 |
void VirtualSpaceList::print_on(outputStream* st, size_t scale) const { |
|
364 |
st->print_cr(SIZE_FORMAT " nodes, current node: " PTR_FORMAT, |
|
365 |
_virtual_space_count, p2i(_current_virtual_space)); |
|
366 |
VirtualSpaceListIterator iter(virtual_space_list()); |
|
367 |
while (iter.repeat()) { |
|
368 |
st->cr(); |
|
369 |
VirtualSpaceNode* node = iter.get_next(); |
|
370 |
node->print_on(st, scale); |
|
371 |
} |
|
372 |
} |
|
373 |
||
374 |
void VirtualSpaceList::print_map(outputStream* st) const { |
|
375 |
VirtualSpaceNode* list = virtual_space_list(); |
|
376 |
VirtualSpaceListIterator iter(list); |
|
377 |
unsigned i = 0; |
|
378 |
while (iter.repeat()) { |
|
379 |
st->print_cr("Node %u:", i); |
|
380 |
VirtualSpaceNode* node = iter.get_next(); |
|
381 |
node->print_map(st, this->is_class()); |
|
382 |
i ++; |
|
383 |
} |
|
384 |
} |
|
385 |
||
386 |
} // namespace metaspace |
|
387 |