Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/gc_realtime/RealtimeRootScanner.cpp
5985 views
1
/*******************************************************************************
2
* Copyright (c) 1991, 2021 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 "j9protos.h"
26
#include "j9consts.h"
27
#include "modronopt.h"
28
29
#include <string.h>
30
31
#include "ClassModel.hpp"
32
#include "EnvironmentBase.hpp"
33
#if defined(J9VM_GC_FINALIZATION)
34
#include "FinalizeListManager.hpp"
35
#include "FinalizerSupport.hpp"
36
#endif /* J9VM_GC_FINALIZATION */
37
#include "Heap.hpp"
38
#include "MemoryPoolSegregated.hpp"
39
#include "MemorySubSpace.hpp"
40
#include "modronapi.hpp"
41
#include "ObjectModel.hpp"
42
#include "ParallelDispatcher.hpp"
43
#include "RealtimeMarkingScheme.hpp"
44
#include "RealtimeRootScanner.hpp"
45
#include "RootScanner.hpp"
46
#include "Scheduler.hpp"
47
#include "SublistSlotIterator.hpp"
48
#include "Task.hpp"
49
50
void
51
MM_RealtimeRootScanner::doClass(J9Class *clazz)
52
{
53
GC_ClassIterator objectSlotIterator(_env, clazz);
54
volatile j9object_t *objectSlotPtr = NULL;
55
while((objectSlotPtr = objectSlotIterator.nextSlot()) != NULL) {
56
/* discard volatile since we must be in stop-the-world mode */
57
doSlot((j9object_t*)objectSlotPtr);
58
}
59
GC_ClassIteratorClassSlots classSlotIterator(static_cast<J9JavaVM*>(_omrVM->_language_vm), clazz);
60
J9Class *classPtr;
61
while (NULL != (classPtr = classSlotIterator.nextSlot())) {
62
doClassSlot(classPtr);
63
}
64
}
65
66
/* Handle a classSlot. This handler is called for every reference to a J9Class.
67
*
68
*/
69
void
70
MM_RealtimeRootScanner::doClassSlot(J9Class *classPtr)
71
{
72
_realtimeGC->getRealtimeDelegate()->markClass(_env, classPtr);
73
}
74
75
MM_RootScanner::CompletePhaseCode
76
MM_RealtimeRootScanner::scanClassesComplete(MM_EnvironmentBase *env)
77
{
78
/* TODO: consider reactivating this call */
79
// reportScanningStarted(RootScannerEntity_ClassesComplete);
80
// _realtimeGC->completeMarking(_env);
81
// reportScanningEnded(RootScannerEntity_ClassesComplete);
82
return complete_phase_OK;
83
}
84
85
/**
86
* This function iterates through all the threads, calling scanOneThread on each one that
87
* should be scanned. The scanOneThread function scans exactly one thread and returns
88
* either true (if it took an action that requires the thread list iterator to return to
89
* the beginning) or false (if the thread list iterator should just continue with the next
90
* thread).
91
*/
92
void
93
MM_RealtimeRootScanner::scanThreads(MM_EnvironmentBase *env)
94
{
95
reportScanningStarted(RootScannerEntity_Threads);
96
97
GC_VMThreadListIterator vmThreadListIterator(static_cast<J9JavaVM*>(_omrVM->_language_vm));
98
StackIteratorData localData;
99
100
localData.rootScanner = this;
101
localData.env = env;
102
103
while(J9VMThread *walkThread = vmThreadListIterator.nextVMThread()) {
104
MM_EnvironmentRealtime* walkThreadEnv = MM_EnvironmentRealtime::getEnvironment(walkThread->omrVMThread);
105
if (GC_UNMARK == walkThreadEnv->_allocationColor) {
106
if (GC_UNMARK == MM_AtomicOperations::lockCompareExchangeU32(&walkThreadEnv->_allocationColor, GC_UNMARK, GC_MARK)) {
107
if (scanOneThread(env, walkThread, (void*) &localData)) {
108
vmThreadListIterator.reset(static_cast<J9JavaVM*>(_omrVM->_language_vm)->mainThread);
109
}
110
}
111
}
112
}
113
114
reportScanningEnded(RootScannerEntity_Threads);
115
}
116
117
/**
118
* The following override of scanOneThread performs metronome-specific processing before
119
* and after the scanning of each thread. Scanning is skipped if the thread has already
120
* been scanned in this cycle.
121
**/
122
bool
123
MM_RealtimeRootScanner::scanOneThread(MM_EnvironmentBase *envBase, J9VMThread* walkThread, void* localData)
124
{
125
MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(envBase);
126
127
scanOneThreadImpl(env, walkThread, localData);
128
129
/* Thead count is used under verbose only.
130
* Avoid the atomic add in the regular path.
131
*/
132
if (_realtimeGC->_sched->verbose() >= 3) {
133
MM_AtomicOperations::add(&_threadCount, 1);
134
}
135
136
if (condYield()) {
137
/* Optionally issue verbose message */
138
if (_realtimeGC->_sched->verbose() >= 3) {
139
PORT_ACCESS_FROM_ENVIRONMENT(env);
140
j9tty_printf(PORTLIB, "Yielded during %s after scanning %d threads\n", scannerName(), _threadCount);
141
}
142
143
return true;
144
}
145
146
return false;
147
}
148
149
void
150
MM_RealtimeRootScanner::scanOneThreadImpl(MM_EnvironmentRealtime *env, J9VMThread* walkThread, void* localData)
151
{
152
}
153
154
void
155
MM_RealtimeRootScanner::reportThreadCount(MM_EnvironmentBase* env)
156
{
157
PORT_ACCESS_FROM_ENVIRONMENT(env);
158
j9tty_printf(PORTLIB, "Scanned %d threads for %s\n", _threadCount, scannerName());
159
}
160
161
void
162
MM_RealtimeRootScanner::scanAtomicRoots(MM_EnvironmentRealtime *env)
163
{
164
if (_classDataAsRoots || _nurseryReferencesOnly || _nurseryReferencesPossibly) {
165
/* The classLoaderObject of a class loader might be in the nursery, but a class loader
166
* can never be in the remembered set, so include class loaders here.
167
*/
168
scanClassLoaders(env);
169
}
170
171
scanJNIGlobalReferences(env);
172
173
if(_stringTableAsRoot && (!_nurseryReferencesOnly && !_nurseryReferencesPossibly)){
174
scanStringTable(env);
175
}
176
}
177
178
void
179
MM_RealtimeRootScanner::doStringTableSlot(J9Object **slotPtr, GC_StringTableIterator *stringTableIterator)
180
{
181
_env->getGCEnvironment()->_markJavaStats._stringConstantsCandidates += 1;
182
if(!_markingScheme->isMarked(*slotPtr)) {
183
_env->getGCEnvironment()->_markJavaStats._stringConstantsCleared += 1;
184
stringTableIterator->removeSlot();
185
}
186
}
187
188
/**
189
* @Clear the string table cache slot if the object is not marked
190
*/
191
void
192
MM_RealtimeRootScanner::doStringCacheTableSlot(J9Object **slotPtr)
193
{
194
J9Object *objectPtr = *slotPtr;
195
if((NULL != objectPtr) && (!_markingScheme->isMarked(*slotPtr))) {
196
*slotPtr = NULL;
197
}
198
}
199
200
/**
201
* Calls the Scheduler's yielding API to determine if the GC should yield.
202
* @return true if the GC should yield, false otherwise
203
*/
204
bool
205
MM_RealtimeRootScanner::shouldYield()
206
{
207
return _realtimeGC->_sched->shouldGCYield(_env, 0);
208
}
209
210
/**
211
* Calls the Scheduler's yielding API only if timeSlackNanoSec is non-zero or
212
* if the yield count has reached 0.
213
* @return true if the GC should yield, false otherwise
214
*/
215
bool
216
MM_RealtimeRootScanner::shouldYieldFromClassScan(UDATA timeSlackNanoSec)
217
{
218
_yieldCount--;
219
if (_yieldCount < 0 || timeSlackNanoSec != 0) {
220
if (_realtimeGC->_sched->shouldGCYield(_env, 0)) {
221
return true;
222
}
223
_yieldCount = ROOT_GRANULARITY;
224
}
225
return false;
226
}
227
228
bool
229
MM_RealtimeRootScanner::shouldYieldFromStringScan()
230
{
231
_yieldCount--;
232
if (_yieldCount < 0) {
233
if (_realtimeGC->_sched->shouldGCYield(_env, 0)) {
234
return true;
235
}
236
_yieldCount = ROOT_GRANULARITY;
237
}
238
return false;
239
}
240
241
bool
242
MM_RealtimeRootScanner::shouldYieldFromMonitorScan()
243
{
244
_yieldCount--;
245
if (_yieldCount < 0) {
246
if (_realtimeGC->_sched->shouldGCYield(_env, 0)) {
247
return true;
248
}
249
_yieldCount = ROOT_GRANULARITY;
250
}
251
return false;
252
}
253
254
/**
255
* Yield from GC by calling the Scheduler's API. Also resets the yield count.
256
* @note this does the same thing as condYield(). It should probably just call
257
* the Scheduler's yield() method to clear up ambiguity but it's been left
258
* untouched for reasons motivated purely by touching the least amount of code.
259
*/
260
void
261
MM_RealtimeRootScanner::yield()
262
{
263
_realtimeGC->_sched->condYieldFromGC(_env);
264
_yieldCount = ROOT_GRANULARITY;
265
}
266
267
/**
268
* Yield only if the Scheduler deems yielding should occur at the time of the
269
* call to this method.
270
*/
271
bool
272
MM_RealtimeRootScanner::condYield(U_64 timeSlackNanoSec)
273
{
274
bool yielded = _realtimeGC->_sched->condYieldFromGC(_env, timeSlackNanoSec);
275
_yieldCount = ROOT_GRANULARITY;
276
return yielded;
277
}
278
279
/**
280
* Scan the per-thread object monitor lookup caches.
281
* Note that this is not a root since the cache contains monitors from the global monitor table
282
* which will be scanned by scanMonitorReferences. It should be scanned first, however, since
283
* scanMonitorReferences may destroy monitors that appear in caches.
284
*/
285
void
286
MM_RealtimeRootScanner::scanMonitorLookupCaches(MM_EnvironmentBase *env)
287
{
288
reportScanningStarted(RootScannerEntity_MonitorLookupCaches);
289
GC_VMThreadListIterator vmThreadListIterator(static_cast<J9JavaVM*>(_omrVM->_language_vm));
290
while(J9VMThread *walkThread = vmThreadListIterator.nextVMThread()) {
291
MM_EnvironmentRealtime* walkThreadEnv = MM_EnvironmentRealtime::getEnvironment(walkThread->omrVMThread);
292
if (FALSE == walkThreadEnv->_monitorCacheCleared) {
293
if (FALSE == MM_AtomicOperations::lockCompareExchangeU32(&walkThreadEnv->_monitorCacheCleared, FALSE, TRUE)) {
294
j9objectmonitor_t *objectMonitorLookupCache = walkThread->objectMonitorLookupCache;
295
UDATA cacheIndex = 0;
296
for (; cacheIndex < J9VMTHREAD_OBJECT_MONITOR_CACHE_SIZE; cacheIndex++) {
297
doMonitorLookupCacheSlot(&objectMonitorLookupCache[cacheIndex]);
298
}
299
if (condYield()) {
300
vmThreadListIterator.reset(static_cast<J9JavaVM*>(_omrVM->_language_vm)->mainThread);
301
}
302
}
303
}
304
}
305
reportScanningEnded(RootScannerEntity_MonitorLookupCaches);
306
}
307
308
void
309
MM_RealtimeRootScanner::scanStringTable(MM_EnvironmentBase *env)
310
{
311
if (env->_currentTask->synchronizeGCThreadsAndReleaseMain(env, UNIQUE_ID)) {
312
/* We can't rely on using _unmarkedImpliesCleared because clearable phase can mark more objects.
313
* Only at this point can we assume unmarked strings are truly dead.
314
*/
315
_realtimeGC->getRealtimeDelegate()->_unmarkedImpliesStringsCleared = true;
316
env->_currentTask->releaseSynchronizedGCThreads(env);
317
}
318
MM_RootScanner::scanStringTable(env);
319
}
320
321