Path: blob/master/runtime/gc_realtime/RealtimeRootScanner.cpp
5985 views
/*******************************************************************************1* Copyright (c) 1991, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* 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-exception20*******************************************************************************/2122#include "j9.h"23#include "j9cfg.h"24#include "j9protos.h"25#include "j9consts.h"26#include "modronopt.h"2728#include <string.h>2930#include "ClassModel.hpp"31#include "EnvironmentBase.hpp"32#if defined(J9VM_GC_FINALIZATION)33#include "FinalizeListManager.hpp"34#include "FinalizerSupport.hpp"35#endif /* J9VM_GC_FINALIZATION */36#include "Heap.hpp"37#include "MemoryPoolSegregated.hpp"38#include "MemorySubSpace.hpp"39#include "modronapi.hpp"40#include "ObjectModel.hpp"41#include "ParallelDispatcher.hpp"42#include "RealtimeMarkingScheme.hpp"43#include "RealtimeRootScanner.hpp"44#include "RootScanner.hpp"45#include "Scheduler.hpp"46#include "SublistSlotIterator.hpp"47#include "Task.hpp"4849void50MM_RealtimeRootScanner::doClass(J9Class *clazz)51{52GC_ClassIterator objectSlotIterator(_env, clazz);53volatile j9object_t *objectSlotPtr = NULL;54while((objectSlotPtr = objectSlotIterator.nextSlot()) != NULL) {55/* discard volatile since we must be in stop-the-world mode */56doSlot((j9object_t*)objectSlotPtr);57}58GC_ClassIteratorClassSlots classSlotIterator(static_cast<J9JavaVM*>(_omrVM->_language_vm), clazz);59J9Class *classPtr;60while (NULL != (classPtr = classSlotIterator.nextSlot())) {61doClassSlot(classPtr);62}63}6465/* Handle a classSlot. This handler is called for every reference to a J9Class.66*67*/68void69MM_RealtimeRootScanner::doClassSlot(J9Class *classPtr)70{71_realtimeGC->getRealtimeDelegate()->markClass(_env, classPtr);72}7374MM_RootScanner::CompletePhaseCode75MM_RealtimeRootScanner::scanClassesComplete(MM_EnvironmentBase *env)76{77/* TODO: consider reactivating this call */78// reportScanningStarted(RootScannerEntity_ClassesComplete);79// _realtimeGC->completeMarking(_env);80// reportScanningEnded(RootScannerEntity_ClassesComplete);81return complete_phase_OK;82}8384/**85* This function iterates through all the threads, calling scanOneThread on each one that86* should be scanned. The scanOneThread function scans exactly one thread and returns87* either true (if it took an action that requires the thread list iterator to return to88* the beginning) or false (if the thread list iterator should just continue with the next89* thread).90*/91void92MM_RealtimeRootScanner::scanThreads(MM_EnvironmentBase *env)93{94reportScanningStarted(RootScannerEntity_Threads);9596GC_VMThreadListIterator vmThreadListIterator(static_cast<J9JavaVM*>(_omrVM->_language_vm));97StackIteratorData localData;9899localData.rootScanner = this;100localData.env = env;101102while(J9VMThread *walkThread = vmThreadListIterator.nextVMThread()) {103MM_EnvironmentRealtime* walkThreadEnv = MM_EnvironmentRealtime::getEnvironment(walkThread->omrVMThread);104if (GC_UNMARK == walkThreadEnv->_allocationColor) {105if (GC_UNMARK == MM_AtomicOperations::lockCompareExchangeU32(&walkThreadEnv->_allocationColor, GC_UNMARK, GC_MARK)) {106if (scanOneThread(env, walkThread, (void*) &localData)) {107vmThreadListIterator.reset(static_cast<J9JavaVM*>(_omrVM->_language_vm)->mainThread);108}109}110}111}112113reportScanningEnded(RootScannerEntity_Threads);114}115116/**117* The following override of scanOneThread performs metronome-specific processing before118* and after the scanning of each thread. Scanning is skipped if the thread has already119* been scanned in this cycle.120**/121bool122MM_RealtimeRootScanner::scanOneThread(MM_EnvironmentBase *envBase, J9VMThread* walkThread, void* localData)123{124MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(envBase);125126scanOneThreadImpl(env, walkThread, localData);127128/* Thead count is used under verbose only.129* Avoid the atomic add in the regular path.130*/131if (_realtimeGC->_sched->verbose() >= 3) {132MM_AtomicOperations::add(&_threadCount, 1);133}134135if (condYield()) {136/* Optionally issue verbose message */137if (_realtimeGC->_sched->verbose() >= 3) {138PORT_ACCESS_FROM_ENVIRONMENT(env);139j9tty_printf(PORTLIB, "Yielded during %s after scanning %d threads\n", scannerName(), _threadCount);140}141142return true;143}144145return false;146}147148void149MM_RealtimeRootScanner::scanOneThreadImpl(MM_EnvironmentRealtime *env, J9VMThread* walkThread, void* localData)150{151}152153void154MM_RealtimeRootScanner::reportThreadCount(MM_EnvironmentBase* env)155{156PORT_ACCESS_FROM_ENVIRONMENT(env);157j9tty_printf(PORTLIB, "Scanned %d threads for %s\n", _threadCount, scannerName());158}159160void161MM_RealtimeRootScanner::scanAtomicRoots(MM_EnvironmentRealtime *env)162{163if (_classDataAsRoots || _nurseryReferencesOnly || _nurseryReferencesPossibly) {164/* The classLoaderObject of a class loader might be in the nursery, but a class loader165* can never be in the remembered set, so include class loaders here.166*/167scanClassLoaders(env);168}169170scanJNIGlobalReferences(env);171172if(_stringTableAsRoot && (!_nurseryReferencesOnly && !_nurseryReferencesPossibly)){173scanStringTable(env);174}175}176177void178MM_RealtimeRootScanner::doStringTableSlot(J9Object **slotPtr, GC_StringTableIterator *stringTableIterator)179{180_env->getGCEnvironment()->_markJavaStats._stringConstantsCandidates += 1;181if(!_markingScheme->isMarked(*slotPtr)) {182_env->getGCEnvironment()->_markJavaStats._stringConstantsCleared += 1;183stringTableIterator->removeSlot();184}185}186187/**188* @Clear the string table cache slot if the object is not marked189*/190void191MM_RealtimeRootScanner::doStringCacheTableSlot(J9Object **slotPtr)192{193J9Object *objectPtr = *slotPtr;194if((NULL != objectPtr) && (!_markingScheme->isMarked(*slotPtr))) {195*slotPtr = NULL;196}197}198199/**200* Calls the Scheduler's yielding API to determine if the GC should yield.201* @return true if the GC should yield, false otherwise202*/203bool204MM_RealtimeRootScanner::shouldYield()205{206return _realtimeGC->_sched->shouldGCYield(_env, 0);207}208209/**210* Calls the Scheduler's yielding API only if timeSlackNanoSec is non-zero or211* if the yield count has reached 0.212* @return true if the GC should yield, false otherwise213*/214bool215MM_RealtimeRootScanner::shouldYieldFromClassScan(UDATA timeSlackNanoSec)216{217_yieldCount--;218if (_yieldCount < 0 || timeSlackNanoSec != 0) {219if (_realtimeGC->_sched->shouldGCYield(_env, 0)) {220return true;221}222_yieldCount = ROOT_GRANULARITY;223}224return false;225}226227bool228MM_RealtimeRootScanner::shouldYieldFromStringScan()229{230_yieldCount--;231if (_yieldCount < 0) {232if (_realtimeGC->_sched->shouldGCYield(_env, 0)) {233return true;234}235_yieldCount = ROOT_GRANULARITY;236}237return false;238}239240bool241MM_RealtimeRootScanner::shouldYieldFromMonitorScan()242{243_yieldCount--;244if (_yieldCount < 0) {245if (_realtimeGC->_sched->shouldGCYield(_env, 0)) {246return true;247}248_yieldCount = ROOT_GRANULARITY;249}250return false;251}252253/**254* Yield from GC by calling the Scheduler's API. Also resets the yield count.255* @note this does the same thing as condYield(). It should probably just call256* the Scheduler's yield() method to clear up ambiguity but it's been left257* untouched for reasons motivated purely by touching the least amount of code.258*/259void260MM_RealtimeRootScanner::yield()261{262_realtimeGC->_sched->condYieldFromGC(_env);263_yieldCount = ROOT_GRANULARITY;264}265266/**267* Yield only if the Scheduler deems yielding should occur at the time of the268* call to this method.269*/270bool271MM_RealtimeRootScanner::condYield(U_64 timeSlackNanoSec)272{273bool yielded = _realtimeGC->_sched->condYieldFromGC(_env, timeSlackNanoSec);274_yieldCount = ROOT_GRANULARITY;275return yielded;276}277278/**279* Scan the per-thread object monitor lookup caches.280* Note that this is not a root since the cache contains monitors from the global monitor table281* which will be scanned by scanMonitorReferences. It should be scanned first, however, since282* scanMonitorReferences may destroy monitors that appear in caches.283*/284void285MM_RealtimeRootScanner::scanMonitorLookupCaches(MM_EnvironmentBase *env)286{287reportScanningStarted(RootScannerEntity_MonitorLookupCaches);288GC_VMThreadListIterator vmThreadListIterator(static_cast<J9JavaVM*>(_omrVM->_language_vm));289while(J9VMThread *walkThread = vmThreadListIterator.nextVMThread()) {290MM_EnvironmentRealtime* walkThreadEnv = MM_EnvironmentRealtime::getEnvironment(walkThread->omrVMThread);291if (FALSE == walkThreadEnv->_monitorCacheCleared) {292if (FALSE == MM_AtomicOperations::lockCompareExchangeU32(&walkThreadEnv->_monitorCacheCleared, FALSE, TRUE)) {293j9objectmonitor_t *objectMonitorLookupCache = walkThread->objectMonitorLookupCache;294UDATA cacheIndex = 0;295for (; cacheIndex < J9VMTHREAD_OBJECT_MONITOR_CACHE_SIZE; cacheIndex++) {296doMonitorLookupCacheSlot(&objectMonitorLookupCache[cacheIndex]);297}298if (condYield()) {299vmThreadListIterator.reset(static_cast<J9JavaVM*>(_omrVM->_language_vm)->mainThread);300}301}302}303}304reportScanningEnded(RootScannerEntity_MonitorLookupCaches);305}306307void308MM_RealtimeRootScanner::scanStringTable(MM_EnvironmentBase *env)309{310if (env->_currentTask->synchronizeGCThreadsAndReleaseMain(env, UNIQUE_ID)) {311/* We can't rely on using _unmarkedImpliesCleared because clearable phase can mark more objects.312* Only at this point can we assume unmarked strings are truly dead.313*/314_realtimeGC->getRealtimeDelegate()->_unmarkedImpliesStringsCleared = true;315env->_currentTask->releaseSynchronizedGCThreads(env);316}317MM_RootScanner::scanStringTable(env);318}319320321