Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/hotspot/share/memory/arena.cpp
40950 views
1
/*
2
* Copyright (c) 2017, 2021, 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 "memory/allocation.hpp"
27
#include "memory/allocation.inline.hpp"
28
#include "memory/resourceArea.hpp"
29
#include "runtime/os.hpp"
30
#include "runtime/task.hpp"
31
#include "runtime/threadCritical.hpp"
32
#include "services/memTracker.hpp"
33
#include "utilities/ostream.hpp"
34
35
//--------------------------------------------------------------------------------------
36
// ChunkPool implementation
37
38
// MT-safe pool of chunks to reduce malloc/free thrashing
39
// NB: not using Mutex because pools are used before Threads are initialized
40
class ChunkPool: public CHeapObj<mtInternal> {
41
Chunk* _first; // first cached Chunk; its first word points to next chunk
42
size_t _num_chunks; // number of unused chunks in pool
43
size_t _num_used; // number of chunks currently checked out
44
const size_t _size; // size of each chunk (must be uniform)
45
46
// Our four static pools
47
static ChunkPool* _large_pool;
48
static ChunkPool* _medium_pool;
49
static ChunkPool* _small_pool;
50
static ChunkPool* _tiny_pool;
51
52
// return first element or null
53
void* get_first() {
54
Chunk* c = _first;
55
if (_first) {
56
_first = _first->next();
57
_num_chunks--;
58
}
59
return c;
60
}
61
62
public:
63
// All chunks in a ChunkPool has the same size
64
ChunkPool(size_t size) : _size(size) { _first = NULL; _num_chunks = _num_used = 0; }
65
66
// Allocate a new chunk from the pool (might expand the pool)
67
NOINLINE void* allocate(size_t bytes, AllocFailType alloc_failmode) {
68
assert(bytes == _size, "bad size");
69
void* p = NULL;
70
// No VM lock can be taken inside ThreadCritical lock, so os::malloc
71
// should be done outside ThreadCritical lock due to NMT
72
{ ThreadCritical tc;
73
_num_used++;
74
p = get_first();
75
}
76
if (p == NULL) p = os::malloc(bytes, mtChunk, CURRENT_PC);
77
if (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) {
78
vm_exit_out_of_memory(bytes, OOM_MALLOC_ERROR, "ChunkPool::allocate");
79
}
80
return p;
81
}
82
83
// Return a chunk to the pool
84
void free(Chunk* chunk) {
85
assert(chunk->length() + Chunk::aligned_overhead_size() == _size, "bad size");
86
ThreadCritical tc;
87
_num_used--;
88
89
// Add chunk to list
90
chunk->set_next(_first);
91
_first = chunk;
92
_num_chunks++;
93
}
94
95
// Prune the pool
96
void free_all_but(size_t n) {
97
Chunk* cur = NULL;
98
Chunk* next;
99
{
100
// if we have more than n chunks, free all of them
101
ThreadCritical tc;
102
if (_num_chunks > n) {
103
// free chunks at end of queue, for better locality
104
cur = _first;
105
for (size_t i = 0; i < (n - 1) && cur != NULL; i++) cur = cur->next();
106
107
if (cur != NULL) {
108
next = cur->next();
109
cur->set_next(NULL);
110
cur = next;
111
112
// Free all remaining chunks while in ThreadCritical lock
113
// so NMT adjustment is stable.
114
while(cur != NULL) {
115
next = cur->next();
116
os::free(cur);
117
_num_chunks--;
118
cur = next;
119
}
120
}
121
}
122
}
123
}
124
125
// Accessors to preallocated pool's
126
static ChunkPool* large_pool() { assert(_large_pool != NULL, "must be initialized"); return _large_pool; }
127
static ChunkPool* medium_pool() { assert(_medium_pool != NULL, "must be initialized"); return _medium_pool; }
128
static ChunkPool* small_pool() { assert(_small_pool != NULL, "must be initialized"); return _small_pool; }
129
static ChunkPool* tiny_pool() { assert(_tiny_pool != NULL, "must be initialized"); return _tiny_pool; }
130
131
static void initialize() {
132
_large_pool = new ChunkPool(Chunk::size + Chunk::aligned_overhead_size());
133
_medium_pool = new ChunkPool(Chunk::medium_size + Chunk::aligned_overhead_size());
134
_small_pool = new ChunkPool(Chunk::init_size + Chunk::aligned_overhead_size());
135
_tiny_pool = new ChunkPool(Chunk::tiny_size + Chunk::aligned_overhead_size());
136
}
137
138
static void clean() {
139
enum { BlocksToKeep = 5 };
140
_tiny_pool->free_all_but(BlocksToKeep);
141
_small_pool->free_all_but(BlocksToKeep);
142
_medium_pool->free_all_but(BlocksToKeep);
143
_large_pool->free_all_but(BlocksToKeep);
144
}
145
};
146
147
ChunkPool* ChunkPool::_large_pool = NULL;
148
ChunkPool* ChunkPool::_medium_pool = NULL;
149
ChunkPool* ChunkPool::_small_pool = NULL;
150
ChunkPool* ChunkPool::_tiny_pool = NULL;
151
152
void chunkpool_init() {
153
ChunkPool::initialize();
154
}
155
156
157
//--------------------------------------------------------------------------------------
158
// ChunkPoolCleaner implementation
159
//
160
161
class ChunkPoolCleaner : public PeriodicTask {
162
enum { CleaningInterval = 5000 }; // cleaning interval in ms
163
164
public:
165
ChunkPoolCleaner() : PeriodicTask(CleaningInterval) {}
166
void task() {
167
ChunkPool::clean();
168
}
169
};
170
171
//--------------------------------------------------------------------------------------
172
// Chunk implementation
173
174
void* Chunk::operator new (size_t requested_size, AllocFailType alloc_failmode, size_t length) throw() {
175
// requested_size is equal to sizeof(Chunk) but in order for the arena
176
// allocations to come out aligned as expected the size must be aligned
177
// to expected arena alignment.
178
// expect requested_size but if sizeof(Chunk) doesn't match isn't proper size we must align it.
179
assert(ARENA_ALIGN(requested_size) == aligned_overhead_size(), "Bad alignment");
180
size_t bytes = ARENA_ALIGN(requested_size) + length;
181
switch (length) {
182
case Chunk::size: return ChunkPool::large_pool()->allocate(bytes, alloc_failmode);
183
case Chunk::medium_size: return ChunkPool::medium_pool()->allocate(bytes, alloc_failmode);
184
case Chunk::init_size: return ChunkPool::small_pool()->allocate(bytes, alloc_failmode);
185
case Chunk::tiny_size: return ChunkPool::tiny_pool()->allocate(bytes, alloc_failmode);
186
default: {
187
void* p = os::malloc(bytes, mtChunk, CALLER_PC);
188
if (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) {
189
vm_exit_out_of_memory(bytes, OOM_MALLOC_ERROR, "Chunk::new");
190
}
191
return p;
192
}
193
}
194
}
195
196
void Chunk::operator delete(void* p) {
197
Chunk* c = (Chunk*)p;
198
switch (c->length()) {
199
case Chunk::size: ChunkPool::large_pool()->free(c); break;
200
case Chunk::medium_size: ChunkPool::medium_pool()->free(c); break;
201
case Chunk::init_size: ChunkPool::small_pool()->free(c); break;
202
case Chunk::tiny_size: ChunkPool::tiny_pool()->free(c); break;
203
default:
204
ThreadCritical tc; // Free chunks under TC lock so that NMT adjustment is stable.
205
os::free(c);
206
}
207
}
208
209
Chunk::Chunk(size_t length) : _len(length) {
210
_next = NULL; // Chain on the linked list
211
}
212
213
void Chunk::chop() {
214
Chunk *k = this;
215
while( k ) {
216
Chunk *tmp = k->next();
217
// clear out this chunk (to detect allocation bugs)
218
if (ZapResourceArea) memset(k->bottom(), badResourceValue, k->length());
219
delete k; // Free chunk (was malloc'd)
220
k = tmp;
221
}
222
}
223
224
void Chunk::next_chop() {
225
_next->chop();
226
_next = NULL;
227
}
228
229
void Chunk::start_chunk_pool_cleaner_task() {
230
#ifdef ASSERT
231
static bool task_created = false;
232
assert(!task_created, "should not start chuck pool cleaner twice");
233
task_created = true;
234
#endif
235
ChunkPoolCleaner* cleaner = new ChunkPoolCleaner();
236
cleaner->enroll();
237
}
238
239
//------------------------------Arena------------------------------------------
240
241
Arena::Arena(MEMFLAGS flag, size_t init_size) : _flags(flag), _size_in_bytes(0) {
242
size_t round_size = (sizeof (char *)) - 1;
243
init_size = (init_size+round_size) & ~round_size;
244
_first = _chunk = new (AllocFailStrategy::EXIT_OOM, init_size) Chunk(init_size);
245
_hwm = _chunk->bottom(); // Save the cached hwm, max
246
_max = _chunk->top();
247
MemTracker::record_new_arena(flag);
248
set_size_in_bytes(init_size);
249
}
250
251
Arena::Arena(MEMFLAGS flag) : _flags(flag), _size_in_bytes(0) {
252
_first = _chunk = new (AllocFailStrategy::EXIT_OOM, Chunk::init_size) Chunk(Chunk::init_size);
253
_hwm = _chunk->bottom(); // Save the cached hwm, max
254
_max = _chunk->top();
255
MemTracker::record_new_arena(flag);
256
set_size_in_bytes(Chunk::init_size);
257
}
258
259
Arena *Arena::move_contents(Arena *copy) {
260
copy->destruct_contents();
261
copy->_chunk = _chunk;
262
copy->_hwm = _hwm;
263
copy->_max = _max;
264
copy->_first = _first;
265
266
// workaround rare racing condition, which could double count
267
// the arena size by native memory tracking
268
size_t size = size_in_bytes();
269
set_size_in_bytes(0);
270
copy->set_size_in_bytes(size);
271
// Destroy original arena
272
reset();
273
return copy; // Return Arena with contents
274
}
275
276
Arena::~Arena() {
277
destruct_contents();
278
MemTracker::record_arena_free(_flags);
279
}
280
281
void* Arena::operator new(size_t size) throw() {
282
assert(false, "Use dynamic memory type binding");
283
return NULL;
284
}
285
286
void* Arena::operator new (size_t size, const std::nothrow_t& nothrow_constant) throw() {
287
assert(false, "Use dynamic memory type binding");
288
return NULL;
289
}
290
291
// dynamic memory type binding
292
void* Arena::operator new(size_t size, MEMFLAGS flags) throw() {
293
return (void *) AllocateHeap(size, flags, CALLER_PC);
294
}
295
296
void* Arena::operator new(size_t size, const std::nothrow_t& nothrow_constant, MEMFLAGS flags) throw() {
297
return (void*)AllocateHeap(size, flags, CALLER_PC, AllocFailStrategy::RETURN_NULL);
298
}
299
300
void Arena::operator delete(void* p) {
301
FreeHeap(p);
302
}
303
304
// Destroy this arenas contents and reset to empty
305
void Arena::destruct_contents() {
306
if (UseMallocOnly && _first != NULL) {
307
char* end = _first->next() ? _first->top() : _hwm;
308
free_malloced_objects(_first, _first->bottom(), end, _hwm);
309
}
310
// reset size before chop to avoid a rare racing condition
311
// that can have total arena memory exceed total chunk memory
312
set_size_in_bytes(0);
313
if (_first != NULL) {
314
_first->chop();
315
}
316
reset();
317
}
318
319
// This is high traffic method, but many calls actually don't
320
// change the size
321
void Arena::set_size_in_bytes(size_t size) {
322
if (_size_in_bytes != size) {
323
ssize_t delta = size - size_in_bytes();
324
_size_in_bytes = size;
325
MemTracker::record_arena_size_change(delta, _flags);
326
}
327
}
328
329
// Total of all Chunks in arena
330
size_t Arena::used() const {
331
size_t sum = _chunk->length() - (_max-_hwm); // Size leftover in this Chunk
332
Chunk *k = _first;
333
while( k != _chunk) { // Whilst have Chunks in a row
334
sum += k->length(); // Total size of this Chunk
335
k = k->next(); // Bump along to next Chunk
336
}
337
return sum; // Return total consumed space.
338
}
339
340
void Arena::signal_out_of_memory(size_t sz, const char* whence) const {
341
vm_exit_out_of_memory(sz, OOM_MALLOC_ERROR, "%s", whence);
342
}
343
344
// Grow a new Chunk
345
void* Arena::grow(size_t x, AllocFailType alloc_failmode) {
346
// Get minimal required size. Either real big, or even bigger for giant objs
347
size_t len = MAX2(x, (size_t) Chunk::size);
348
349
Chunk *k = _chunk; // Get filled-up chunk address
350
_chunk = new (alloc_failmode, len) Chunk(len);
351
352
if (_chunk == NULL) {
353
_chunk = k; // restore the previous value of _chunk
354
return NULL;
355
}
356
if (k) k->set_next(_chunk); // Append new chunk to end of linked list
357
else _first = _chunk;
358
_hwm = _chunk->bottom(); // Save the cached hwm, max
359
_max = _chunk->top();
360
set_size_in_bytes(size_in_bytes() + len);
361
void* result = _hwm;
362
_hwm += x;
363
return result;
364
}
365
366
367
368
// Reallocate storage in Arena.
369
void *Arena::Arealloc(void* old_ptr, size_t old_size, size_t new_size, AllocFailType alloc_failmode) {
370
if (new_size == 0) {
371
Afree(old_ptr, old_size); // like realloc(3)
372
return NULL;
373
}
374
if (old_ptr == NULL) {
375
assert(old_size == 0, "sanity");
376
return Amalloc(new_size, alloc_failmode); // as with realloc(3), a NULL old ptr is equivalent to malloc(3)
377
}
378
#ifdef ASSERT
379
if (UseMallocOnly) {
380
// always allocate a new object (otherwise we'll free this one twice)
381
char* copy = (char*)Amalloc(new_size, alloc_failmode);
382
if (copy == NULL) {
383
return NULL;
384
}
385
size_t n = MIN2(old_size, new_size);
386
if (n > 0) memcpy(copy, old_ptr, n);
387
Afree(old_ptr,old_size); // Mostly done to keep stats accurate
388
return copy;
389
}
390
#endif
391
char *c_old = (char*)old_ptr; // Handy name
392
// Stupid fast special case
393
if( new_size <= old_size ) { // Shrink in-place
394
if( c_old+old_size == _hwm) // Attempt to free the excess bytes
395
_hwm = c_old+new_size; // Adjust hwm
396
return c_old;
397
}
398
399
// make sure that new_size is legal
400
size_t corrected_new_size = ARENA_ALIGN(new_size);
401
402
// See if we can resize in-place
403
if( (c_old+old_size == _hwm) && // Adjusting recent thing
404
(c_old+corrected_new_size <= _max) ) { // Still fits where it sits
405
_hwm = c_old+corrected_new_size; // Adjust hwm
406
return c_old; // Return old pointer
407
}
408
409
// Oops, got to relocate guts
410
void *new_ptr = Amalloc(new_size, alloc_failmode);
411
if (new_ptr == NULL) {
412
return NULL;
413
}
414
memcpy( new_ptr, c_old, old_size );
415
Afree(c_old,old_size); // Mostly done to keep stats accurate
416
return new_ptr;
417
}
418
419
420
// Determine if pointer belongs to this Arena or not.
421
bool Arena::contains( const void *ptr ) const {
422
#ifdef ASSERT
423
if (UseMallocOnly) {
424
// really slow, but not easy to make fast
425
if (_chunk == NULL) return false;
426
char** bottom = (char**)_chunk->bottom();
427
for (char** p = (char**)_hwm - 1; p >= bottom; p--) {
428
if (*p == ptr) return true;
429
}
430
for (Chunk *c = _first; c != NULL; c = c->next()) {
431
if (c == _chunk) continue; // current chunk has been processed
432
char** bottom = (char**)c->bottom();
433
for (char** p = (char**)c->top() - 1; p >= bottom; p--) {
434
if (*p == ptr) return true;
435
}
436
}
437
return false;
438
}
439
#endif
440
if( (void*)_chunk->bottom() <= ptr && ptr < (void*)_hwm )
441
return true; // Check for in this chunk
442
for (Chunk *c = _first; c; c = c->next()) {
443
if (c == _chunk) continue; // current chunk has been processed
444
if ((void*)c->bottom() <= ptr && ptr < (void*)c->top()) {
445
return true; // Check for every chunk in Arena
446
}
447
}
448
return false; // Not in any Chunk, so not in Arena
449
}
450
451
452
#ifdef ASSERT
453
void* Arena::malloc(size_t size) {
454
assert(UseMallocOnly, "shouldn't call");
455
// use malloc, but save pointer in res. area for later freeing
456
char** save = (char**)internal_malloc_4(sizeof(char*));
457
return (*save = (char*)os::malloc(size, mtChunk));
458
}
459
460
// for debugging with UseMallocOnly
461
void* Arena::internal_malloc_4(size_t x) {
462
assert( (x&(sizeof(char*)-1)) == 0, "misaligned size" );
463
check_for_overflow(x, "Arena::internal_malloc_4");
464
if (_hwm + x > _max) {
465
return grow(x);
466
} else {
467
char *old = _hwm;
468
_hwm += x;
469
return old;
470
}
471
}
472
#endif
473
474
475
//--------------------------------------------------------------------------------------
476
// Non-product code
477
478
#ifndef PRODUCT
479
480
// debugging code
481
inline void Arena::free_all(char** start, char** end) {
482
for (char** p = start; p < end; p++) if (*p) os::free(*p);
483
}
484
485
void Arena::free_malloced_objects(Chunk* chunk, char* hwm, char* max, char* hwm2) {
486
assert(UseMallocOnly, "should not call");
487
// free all objects malloced since resource mark was created; resource area
488
// contains their addresses
489
if (chunk->next()) {
490
// this chunk is full, and some others too
491
for (Chunk* c = chunk->next(); c != NULL; c = c->next()) {
492
char* top = c->top();
493
if (c->next() == NULL) {
494
top = hwm2; // last junk is only used up to hwm2
495
assert(c->contains(hwm2), "bad hwm2");
496
}
497
free_all((char**)c->bottom(), (char**)top);
498
}
499
assert(chunk->contains(hwm), "bad hwm");
500
assert(chunk->contains(max), "bad max");
501
free_all((char**)hwm, (char**)max);
502
} else {
503
// this chunk was partially used
504
assert(chunk->contains(hwm), "bad hwm");
505
assert(chunk->contains(hwm2), "bad hwm2");
506
free_all((char**)hwm, (char**)hwm2);
507
}
508
}
509
510
#endif // Non-product
511
512