Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/hotspot/share/services/mallocSiteTable.cpp
41145 views
1
/*
2
* Copyright (c) 2014, 2019, 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
27
#include "memory/allocation.inline.hpp"
28
#include "runtime/atomic.hpp"
29
#include "services/mallocSiteTable.hpp"
30
31
// Malloc site hashtable buckets
32
MallocSiteHashtableEntry* MallocSiteTable::_table[MallocSiteTable::table_size];
33
const NativeCallStack* MallocSiteTable::_hash_entry_allocation_stack = NULL;
34
const MallocSiteHashtableEntry* MallocSiteTable::_hash_entry_allocation_site = NULL;
35
36
// concurrent access counter
37
volatile int MallocSiteTable::_access_count = 0;
38
39
// Tracking hashtable contention
40
NOT_PRODUCT(int MallocSiteTable::_peak_count = 0;)
41
42
43
/*
44
* Initialize malloc site table.
45
* Hashtable entry is malloc'd, so it can cause infinite recursion.
46
* To avoid above problem, we pre-initialize a hash entry for
47
* this allocation site.
48
* The method is called during C runtime static variable initialization
49
* time, it is in single-threaded mode from JVM perspective.
50
*/
51
bool MallocSiteTable::initialize() {
52
assert((size_t)table_size <= MAX_MALLOCSITE_TABLE_SIZE, "Hashtable overflow");
53
54
// Fake the call stack for hashtable entry allocation
55
assert(NMT_TrackingStackDepth > 1, "At least one tracking stack");
56
57
// Create pseudo call stack for hashtable entry allocation
58
address pc[3];
59
if (NMT_TrackingStackDepth >= 3) {
60
uintx *fp = (uintx*)MallocSiteTable::allocation_at;
61
// On ppc64, 'fp' is a pointer to a function descriptor which is a struct of
62
// three native pointers where the first pointer is the real function address.
63
// See: http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html#FUNC-DES
64
pc[2] = (address)(fp PPC64_ONLY(BIG_ENDIAN_ONLY([0])));
65
}
66
if (NMT_TrackingStackDepth >= 2) {
67
uintx *fp = (uintx*)MallocSiteTable::lookup_or_add;
68
pc[1] = (address)(fp PPC64_ONLY(BIG_ENDIAN_ONLY([0])));
69
}
70
uintx *fp = (uintx*)MallocSiteTable::new_entry;
71
pc[0] = (address)(fp PPC64_ONLY(BIG_ENDIAN_ONLY([0])));
72
73
static const NativeCallStack stack(pc, MIN2(((int)(sizeof(pc) / sizeof(address))), ((int)NMT_TrackingStackDepth)));
74
static const MallocSiteHashtableEntry entry(stack, mtNMT);
75
76
assert(_hash_entry_allocation_stack == NULL &&
77
_hash_entry_allocation_site == NULL,
78
"Already initailized");
79
80
_hash_entry_allocation_stack = &stack;
81
_hash_entry_allocation_site = &entry;
82
83
// Add the allocation site to hashtable.
84
int index = hash_to_index(entry.hash());
85
_table[index] = const_cast<MallocSiteHashtableEntry*>(&entry);
86
87
return true;
88
}
89
90
// Walks entries in the hashtable.
91
// It stops walk if the walker returns false.
92
bool MallocSiteTable::walk(MallocSiteWalker* walker) {
93
MallocSiteHashtableEntry* head;
94
for (int index = 0; index < table_size; index ++) {
95
head = _table[index];
96
while (head != NULL) {
97
if (!walker->do_malloc_site(head->peek())) {
98
return false;
99
}
100
head = (MallocSiteHashtableEntry*)head->next();
101
}
102
}
103
return true;
104
}
105
106
/*
107
* The hashtable does not have deletion policy on individual entry,
108
* and each linked list node is inserted via compare-and-swap,
109
* so each linked list is stable, the contention only happens
110
* at the end of linked list.
111
* This method should not return NULL under normal circumstance.
112
* If NULL is returned, it indicates:
113
* 1. Out of memory, it cannot allocate new hash entry.
114
* 2. Overflow hash bucket.
115
* Under any of above circumstances, caller should handle the situation.
116
*/
117
MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, size_t* bucket_idx,
118
size_t* pos_idx, MEMFLAGS flags) {
119
assert(flags != mtNone, "Should have a real memory type");
120
const unsigned int hash = key.calculate_hash();
121
const unsigned int index = hash_to_index(hash);
122
*bucket_idx = (size_t)index;
123
*pos_idx = 0;
124
125
// First entry for this hash bucket
126
if (_table[index] == NULL) {
127
MallocSiteHashtableEntry* entry = new_entry(key, flags);
128
// OOM check
129
if (entry == NULL) return NULL;
130
131
// swap in the head
132
if (Atomic::replace_if_null(&_table[index], entry)) {
133
return entry->data();
134
}
135
136
delete entry;
137
}
138
139
MallocSiteHashtableEntry* head = _table[index];
140
while (head != NULL && (*pos_idx) <= MAX_BUCKET_LENGTH) {
141
if (head->hash() == hash) {
142
MallocSite* site = head->data();
143
if (site->flag() == flags && site->equals(key)) {
144
return head->data();
145
}
146
}
147
148
if (head->next() == NULL && (*pos_idx) < MAX_BUCKET_LENGTH) {
149
MallocSiteHashtableEntry* entry = new_entry(key, flags);
150
// OOM check
151
if (entry == NULL) return NULL;
152
if (head->atomic_insert(entry)) {
153
(*pos_idx) ++;
154
return entry->data();
155
}
156
// contended, other thread won
157
delete entry;
158
}
159
head = (MallocSiteHashtableEntry*)head->next();
160
(*pos_idx) ++;
161
}
162
return NULL;
163
}
164
165
// Access malloc site
166
MallocSite* MallocSiteTable::malloc_site(size_t bucket_idx, size_t pos_idx) {
167
assert(bucket_idx < table_size, "Invalid bucket index");
168
MallocSiteHashtableEntry* head = _table[bucket_idx];
169
for (size_t index = 0;
170
index < pos_idx && head != NULL;
171
index++, head = (MallocSiteHashtableEntry*)head->next()) {}
172
assert(head != NULL, "Invalid position index");
173
return head->data();
174
}
175
176
// Allocates MallocSiteHashtableEntry object. Special call stack
177
// (pre-installed allocation site) has to be used to avoid infinite
178
// recursion.
179
MallocSiteHashtableEntry* MallocSiteTable::new_entry(const NativeCallStack& key, MEMFLAGS flags) {
180
void* p = AllocateHeap(sizeof(MallocSiteHashtableEntry), mtNMT,
181
*hash_entry_allocation_stack(), AllocFailStrategy::RETURN_NULL);
182
return ::new (p) MallocSiteHashtableEntry(key, flags);
183
}
184
185
void MallocSiteTable::reset() {
186
for (int index = 0; index < table_size; index ++) {
187
MallocSiteHashtableEntry* head = _table[index];
188
_table[index] = NULL;
189
delete_linked_list(head);
190
}
191
192
_hash_entry_allocation_stack = NULL;
193
_hash_entry_allocation_site = NULL;
194
}
195
196
void MallocSiteTable::delete_linked_list(MallocSiteHashtableEntry* head) {
197
MallocSiteHashtableEntry* p;
198
while (head != NULL) {
199
p = head;
200
head = (MallocSiteHashtableEntry*)head->next();
201
if (p != hash_entry_allocation_site()) {
202
delete p;
203
}
204
}
205
}
206
207
void MallocSiteTable::shutdown() {
208
AccessLock locker(&_access_count);
209
locker.exclusiveLock();
210
reset();
211
}
212
213
bool MallocSiteTable::walk_malloc_site(MallocSiteWalker* walker) {
214
assert(walker != NULL, "NuLL walker");
215
AccessLock locker(&_access_count);
216
if (locker.sharedLock()) {
217
NOT_PRODUCT(_peak_count = MAX2(_peak_count, _access_count);)
218
return walk(walker);
219
}
220
return false;
221
}
222
223
224
void MallocSiteTable::AccessLock::exclusiveLock() {
225
int target;
226
int val;
227
228
assert(_lock_state != ExclusiveLock, "Can only call once");
229
assert(*_lock >= 0, "Can not content exclusive lock");
230
231
// make counter negative to block out shared locks
232
do {
233
val = *_lock;
234
target = _MAGIC_ + *_lock;
235
} while (Atomic::cmpxchg(_lock, val, target) != val);
236
237
// wait for all readers to exit
238
while (*_lock != _MAGIC_) {
239
#ifdef _WINDOWS
240
os::naked_short_sleep(1);
241
#else
242
os::naked_yield();
243
#endif
244
}
245
_lock_state = ExclusiveLock;
246
}
247
248
void MallocSiteTable::print_tuning_statistics(outputStream* st) {
249
250
AccessLock locker(&_access_count);
251
if (locker.sharedLock()) {
252
// Total number of allocation sites, include empty sites
253
int total_entries = 0;
254
// Number of allocation sites that have all memory freed
255
int empty_entries = 0;
256
// Number of captured call stack distribution
257
int stack_depth_distribution[NMT_TrackingStackDepth + 1] = { 0 };
258
// Chain lengths
259
int lengths[table_size] = { 0 };
260
261
for (int i = 0; i < table_size; i ++) {
262
int this_chain_length = 0;
263
const MallocSiteHashtableEntry* head = _table[i];
264
while (head != NULL) {
265
total_entries ++;
266
this_chain_length ++;
267
if (head->size() == 0) {
268
empty_entries ++;
269
}
270
const int callstack_depth = head->peek()->call_stack()->frames();
271
assert(callstack_depth >= 0 && callstack_depth <= NMT_TrackingStackDepth,
272
"Sanity (%d)", callstack_depth);
273
stack_depth_distribution[callstack_depth] ++;
274
head = head->next();
275
}
276
lengths[i] = this_chain_length;
277
}
278
279
st->print_cr("Malloc allocation site table:");
280
st->print_cr("\tTotal entries: %d", total_entries);
281
st->print_cr("\tEmpty entries: %d (%2.2f%%)", empty_entries, ((float)empty_entries * 100) / total_entries);
282
st->cr();
283
284
// We report the hash distribution (chain length distribution) of the n shortest chains
285
// - under the assumption that this usually contains all lengths. Reporting threshold
286
// is 20, and the expected avg chain length is 5..6 (see table size).
287
static const int chain_length_threshold = 20;
288
int chain_length_distribution[chain_length_threshold] = { 0 };
289
int over_threshold = 0;
290
int longest_chain_length = 0;
291
for (int i = 0; i < table_size; i ++) {
292
if (lengths[i] >= chain_length_threshold) {
293
over_threshold ++;
294
} else {
295
chain_length_distribution[lengths[i]] ++;
296
}
297
longest_chain_length = MAX2(longest_chain_length, lengths[i]);
298
}
299
300
st->print_cr("Hash distribution:");
301
if (chain_length_distribution[0] == 0) {
302
st->print_cr("no empty buckets.");
303
} else {
304
st->print_cr("%d buckets are empty.", chain_length_distribution[0]);
305
}
306
for (int len = 1; len < MIN2(longest_chain_length + 1, chain_length_threshold); len ++) {
307
st->print_cr("%2d %s: %d.", len, (len == 1 ? " entry" : "entries"), chain_length_distribution[len]);
308
}
309
if (longest_chain_length >= chain_length_threshold) {
310
st->print_cr(">=%2d entries: %d.", chain_length_threshold, over_threshold);
311
}
312
st->print_cr("most entries: %d.", longest_chain_length);
313
st->cr();
314
315
st->print_cr("Call stack depth distribution:");
316
for (int i = 0; i <= NMT_TrackingStackDepth; i ++) {
317
st->print_cr("\t%d: %d", i, stack_depth_distribution[i]);
318
}
319
st->cr();
320
} // lock
321
}
322
323
324
bool MallocSiteHashtableEntry::atomic_insert(MallocSiteHashtableEntry* entry) {
325
return Atomic::replace_if_null(&_next, entry);
326
}
327
328