Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/gc_trace/TgcScavenger.cpp
5985 views
1
/*******************************************************************************
2
* Copyright (c) 1991, 2019 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21
*******************************************************************************/
22
23
#include "j9.h"
24
#include "j9cfg.h"
25
#include "j9port.h"
26
#include "Tgc.hpp"
27
#include "mmhook.h"
28
29
#if defined(J9VM_GC_MODRON_SCAVENGER)
30
#include "GCExtensions.hpp"
31
#include "MemorySubSpace.hpp"
32
#include "MemorySubSpaceRegionIterator.hpp"
33
#include "TgcExtensions.hpp"
34
#include "HeapRegionDescriptor.hpp"
35
#include "MemorySpace.hpp"
36
#include "ScavengerStats.hpp"
37
#include "ObjectHeapBufferedIterator.hpp"
38
39
#define MAX_AGE (OBJECT_HEADER_AGE_MAX+1)
40
41
/**
42
* Structure to hold details of occurrences
43
* of a class within the survivor space
44
*/
45
typedef struct ClassEntry {
46
struct ClassEntry *next;
47
48
J9Class *clazz;
49
UDATA count[MAX_AGE];
50
} ClassEntry;
51
52
/**
53
* Find the occurrence of clazz inside classList
54
* (if one exists)
55
*/
56
static ClassEntry *
57
findClassInList(ClassEntry *classList, J9Class *clazz)
58
{
59
ClassEntry *mover;
60
61
for(mover = classList; mover; mover = mover->next) {
62
if(mover->clazz == clazz) {
63
return mover;
64
}
65
}
66
67
/* we haven't found the class in the list */
68
return NULL;
69
}
70
71
/**
72
* Add a new ClassEntry object to the head of
73
* classList, setting its class to clazz
74
*/
75
static ClassEntry *
76
addClassEntry(J9VMThread *vmThread, ClassEntry *classList, J9Class *clazz, UDATA age)
77
{
78
MM_Forge *forge = MM_GCExtensions::getExtensions(vmThread->javaVM)->getForge();
79
80
ClassEntry *newEntry = (ClassEntry *) forge->allocate((UDATA) sizeof(ClassEntry), MM_AllocationCategory::DIAGNOSTIC, J9_GET_CALLSITE());
81
if (newEntry) {
82
memset(newEntry, 0, sizeof(ClassEntry));
83
} else {
84
/* we have failed to allocate */
85
return NULL;
86
}
87
88
/* add to the head of the list */
89
newEntry->next = classList;
90
91
/* setup the contents */
92
newEntry->clazz = clazz;
93
newEntry->count[age] = 1;
94
95
return newEntry;
96
}
97
98
/**
99
* Count the number of objects within a classEntry
100
*/
101
static UDATA
102
countObjects(ClassEntry *classEntry)
103
{
104
UDATA count = 0, i;
105
106
for(i=0; i<MAX_AGE; i++) {
107
count += classEntry->count[i];
108
}
109
return count;
110
}
111
112
/**
113
* Print a histogram of the objects using classList
114
*/
115
static void
116
printHistogram(J9VMThread *vmThread, ClassEntry *classList)
117
{
118
ClassEntry *mover;
119
UDATA objectCount = 0, i;
120
MM_TgcExtensions *tgcExtensions = MM_TgcExtensions::getExtensions(vmThread);
121
122
tgcExtensions->printf("\n{SCAV: tgcScavenger OBJECT HISTOGRAM}\n");
123
tgcExtensions->printf("\n{SCAV: | class | instances of age 0-%zu in semi-space |\n", (UDATA)OBJECT_HEADER_AGE_MAX);
124
mover = classList;
125
126
while(mover) {
127
/* print the results for that class */
128
tgcExtensions->printf("{SCAV: ");
129
tgcPrintClass(vmThread->javaVM, mover->clazz);
130
for(i=0; i<MAX_AGE; i++) {
131
tgcExtensions->printf(" %zu", mover->count[i]);
132
}
133
tgcExtensions->printf("\n");
134
/* update the total object count */
135
objectCount += countObjects(mover);
136
137
/* get the next class */
138
mover = mover->next;
139
}
140
tgcExtensions->printf("{SCAV: Total objects in semispace = \"%zu\"\n\n", objectCount);
141
142
}
143
144
/**
145
* Free all the memory associated with classList
146
*/
147
static void
148
deleteClassList(J9VMThread *vmThread, ClassEntry *classList)
149
{
150
MM_Forge *forge = MM_GCExtensions::getExtensions(vmThread->javaVM)->getForge();
151
ClassEntry *entry, *mover;
152
153
if(!classList) {
154
return;
155
}
156
157
entry = classList;
158
159
while(entry) {
160
mover = entry->next;
161
forge->free(entry);
162
entry = mover;
163
}
164
}
165
166
/**
167
* Report object histogram following scavenger collection.
168
* When tgcScavenger is enabled, this function is called following each
169
* scavenger collection. The function walks the survivor space keeping tally
170
* of each class of object it encounters, and the relative age of each
171
* instance of that object (how many times it has been copied). The information
172
* is output as a simple histogram suitable for loading into a spreadsheet
173
* or similar for further analysis.
174
*/
175
static void
176
tgcHookScavengerReportObjectHistogram(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData)
177
{
178
MM_LocalGCEndEvent* event = (MM_LocalGCEndEvent*)eventData;
179
J9VMThread *vmThread = (J9VMThread*)event->currentThread->_language_vmthread;
180
J9JavaVM *javaVM = vmThread->javaVM;
181
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(javaVM);
182
MM_TgcExtensions *tgcExtensions = MM_TgcExtensions::getExtensions(extensions);
183
bool shouldReport = false;
184
185
ClassEntry *classList = NULL;
186
MM_MemorySubSpace *subspace = ((MM_MemorySubSpace *)event->subSpace)->getDefaultMemorySubSpace();
187
GC_MemorySubSpaceRegionIterator regionIterator(subspace);
188
MM_HeapRegionDescriptor *region = NULL;
189
190
while(NULL != (region = regionIterator.nextRegion())) {
191
GC_ObjectHeapBufferedIterator objectHeapIterator(extensions, region);
192
J9Object *object = NULL;
193
194
shouldReport = true;
195
while(NULL != (object = objectHeapIterator.nextObject())) {
196
/* iterate over objects in the segment */
197
UDATA objectAge = extensions->objectModel.getObjectAge(object);
198
ClassEntry *entry = findClassInList(classList, J9GC_J9OBJECT_CLAZZ(object, extensions));
199
if(NULL != entry) {
200
/* increment the appropriate struct */
201
entry->count[objectAge] += 1;
202
} else {
203
/* add it to the list */
204
entry = addClassEntry(vmThread, classList, J9GC_J9OBJECT_CLAZZ(object, extensions), objectAge);
205
if(!entry) {
206
/* whoops! */
207
tgcExtensions->printf("Failed to allocate for histogram!\n");
208
deleteClassList(vmThread, classList);
209
return;
210
}
211
/* reset the classList to point at the new head */
212
classList = entry;
213
}
214
}
215
}
216
217
if (shouldReport) {
218
/* done iterating - print out the results */
219
printHistogram(vmThread, classList);
220
}
221
/* free all the memory we allocated earlier */
222
deleteClassList(vmThread, classList);
223
}
224
225
226
static void
227
tgcHookScavengerFlipSizeHistogram(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData)
228
{
229
MM_ScavengeEndEvent* event = (MM_ScavengeEndEvent*)eventData;
230
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(event->currentThread);
231
MM_TgcExtensions *tgcExtensions = MM_TgcExtensions::getExtensions(extensions);
232
233
tgcExtensions->printf("Scavenger Copy Bytes by Object Age:\n ");
234
for (IDATA i = -1; i <= OBJECT_HEADER_AGE_MAX; ++i) {
235
tgcExtensions->printf(" %9zi ", i);
236
}
237
tgcExtensions->printf("\n_");
238
for (UDATA i = 0; i <= OBJECT_HEADER_AGE_MAX+1; ++i) {
239
tgcExtensions->printf("___________");
240
}
241
tgcExtensions->printf("_\n");
242
243
MM_ScavengerStats *stats = &extensions->scavengerStats;
244
for (UDATA j = 0; j < SCAVENGER_FLIP_HISTORY_SIZE; ++j) {
245
tgcExtensions->printf(" ");
246
247
/* Flipped bytes */
248
for (UDATA i = 0; i <= OBJECT_HEADER_AGE_MAX+1; ++i) {
249
/* Blank out the top left table elements because data will not be populated for this cell until the next scavenge. */
250
if ((0 == j) && (0 == i)) {
251
tgcExtensions->printf(" ");
252
} else {
253
char tenureChar = ' ';
254
if (i > 0 && (0 != (((UDATA)1 << (i-1)) & stats->getFlipHistory(j)->_tenureMask))) {
255
tenureChar = '*';
256
}
257
tgcExtensions->printf(" %9zu%c", stats->getFlipHistory(j)->_flipBytes[i], tenureChar);
258
}
259
}
260
tgcExtensions->printf(" \n ");
261
262
/* Tenured bytes */
263
for (UDATA i = 0; i <= OBJECT_HEADER_AGE_MAX+1; ++i) {
264
/* Blank out the top left table elements because data will not be populated for this cell until the next scavenge. */
265
if ((0 == j) && (0 == i)) {
266
tgcExtensions->printf(" ");
267
} else {
268
tgcExtensions->printf(" %9zu ", stats->getFlipHistory(j)->_tenureBytes[i]);
269
}
270
}
271
tgcExtensions->printf(" \n____________");
272
273
/* Survival rate */
274
for (UDATA i = 1; i <= OBJECT_HEADER_AGE_MAX+1; ++i) {
275
if (j < SCAVENGER_FLIP_HISTORY_SIZE - 1) {
276
UDATA currentFlipBytes = stats->getFlipHistory(j)->_flipBytes[i];
277
UDATA currentTenureBytes = stats->getFlipHistory(j)->_tenureBytes[i];
278
UDATA previousFlipBytes = stats->getFlipHistory(j+1)->_flipBytes[i-1];
279
280
double survivalPercentage = 0.0;
281
if (previousFlipBytes != 0) {
282
survivalPercentage = ((double)(currentFlipBytes + currentTenureBytes) / (double)previousFlipBytes) * 100.0;
283
}
284
285
const char* underscorePadding = "";
286
if (99.9995 <= survivalPercentage) {
287
underscorePadding = "_";
288
} else if (9.9995 <= survivalPercentage) {
289
underscorePadding = "__";
290
} else {
291
underscorePadding = "___";
292
}
293
294
tgcExtensions->printf("__%s%.3lf%%", underscorePadding, survivalPercentage);
295
} else {
296
tgcExtensions->printf("___________");
297
}
298
}
299
tgcExtensions->printf("_\n");
300
}
301
}
302
303
static void
304
tgcHookScavengerDiscardedSpace(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData)
305
{
306
MM_ScavengeEndEvent* event = (MM_ScavengeEndEvent*)eventData;
307
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(event->currentThread);
308
MM_TgcExtensions *tgcExtensions = MM_TgcExtensions::getExtensions(extensions);
309
310
MM_ScavengerStats *stats = &extensions->scavengerStats;
311
312
tgcExtensions->printf("\n");
313
/* If _survivorTLHRemainderCount/_tenureTLHRemainderCount gets too high (in 1000s),
314
* it may indicate that discard thresholds are too low (and possibly causing contention while popping/pushing scan queue
315
*/
316
tgcExtensions->printf("Scavenger flipped=%zu discard=%zu TLHRemainderReuse=%zu\n", stats->_flipBytes, stats->_flipDiscardBytes, stats->_survivorTLHRemainderCount);
317
tgcExtensions->printf("Scavenger tenured=%zu discard=%zu TLHRemainderReuse=%zu\n", stats->_tenureAggregateBytes, stats->_tenureDiscardBytes, stats->_tenureTLHRemainderCount);
318
}
319
320
static void
321
tgcHookScavengerAllocationPaths(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData)
322
{
323
MM_ScavengeEndEvent* event = (MM_ScavengeEndEvent*)eventData;
324
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(event->currentThread);
325
MM_TgcExtensions *tgcExtensions = MM_TgcExtensions::getExtensions(extensions);
326
327
MM_ScavengerStats *stats = &extensions->scavengerStats;
328
tgcExtensions->printf("\n");
329
tgcExtensions->printf("Scavenger semi space allocation path: large=%zu, small=%zu\n", stats->_semiSpaceAllocationCountLarge, stats->_semiSpaceAllocationCountSmall);
330
tgcExtensions->printf("Scavenger tenure space allocation path: large=%zu, small=%zu\n", stats->_tenureSpaceAllocationCountLarge, stats->_tenureSpaceAllocationCountSmall);
331
tgcExtensions->printf("\n");
332
}
333
334
/**
335
* Initialize scavenger tgc tracing.
336
* Initializes the TgcScavengerExtensions object associated with scavenger tgc tracing. Attaches hooks
337
* to the appropriate functions handling events used by scavenger tgc tracing.
338
*/
339
bool
340
tgcScavengerInitialize(J9JavaVM *javaVM)
341
{
342
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(javaVM);
343
bool result = true;
344
345
J9HookInterface** omrHooks = J9_HOOK_INTERFACE(extensions->omrHookInterface);
346
/* Object histogram is expensive, but for legacy reasons we still support it. Other, cheaper, scavenger stats have been
347
* given special options, but also reported through this generic scavenge tgc options. */
348
(*omrHooks)->J9HookRegisterWithCallSite(omrHooks, J9HOOK_MM_OMR_LOCAL_GC_END, tgcHookScavengerReportObjectHistogram, OMR_GET_CALLSITE(), NULL);
349
350
return result;
351
}
352
353
/**
354
* Initialize scavenger copied objects tgc tracing.
355
* Attaches hooks to the appropriate functions handling events used by scavenger copied object tgc tracing.
356
*/
357
bool
358
tgcScavengerSurvivalStatsInitialize(J9JavaVM *javaVM)
359
{
360
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(javaVM);
361
bool result = true;
362
363
J9HookInterface** privateHooks = J9_HOOK_INTERFACE(extensions->privateHookInterface);
364
(*privateHooks)->J9HookRegisterWithCallSite(privateHooks, J9HOOK_MM_PRIVATE_SCAVENGE_END, tgcHookScavengerFlipSizeHistogram, OMR_GET_CALLSITE(), NULL);
365
366
return result;
367
}
368
369
/**
370
* Initialize scavenger allocate and discard stats
371
* Attaches hooks to the appropriate functions handling events used by scavenger allocate and discard tgc tracing.
372
*/
373
bool
374
tgcScavengerMemoryStatsInitialize(J9JavaVM *javaVM)
375
{
376
MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(javaVM);
377
bool result = true;
378
379
J9HookInterface** privateHooks = J9_HOOK_INTERFACE(extensions->privateHookInterface);
380
(*privateHooks)->J9HookRegisterWithCallSite(privateHooks, J9HOOK_MM_PRIVATE_SCAVENGE_END, tgcHookScavengerDiscardedSpace, OMR_GET_CALLSITE(), NULL);
381
(*privateHooks)->J9HookRegisterWithCallSite(privateHooks, J9HOOK_MM_PRIVATE_SCAVENGE_END, tgcHookScavengerAllocationPaths, OMR_GET_CALLSITE(), NULL);
382
383
return result;
384
}
385
386
#endif /* J9VM_GC_MODRON_SCAVENGER */
387
388