Path: blob/master/runtime/gc_glue_java/MetronomeDelegate.cpp
5990 views
/*******************************************************************************1* Copyright (c) 2019, 2022 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 "MetronomeDelegate.hpp"2324#if defined(J9VM_GC_REALTIME)2526#include "omr.h"2728#include "ClassHeapIterator.hpp"29#include "ClassLoaderIterator.hpp"30#include "ClassLoaderLinkedListIterator.hpp"31#include "ClassLoaderManager.hpp"32#include "ClassLoaderSegmentIterator.hpp"33#include "EnvironmentRealtime.hpp"34#include "FinalizableClassLoaderBuffer.hpp"35#include "FinalizableObjectBuffer.hpp"36#include "FinalizableReferenceBuffer.hpp"37#include "FinalizeListManager.hpp"38#include "FinalizerSupport.hpp"39#include "GCExtensionsBase.hpp"40#include "Heap.hpp"41#include "HeapRegionDescriptorRealtime.hpp"42#include "MetronomeAlarmThread.hpp"43#include "JNICriticalRegion.hpp"44#include "OwnableSynchronizerObjectBufferRealtime.hpp"45#include "OwnableSynchronizerObjectList.hpp"46#include "RealtimeAccessBarrier.hpp"47#include "RealtimeGC.hpp"48#include "RealtimeMarkingScheme.hpp"49#include "RealtimeMarkingSchemeRootMarker.hpp"50#include "RealtimeMarkingSchemeRootClearer.hpp"51#include "RealtimeMarkTask.hpp"52#include "RealtimeRootScanner.hpp"53#include "ReferenceObjectBufferRealtime.hpp"54#include "ReferenceObjectList.hpp"55#include "Scheduler.hpp"56#include "UnfinalizedObjectBufferRealtime.hpp"57#include "UnfinalizedObjectList.hpp"5859void60MM_MetronomeDelegate::yieldWhenRequested(MM_EnvironmentBase *env)61{62MM_GCExtensionsBase *ext = env->getExtensions();63UDATA accessMask;64MM_Scheduler *sched = (MM_Scheduler *)ext->dispatcher;65if (sched->_mode != MM_Scheduler::MUTATOR) {66MM_JNICriticalRegion::releaseAccess((J9VMThread *)env->getOmrVMThread()->_language_vmthread, &accessMask);67while (sched->_mode != MM_Scheduler::MUTATOR) {68omrthread_sleep(10);69}70MM_JNICriticalRegion::reacquireAccess((J9VMThread *)env->getOmrVMThread()->_language_vmthread, accessMask);71}72}7374/**75* C entrypoint for the newly created alarm thread.76*/77int J9THREAD_PROC78MM_MetronomeDelegate::metronomeAlarmThreadWrapper(void* userData)79{80MM_MetronomeAlarmThread *alarmThread = (MM_MetronomeAlarmThread *)userData;81J9JavaVM *javaVM = (J9JavaVM *)alarmThread->getScheduler()->_extensions->getOmrVM()->_language_vm;82PORT_ACCESS_FROM_JAVAVM(javaVM);83uintptr_t rc;8485j9sig_protect(MM_MetronomeDelegate::signalProtectedFunction, (void*)userData,86javaVM->internalVMFunctions->structuredSignalHandlerVM, javaVM,87J9PORT_SIG_FLAG_SIGALLSYNC | J9PORT_SIG_FLAG_MAY_CONTINUE_EXECUTION,88&rc);8990omrthread_monitor_enter(alarmThread->_mutex);91alarmThread->_alarmThreadActive = MM_MetronomeAlarmThread::ALARM_THREAD_SHUTDOWN;92omrthread_monitor_notify(alarmThread->_mutex);93omrthread_exit(alarmThread->_mutex);9495return 0;96}9798uintptr_t99MM_MetronomeDelegate::signalProtectedFunction(J9PortLibrary *privatePortLibrary, void* userData)100{101MM_MetronomeAlarmThread *alarmThread = (MM_MetronomeAlarmThread *)userData;102J9JavaVM *javaVM = (J9JavaVM *)alarmThread->getScheduler()->_extensions->getOmrVM()->_language_vm;103J9VMThread *vmThread = NULL;104MM_EnvironmentRealtime *env = NULL;105106if (JNI_OK != (javaVM->internalVMFunctions->attachSystemDaemonThread(javaVM, &vmThread, "GC Alarm"))) {107return 0;108}109110env = MM_EnvironmentRealtime::getEnvironment(vmThread->omrVMThread);111112alarmThread->run(env);113114javaVM->internalVMFunctions->DetachCurrentThread((JavaVM*)javaVM);115116return 0;117}118119void120MM_MetronomeDelegate::clearGCStats()121{122_extensions->markJavaStats.clear();123}124125void126MM_MetronomeDelegate::clearGCStatsEnvironment(MM_EnvironmentRealtime *env)127{128env->_markStats.clear();129env->getGCEnvironment()->_markJavaStats.clear();130env->_workPacketStats.clear();131}132133void134MM_MetronomeDelegate::mergeGCStats(MM_EnvironmentRealtime *env)135{136GC_Environment *gcEnv = env->getGCEnvironment();137138MM_GlobalGCStats *finalGCStats= &_extensions->globalGCStats;139finalGCStats->markStats.merge(&env->_markStats);140_extensions->markJavaStats.merge(&gcEnv->_markJavaStats);141finalGCStats->workPacketStats.merge(&env->_workPacketStats);142}143144uintptr_t145MM_MetronomeDelegate::getSplitArraysProcessed(MM_EnvironmentRealtime *env)146{147GC_Environment *gcEnv = env->getGCEnvironment();148return gcEnv->_markJavaStats.splitArraysProcessed;149}150151bool152MM_MetronomeDelegate::initialize(MM_EnvironmentBase *env)153{154_scheduler = _realtimeGC->_sched;155_markingScheme = _realtimeGC->getMarkingScheme();156157#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)158_unmarkedImpliesClasses = false;159#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */160_unmarkedImpliesCleared = false;161_unmarkedImpliesStringsCleared = false;162163/* allocate and initialize the global reference object lists */164if (!allocateAndInitializeReferenceObjectLists(env)){165return false;166}167168/* allocate and initialize the global unfinalized object lists */169if (!allocateAndInitializeUnfinalizedObjectLists(env)) {170return false;171}172173/* allocate and initialize the global ownable synchronizer object lists */174if (!allocateAndInitializeOwnableSynchronizerObjectLists(env)) {175return false;176}177178#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)179if (!_extensions->dynamicClassUnloadingThresholdForced) {180_extensions->dynamicClassUnloadingThreshold = 1;181}182if (!_extensions->dynamicClassUnloadingKickoffThresholdForced) {183_extensions->dynamicClassUnloadingKickoffThreshold = 0;184}185#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */186187/* Create the appropriate access barrier for Metronome */188MM_RealtimeAccessBarrier *accessBarrier = NULL;189accessBarrier = allocateAccessBarrier(env);190if (NULL == accessBarrier) {191return false;192}193MM_GCExtensions::getExtensions(_javaVM)->accessBarrier = (MM_ObjectAccessBarrier *)accessBarrier;194195_javaVM->realtimeHeapMapBasePageRounded = _markingScheme->_markMap->getHeapMapBaseRegionRounded();196_javaVM->realtimeHeapMapBits = _markingScheme->_markMap->getHeapMapBits();197198return true;199}200201bool202MM_MetronomeDelegate::allocateAndInitializeReferenceObjectLists(MM_EnvironmentBase *env)203{204const UDATA listCount = getReferenceObjectListCount(env);205Assert_MM_true(0 < listCount);206_extensions->referenceObjectLists = (MM_ReferenceObjectList *)env->getForge()->allocate((sizeof(MM_ReferenceObjectList) * listCount), MM_AllocationCategory::FIXED, J9_GET_CALLSITE());207if (NULL == _extensions->referenceObjectLists) {208return false;209}210for (UDATA index = 0; index < listCount; index++) {211new(&_extensions->referenceObjectLists[index]) MM_ReferenceObjectList();212}213return true;214}215216bool217MM_MetronomeDelegate::allocateAndInitializeUnfinalizedObjectLists(MM_EnvironmentBase *env)218{219const UDATA listCount = getUnfinalizedObjectListCount(env);220Assert_MM_true(0 < listCount);221MM_UnfinalizedObjectList *unfinalizedObjectLists = (MM_UnfinalizedObjectList *)env->getForge()->allocate((sizeof(MM_UnfinalizedObjectList) * listCount), MM_AllocationCategory::FIXED, J9_GET_CALLSITE());222if (NULL == unfinalizedObjectLists) {223return false;224}225for (UDATA index = 0; index < listCount; index++) {226new(&unfinalizedObjectLists[index]) MM_UnfinalizedObjectList();227/* add each list to the global list. we need to maintain the doubly linked list228* to ensure uniformity with SE/Balanced.229*/230MM_UnfinalizedObjectList *previousUnfinalizedObjectList = (0 == index) ? NULL : &unfinalizedObjectLists[index-1];231MM_UnfinalizedObjectList *nextUnfinalizedObjectList = ((listCount - 1) == index) ? NULL : &unfinalizedObjectLists[index+1];232233unfinalizedObjectLists[index].setNextList(nextUnfinalizedObjectList);234unfinalizedObjectLists[index].setPreviousList(previousUnfinalizedObjectList);235}236_extensions->unfinalizedObjectLists = unfinalizedObjectLists;237return true;238}239240bool241MM_MetronomeDelegate::allocateAndInitializeOwnableSynchronizerObjectLists(MM_EnvironmentBase *env)242{243const UDATA listCount = getOwnableSynchronizerObjectListCount(env);244Assert_MM_true(0 < listCount);245MM_OwnableSynchronizerObjectList *ownableSynchronizerObjectLists = (MM_OwnableSynchronizerObjectList *)env->getForge()->allocate((sizeof(MM_OwnableSynchronizerObjectList) * listCount), MM_AllocationCategory::FIXED, J9_GET_CALLSITE());246if (NULL == ownableSynchronizerObjectLists) {247return false;248}249for (UDATA index = 0; index < listCount; index++) {250new(&ownableSynchronizerObjectLists[index]) MM_OwnableSynchronizerObjectList();251/* add each list to the global list. we need to maintain the doubly linked list252* to ensure uniformity with SE/Balanced.253*/254MM_OwnableSynchronizerObjectList *previousOwnableSynchronizerObjectList = (0 == index) ? NULL : &ownableSynchronizerObjectLists[index-1];255MM_OwnableSynchronizerObjectList *nextOwnableSynchronizerObjectList = ((listCount - 1) == index) ? NULL : &ownableSynchronizerObjectLists[index+1];256257ownableSynchronizerObjectLists[index].setNextList(nextOwnableSynchronizerObjectList);258ownableSynchronizerObjectLists[index].setPreviousList(previousOwnableSynchronizerObjectList);259}260_extensions->setOwnableSynchronizerObjectLists(ownableSynchronizerObjectLists);261return true;262}263264void265MM_MetronomeDelegate::tearDown(MM_EnvironmentBase *env)266{267if (NULL != _extensions->referenceObjectLists) {268env->getForge()->free(_extensions->referenceObjectLists);269_extensions->referenceObjectLists = NULL;270}271272if (NULL != _extensions->unfinalizedObjectLists) {273env->getForge()->free(_extensions->unfinalizedObjectLists);274_extensions->unfinalizedObjectLists = NULL;275}276277if (NULL != _extensions->getOwnableSynchronizerObjectLists()) {278env->getForge()->free(_extensions->getOwnableSynchronizerObjectLists());279_extensions->setOwnableSynchronizerObjectLists(NULL);280}281282if (NULL != _extensions->accessBarrier) {283_extensions->accessBarrier->kill(env);284_extensions->accessBarrier = NULL;285}286287_javaVM->realtimeHeapMapBits = NULL;288}289290void291MM_MetronomeDelegate::mainSetupForGC(MM_EnvironmentBase *env)292{293#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)294/* Set the dynamic class unloading flag based on command line and runtime state */295switch (_extensions->dynamicClassUnloading) {296case MM_GCExtensions::DYNAMIC_CLASS_UNLOADING_NEVER:297_extensions->runtimeCheckDynamicClassUnloading = false;298break;299case MM_GCExtensions::DYNAMIC_CLASS_UNLOADING_ALWAYS:300_extensions->runtimeCheckDynamicClassUnloading = true;301break;302case MM_GCExtensions::DYNAMIC_CLASS_UNLOADING_ON_CLASS_LOADER_CHANGES:303_extensions->runtimeCheckDynamicClassUnloading = (_extensions->aggressive || _extensions->classLoaderManager->isTimeForClassUnloading(env));304break;305default:306break;307}308#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */309310#if defined(J9VM_GC_FINALIZATION)311_finalizationRequired = false;312#endif /* J9VM_GC_FINALIZATION */313}314315void316MM_MetronomeDelegate::mainCleanupAfterGC(MM_EnvironmentBase *env)317{318#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)319/* flush the dead class segments if their size exceeds the CacheSize mark.320* Heap fixup should have been completed in this cycle.321*/322if (_extensions->classLoaderManager->reclaimableMemory() > _extensions->deadClassLoaderCacheSize) {323Trc_MM_FlushUndeadSegments_Entry(env->getLanguageVMThread(), "Non-zero reclaimable memory available");324_extensions->classLoaderManager->flushUndeadSegments(env);325Trc_MM_FlushUndeadSegments_Exit(env->getLanguageVMThread());326}327#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */328}329330void331MM_MetronomeDelegate::incrementalCollectStart(MM_EnvironmentRealtime *env)332{333#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)334_dynamicClassUnloadingEnabled = ((_extensions->runtimeCheckDynamicClassUnloading != 0) ? true : false);335#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */336}337338void339MM_MetronomeDelegate::incrementalCollect(MM_EnvironmentRealtime *env)340{341#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)342PORT_ACCESS_FROM_ENVIRONMENT(env);343_dynamicClassUnloadingEnabled = ((_extensions->runtimeCheckDynamicClassUnloading != 0) ? true : false);344345if (_extensions->runtimeCheckDynamicClassUnloading != 0) {346MM_ClassUnloadStats *classUnloadStats = &_extensions->globalGCStats.classUnloadStats;347_realtimeGC->setCollectorUnloadingClassLoaders();348reportClassUnloadingStart(env);349classUnloadStats->_startTime = j9time_hires_clock();350unloadDeadClassLoaders(env);351classUnloadStats->_endTime = j9time_hires_clock();352reportClassUnloadingEnd(env);353354/* If there was dynamic class unloading checks during the run, record the new number of class355* loaders last seen during a DCU pass356*/357_extensions->classLoaderManager->setLastUnloadNumOfClassLoaders();358_extensions->classLoaderManager->setLastUnloadNumOfAnonymousClasses();359}360361/* Handling of classes done. Return back to "mark if necessary" mode */362_unmarkedImpliesClasses = false;363364/* Clear the appropriate flags of all classLoaders */365GC_ClassLoaderIterator classLoaderIterator(_javaVM->classLoaderBlocks);366J9ClassLoader *classLoader;367while((classLoader = classLoaderIterator.nextSlot()) != NULL) {368classLoader->gcFlags &= ~J9_GC_CLASS_LOADER_SCANNED;369}370#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */371372/* If the J9VM_DEBUG_ATTRIBUTE_ALLOW_USER_HEAP_WALK flag is set,373* or if we are about to unload classes and free class memory segments374* then fix the heap so that it can be walked by debugging tools375*/376#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)377bool fixupForClassUnload = (_extensions->classLoaderManager->reclaimableMemory() > _extensions->deadClassLoaderCacheSize);378#else /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */379bool fixupForClassUnload = false;380#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */381if (J9VM_DEBUG_ATTRIBUTE_ALLOW_USER_HEAP_WALK == (((J9JavaVM *)env->getLanguageVM())->requiredDebugAttributes & J9VM_DEBUG_ATTRIBUTE_ALLOW_USER_HEAP_WALK)382|| fixupForClassUnload) {383_realtimeGC->_fixHeapForWalk = true;384}385}386387void388MM_MetronomeDelegate::doAuxiliaryGCWork(MM_EnvironmentBase *env)389{390#if defined(J9VM_GC_FINALIZATION)391if(isFinalizationRequired()) {392omrthread_monitor_enter(_javaVM->finalizeMainMonitor);393_javaVM->finalizeMainFlags |= J9_FINALIZE_FLAGS_MAIN_WAKE_UP;394omrthread_monitor_notify_all(_javaVM->finalizeMainMonitor);395omrthread_monitor_exit(_javaVM->finalizeMainMonitor);396}397#endif /* J9VM_GC_FINALIZATION */398}399400#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)401void402MM_MetronomeDelegate::processDyingClasses(MM_EnvironmentRealtime *env, UDATA* classUnloadCountResult, UDATA* anonymousClassUnloadCountResult, UDATA* classLoaderUnloadCountResult, J9ClassLoader** classLoaderUnloadListResult)403{404J9ClassLoader *classLoader = NULL;405J9VMThread *vmThread = (J9VMThread *)env->getLanguageVMThread();406UDATA classUnloadCount = 0;407UDATA anonymousClassUnloadCount = 0;408UDATA classLoaderUnloadCount = 0;409J9ClassLoader *unloadLink = NULL;410J9Class *classUnloadList = NULL;411J9Class *anonymousClassUnloadList = NULL;412413/*414* Verify that boolean array class has been marked. Assertion is done to ensure correctness415* of an optimization in ClassIteratorClassSlots that only checks booleanArrayClass Interfaces416* since all array claseses share the same ITable.417*/418Assert_MM_true(_markingScheme->isMarked(_javaVM->booleanArrayClass->classObject));419420/*421* Walk anonymous classes and set unmarked as dying422*423* Do this walk before classloaders to be unloaded walk to create list of anonymous classes to be unloaded and use it424* as sublist to continue to build general list of classes to be unloaded425*426* Anonymous classes suppose to be allocated one per segment427* This is not relevant here however becomes important at segment removal time428*/429anonymousClassUnloadList = addDyingClassesToList(env, _javaVM->anonClassLoader, false, anonymousClassUnloadList, &anonymousClassUnloadCount);430431/* class unload list includes anonymous class unload list */432classUnloadList = anonymousClassUnloadList;433classUnloadCount += anonymousClassUnloadCount;434435GC_ClassLoaderLinkedListIterator classLoaderIterator(env, _extensions->classLoaderManager);436while(NULL != (classLoader = (J9ClassLoader *)classLoaderIterator.nextSlot())) {437if (0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD)) {438Assert_MM_true(NULL == classLoader->unloadLink);439if(_markingScheme->isMarked(classLoader->classLoaderObject) ) {440classLoader->gcFlags &= ~J9_GC_CLASS_LOADER_SCANNED;441} else {442/* Anonymous classloader should not be unloaded */443Assert_MM_true(0 == (classLoader->flags & J9CLASSLOADER_ANON_CLASS_LOADER));444445classLoaderUnloadCount += 1;446classLoader->gcFlags |= J9_GC_CLASS_LOADER_DEAD;447448/* add this loader to the linked list of loaders being unloaded in this cycle */449classLoader->unloadLink = unloadLink;450unloadLink = classLoader;451452classUnloadList = addDyingClassesToList(env, classLoader, true, classUnloadList, &classUnloadCount);453}454}455yieldFromClassUnloading(env);456}457458if (0 != classUnloadCount) {459/* Call classes unload hook */460TRIGGER_J9HOOK_VM_CLASSES_UNLOAD(_javaVM->hookInterface, vmThread, classUnloadCount, classUnloadList);461yieldFromClassUnloading(env);462}463464if (0 != anonymousClassUnloadCount) {465/* Call anonymous classes unload hook */466TRIGGER_J9HOOK_VM_ANON_CLASSES_UNLOAD(_javaVM->hookInterface, vmThread, anonymousClassUnloadCount, anonymousClassUnloadList);467yieldFromClassUnloading(env);468}469470if (0 != classLoaderUnloadCount) {471/* Call classloader unload hook */472TRIGGER_J9HOOK_VM_CLASS_LOADERS_UNLOAD(_javaVM->hookInterface, vmThread, unloadLink);473yieldFromClassUnloading(env);474}475476/* Ensure that the VM has an accurate anonymous class count */477_javaVM->anonClassCount -= anonymousClassUnloadCount;478479*classUnloadCountResult = classUnloadCount;480*anonymousClassUnloadCountResult = anonymousClassUnloadCount;481*classLoaderUnloadCountResult = classLoaderUnloadCount;482*classLoaderUnloadListResult = unloadLink;483}484485J9Class *486MM_MetronomeDelegate::addDyingClassesToList(MM_EnvironmentRealtime *env, J9ClassLoader * classLoader, bool setAll, J9Class *classUnloadListStart, UDATA *classUnloadCountResult)487{488J9VMThread *vmThread = (J9VMThread *)env->getLanguageVMThread();489J9Class *classUnloadList = classUnloadListStart;490UDATA classUnloadCount = 0;491492if (NULL != classLoader) {493GC_ClassLoaderSegmentIterator segmentIterator(classLoader, MEMORY_TYPE_RAM_CLASS);494J9MemorySegment *segment = NULL;495while(NULL != (segment = segmentIterator.nextSegment())) {496GC_ClassHeapIterator classHeapIterator(_javaVM, segment);497J9Class *clazz = NULL;498while(NULL != (clazz = classHeapIterator.nextClass())) {499500J9CLASS_EXTENDED_FLAGS_CLEAR(clazz, J9ClassGCScanned);501502J9Object *classObject = clazz->classObject;503if (setAll || !_markingScheme->isMarked(classObject)) {504505/* with setAll all classes must be unmarked */506Assert_MM_true(!_markingScheme->isMarked(classObject));507508classUnloadCount += 1;509510/* Remove the class from the subclass traversal list */511_extensions->classLoaderManager->removeFromSubclassHierarchy(env, clazz);512/* Mark class as dying */513clazz->classDepthAndFlags |= J9AccClassDying;514515/* Call class unload hook */516Trc_MM_cleanUpClassLoadersStart_triggerClassUnload(env->getLanguageVMThread(),clazz,517(UDATA) J9UTF8_LENGTH(J9ROMCLASS_CLASSNAME(clazz->romClass)),518J9UTF8_DATA(J9ROMCLASS_CLASSNAME(clazz->romClass)));519TRIGGER_J9HOOK_VM_CLASS_UNLOAD(_javaVM->hookInterface, vmThread, clazz);520521/* add class to dying anonymous classes link list */522clazz->gcLink = classUnloadList;523classUnloadList = clazz;524}525}526}527}528529*classUnloadCountResult += classUnloadCount;530return classUnloadList;531}532533/**534* Free classloaders which are being unloaded during this GC cycle. Also remove all535* dead classes from the traversal list.536* @note the traversal code belongs in its own function or possibly processDyingClasses537* or possible processDyingClasses. It is currently here for historic reasons.538*539* @param deadClassLoaders Linked list of classloaders dying during this GC cycle540*/541void542MM_MetronomeDelegate::processUnlinkedClassLoaders(MM_EnvironmentBase *envModron, J9ClassLoader *deadClassLoaders)543{544MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(envModron);545J9ClassLoader *unloadLink = deadClassLoaders;546J9VMThread *vmThread = (J9VMThread *)env->getLanguageVMThread();547J9JavaVM *javaVM = (J9JavaVM *)env->getLanguageVM();548549/* Remove dead classes from the traversal list (if necessary) */550J9Class *jlObject = J9VMJAVALANGOBJECT_OR_NULL(javaVM);551J9Class *previousClass = jlObject;552J9Class *nextClass = (NULL != jlObject) ? jlObject->subclassTraversalLink : jlObject;553while ((NULL != nextClass) && (jlObject != nextClass)) {554if (J9CLASS_FLAGS(nextClass) & J9AccClassDying) {555while ((NULL != nextClass->subclassTraversalLink) && (jlObject != nextClass) && (J9CLASS_FLAGS(nextClass) & 0x08000000)) {556nextClass = nextClass->subclassTraversalLink;557}558previousClass->subclassTraversalLink = nextClass;559}560previousClass = nextClass;561nextClass = nextClass->subclassTraversalLink;562/* TODO CRGTMP Do we need to yield here? Is yielding safe? */563}564565/* Free memory for dead classloaders */566while (NULL != unloadLink) {567J9ClassLoader *nextUnloadLink = unloadLink->unloadLink;568_javaVM->internalVMFunctions->freeClassLoader(unloadLink, _javaVM, vmThread, 1);569unloadLink = nextUnloadLink;570yieldFromClassUnloading(env);571}572}573574void575MM_MetronomeDelegate::updateClassUnloadStats(MM_EnvironmentBase *env, UDATA classUnloadCount, UDATA anonymousClassUnloadCount, UDATA classLoaderUnloadCount)576{577MM_ClassUnloadStats *classUnloadStats = &_extensions->globalGCStats.classUnloadStats;578579/* TODO CRGTMP move global stats into super class implementation once it is created */580classUnloadStats->_classesUnloadedCount = classUnloadCount;581classUnloadStats->_anonymousClassesUnloadedCount = anonymousClassUnloadCount;582classUnloadStats->_classLoaderUnloadedCount = classLoaderUnloadCount;583584/* Record increment stats */585_extensions->globalGCStats.metronomeStats.classesUnloadedCount = classUnloadCount;586_extensions->globalGCStats.metronomeStats.anonymousClassesUnloadedCount = anonymousClassUnloadCount;587_extensions->globalGCStats.metronomeStats.classLoaderUnloadedCount = classLoaderUnloadCount;588}589590/**591* Unload classcloaders that are no longer referenced. If the classloader has shared592* libraries open place it on the finalize queue instead of freeing it.593*594*/595void596MM_MetronomeDelegate::unloadDeadClassLoaders(MM_EnvironmentBase *envModron)597{598MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(envModron);599J9ClassLoader *unloadLink = NULL;600UDATA classUnloadCount = 0;601UDATA anonymousClassUnloadCount = 0;602UDATA classLoaderUnloadCount = 0;603J9ClassLoader *classLoadersUnloadedList = NULL;604J9MemorySegment *reclaimedSegments = NULL;605606/* set the vmState whilst we're unloading classes */607UDATA vmState = env->pushVMstate(OMRVMSTATE_GC_CLEANING_METADATA);608609lockClassUnloadMonitor(env);610611processDyingClasses(env, &classUnloadCount, &anonymousClassUnloadCount, &classLoaderUnloadCount, &classLoadersUnloadedList);612613/* cleanup segments in anonymous classloader */614_extensions->classLoaderManager->cleanUpSegmentsInAnonymousClassLoader(env, &reclaimedSegments);615616/* enqueue all the segments we just salvaged from the dead class loaders for delayed free (this work was historically attributed in the unload end operation so it goes after the timer start) */617_extensions->classLoaderManager->enqueueUndeadClassSegments(reclaimedSegments);618619yieldFromClassUnloading(env);620621GC_FinalizableClassLoaderBuffer buffer(_extensions);622623while (NULL != classLoadersUnloadedList) {624/* fetch the next loader immediately, since we will re-use the unloadLink in this loop */625J9ClassLoader* classLoader = classLoadersUnloadedList;626classLoadersUnloadedList = classLoader->unloadLink;627628Assert_MM_true(0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_SCANNED));629Assert_MM_true(J9_GC_CLASS_LOADER_DEAD == (classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD));630Assert_MM_true(0 == (classLoader->gcFlags & (J9_GC_CLASS_LOADER_UNLOADING | J9_GC_CLASS_LOADER_ENQ_UNLOAD)));631632/* Class loader died this collection, so do cleanup work */633reclaimedSegments = NULL;634635/* Perform classLoader-specific clean up work, including freeing the classLoader's class hash table and636* class path entries.637*/638_javaVM->internalVMFunctions->cleanUpClassLoader((J9VMThread *)env->getLanguageVMThread(), classLoader);639/* free any ROM classes now and enqueue any RAM classes */640_extensions->classLoaderManager->cleanUpSegmentsAlongClassLoaderLink(_javaVM, classLoader->classSegments, &reclaimedSegments);641/* we are taking responsibility for cleaning these here so free them */642classLoader->classSegments = NULL;643/* enqueue all the segments we just salvaged from the dead class loaders for delayed free (this work was historically attributed in the unload end operation so it goes after the timer start) */644_extensions->classLoaderManager->enqueueUndeadClassSegments(reclaimedSegments);645646/* Remove this classloader slot */647_extensions->classLoaderManager->unlinkClassLoader(classLoader);648649#if defined(J9VM_GC_FINALIZATION)650/* Determine if the classLoader needs to be enqueued for finalization (for shared library unloading),651* otherwise add it to the list of classLoaders to be unloaded by cleanUpClassLoadersEnd.652*/653if(((NULL != classLoader->sharedLibraries)654&& (0 != pool_numElements(classLoader->sharedLibraries)))655|| (_extensions->fvtest_forceFinalizeClassLoaders)) {656/* Attempt to enqueue the class loader for the finalizer */657buffer.add(env, classLoader);658classLoader->gcFlags |= J9_GC_CLASS_LOADER_ENQ_UNLOAD;659_finalizationRequired = true;660} else661#endif /* J9VM_GC_FINALIZATION */662{663/* Add the classLoader to the list of classLoaders to unloaded by cleanUpClassLoadersEnd */664classLoader->unloadLink = unloadLink;665unloadLink = classLoader;666}667yieldFromClassUnloading(env);668}669670buffer.flush(env);671672updateClassUnloadStats(env, classUnloadCount, anonymousClassUnloadCount, classLoaderUnloadCount);673674processUnlinkedClassLoaders(env, unloadLink);675676unlockClassUnloadMonitor(env);677678env->popVMstate(vmState);679}680681/**682* Check to see if it is time to yield. If it is time to yield the GC683* must release the classUnloadMonitor before yielding. Once the GC684* comes back from the yield it is required to acquire the classUnloadMonitor685* again.686*/687void688MM_MetronomeDelegate::yieldFromClassUnloading(MM_EnvironmentRealtime *env)689{690if (_realtimeGC->shouldYield(env)) {691unlockClassUnloadMonitor(env);692_realtimeGC->yield(env);693lockClassUnloadMonitor(env);694}695}696697/**698* The GC is required to hold the classUnloadMonitor while it is unloading classes.699* This will ensure that the JIT will abort and ongoing compilations700*/701void702MM_MetronomeDelegate::lockClassUnloadMonitor(MM_EnvironmentRealtime *env)703{704/* Grab the classUnloadMonitor so that the JIT and the GC will not interfere with each other */705#if defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR)706if (0 != omrthread_rwmutex_try_enter_write(_javaVM->classUnloadMutex)) {707#else708if (0 != omrthread_monitor_try_enter(_javaVM->classUnloadMutex)) {709#endif /* J9VM_JIT_CLASS_UNLOAD_RWMONITOR */710/* Failed acquire the monitor so interrupt the JIT. This will allow the GC711* to continue unloading classes.712*/713TRIGGER_J9HOOK_MM_INTERRUPT_COMPILATION(_extensions->hookInterface, (J9VMThread *)env->getLanguageVMThread());714#if defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR)715omrthread_rwmutex_enter_write(_javaVM->classUnloadMutex);716#else717omrthread_monitor_enter(_javaVM->classUnloadMutex);718#endif /* J9VM_JIT_CLASS_UNLOAD_RWMONITOR */719}720}721722/**723* Release the classUnloadMonitor. This will allow the JIT to compile new methods.724*/725void726MM_MetronomeDelegate::unlockClassUnloadMonitor(MM_EnvironmentRealtime *env)727{728#if defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR)729omrthread_rwmutex_exit_write(_javaVM->classUnloadMutex);730#else731omrthread_monitor_exit(_javaVM->classUnloadMutex);732#endif /* J9VM_JIT_CLASS_UNLOAD_RWMONITOR */733}734735void736MM_MetronomeDelegate::reportClassUnloadingStart(MM_EnvironmentBase *env)737{738PORT_ACCESS_FROM_ENVIRONMENT(env);739Trc_MM_ClassUnloadingStart(env->getLanguageVMThread());740741TRIGGER_J9HOOK_MM_PRIVATE_CLASS_UNLOADING_START(742_extensions->privateHookInterface,743env->getOmrVMThread(),744j9time_hires_clock(),745J9HOOK_MM_PRIVATE_CLASS_UNLOADING_START);746}747748void749MM_MetronomeDelegate::reportClassUnloadingEnd(MM_EnvironmentBase *env)750{751PORT_ACCESS_FROM_ENVIRONMENT(env);752MM_ClassUnloadStats *classUnloadStats = &_extensions->globalGCStats.classUnloadStats;753754Trc_MM_ClassUnloadingEnd(env->getLanguageVMThread(),755classUnloadStats->_classLoaderUnloadedCount,756classUnloadStats->_classesUnloadedCount);757758TRIGGER_J9HOOK_MM_CLASS_UNLOADING_END(759_extensions->hookInterface,760(J9VMThread *)env->getLanguageVMThread(),761j9time_hires_clock(),762J9HOOK_MM_CLASS_UNLOADING_END,763classUnloadStats->_endTime - classUnloadStats->_startTime,764classUnloadStats->_classLoaderUnloadedCount,765classUnloadStats->_classesUnloadedCount,766classUnloadStats->_classUnloadMutexQuiesceTime,767classUnloadStats->_endSetupTime - classUnloadStats->_startSetupTime,768classUnloadStats->_endScanTime - classUnloadStats->_startScanTime,769classUnloadStats->_endPostTime - classUnloadStats->_startPostTime);770}771#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */772773void774MM_MetronomeDelegate::reportSyncGCEnd(MM_EnvironmentBase *env)775{776OMRPORT_ACCESS_FROM_ENVIRONMENT(env);777UDATA approximateFreeMemorySize = _extensions->heap->getApproximateActiveFreeMemorySize();778#if defined(OMR_GC_DYNAMIC_CLASS_UNLOADING)779MM_ClassUnloadStats *classUnloadStats = &_extensions->globalGCStats.classUnloadStats;780UDATA classLoaderUnloadCount = classUnloadStats->_classLoaderUnloadedCount;781UDATA classUnloadCount = classUnloadStats->_classesUnloadedCount;782UDATA anonymousClassUnloadCount = classUnloadStats->_anonymousClassesUnloadedCount;783#else /* defined(OMR_GC_DYNAMIC_CLASS_UNLOADING) */784UDATA classLoaderUnloadCount = 0;785UDATA classUnloadCount = 0;786UDATA anonymousClassUnloadCount = 0;787#endif /* defined(OMR_GC_DYNAMIC_CLASS_UNLOADING) */788UDATA weakReferenceCount = _extensions->markJavaStats._weakReferenceStats._cleared;789UDATA softReferenceCount = _extensions->markJavaStats._softReferenceStats._cleared;790UDATA maxSoftReferenceAge = _extensions->getMaxSoftReferenceAge();791UDATA softReferenceAge = _extensions->getDynamicMaxSoftReferenceAge();792UDATA phantomReferenceCount = _extensions->markJavaStats._phantomReferenceStats._cleared;793UDATA finalizerCount = _extensions->globalGCStats.metronomeStats.getWorkPacketOverflowCount();794UDATA packetOverflowCount = _extensions->globalGCStats.metronomeStats.getWorkPacketOverflowCount();795UDATA objectOverflowCount = _extensions->globalGCStats.metronomeStats.getObjectOverflowCount();796797Trc_MM_SynchGCEnd(env->getLanguageVMThread(),798approximateFreeMemorySize,7990,800classLoaderUnloadCount,801classUnloadCount,802weakReferenceCount,803softReferenceCount,804maxSoftReferenceAge,805softReferenceAge,806phantomReferenceCount,807finalizerCount,808packetOverflowCount,809objectOverflowCount810);811812TRIGGER_J9HOOK_MM_PRIVATE_METRONOME_SYNCHRONOUS_GC_END(_extensions->privateHookInterface,813env->getOmrVMThread(), omrtime_hires_clock(),814J9HOOK_MM_PRIVATE_METRONOME_SYNCHRONOUS_GC_END,815approximateFreeMemorySize,8160,817classLoaderUnloadCount,818classUnloadCount,819anonymousClassUnloadCount,820weakReferenceCount,821softReferenceCount,822maxSoftReferenceAge,823softReferenceAge,824phantomReferenceCount,825finalizerCount,826packetOverflowCount,827objectOverflowCount828);829}830/**831* Factory method for creating the access barrier. Note that the default realtime access barrier832* doesn't handle the RTSJ checks.833*/834MM_RealtimeAccessBarrier*835MM_MetronomeDelegate::allocateAccessBarrier(MM_EnvironmentBase *env)836{837return MM_RealtimeAccessBarrier::newInstance(env);838}839840/**841* Iterates over all threads and enables the double barrier for each thread by setting the842* remembered set fragment index to the reserved index.843*/844void845MM_MetronomeDelegate::enableDoubleBarrier(MM_EnvironmentBase *env)846{847MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(env);848MM_RealtimeAccessBarrier* realtimeAccessBarrier = (MM_RealtimeAccessBarrier*)extensions->accessBarrier;849GC_VMThreadListIterator vmThreadListIterator(_javaVM);850851/* First, enable the global double barrier flag so new threads will have the double barrier enabled. */852realtimeAccessBarrier->setDoubleBarrierActive();853while(J9VMThread* thread = vmThreadListIterator.nextVMThread()) {854/* Second, enable the double barrier on all threads individually. */855realtimeAccessBarrier->setDoubleBarrierActiveOnThread(MM_EnvironmentBase::getEnvironment(thread->omrVMThread));856}857}858859/**860* Disables the double barrier for the specified thread.861*/862void863MM_MetronomeDelegate::disableDoubleBarrierOnThread(MM_EnvironmentBase* env, OMR_VMThread* vmThread)864{865/* This gets called on a per thread basis as threads get scanned. */866MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(env);867MM_RealtimeAccessBarrier* realtimeAccessBarrier = (MM_RealtimeAccessBarrier*)extensions->accessBarrier;868realtimeAccessBarrier->setDoubleBarrierInactiveOnThread(MM_EnvironmentBase::getEnvironment(vmThread));869}870871/**872* Disables the global double barrier flag. This should be called after all threads have been scanned873* and disableDoubleBarrierOnThread has been called on each of them.874*/875void876MM_MetronomeDelegate::disableDoubleBarrier(MM_EnvironmentBase* env)877{878/* The enabling of the double barrier must traverse all threads, but the double barrier gets disabled879* on a per thread basis as threads get scanned, so no need to traverse all threads in this method.880*/881MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(env);882MM_RealtimeAccessBarrier* realtimeAccessBarrier = (MM_RealtimeAccessBarrier*)extensions->accessBarrier;883realtimeAccessBarrier->setDoubleBarrierInactive();884}885886887#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)888/**889* Walk all class loaders marking their classes if the classLoader object has been890* marked.891*892* @return true if any classloaders/classes are marked, false otherwise893*/894bool895MM_MetronomeDelegate::doClassTracing(MM_EnvironmentRealtime *env)896{897J9ClassLoader *classLoader;898bool didWork = false;899900MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(env);901GC_ClassLoaderLinkedListIterator classLoaderIterator(env, extensions->classLoaderManager);902903while((classLoader = (J9ClassLoader *)classLoaderIterator.nextSlot()) != NULL) {904if (0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD)) {905if(J9CLASSLOADER_ANON_CLASS_LOADER == (classLoader->flags & J9CLASSLOADER_ANON_CLASS_LOADER)) {906/* Anonymous classloader should be scanned on level of classes every time */907GC_ClassLoaderSegmentIterator segmentIterator(classLoader, MEMORY_TYPE_RAM_CLASS);908J9MemorySegment *segment = NULL;909while(NULL != (segment = segmentIterator.nextSegment())) {910GC_ClassHeapIterator classHeapIterator(_javaVM, segment);911J9Class *clazz = NULL;912while(NULL != (clazz = classHeapIterator.nextClass())) {913if((0 == (J9CLASS_EXTENDED_FLAGS(clazz) & J9ClassGCScanned)) && _markingScheme->isMarked(clazz->classObject)) {914J9CLASS_EXTENDED_FLAGS_SET(clazz, J9ClassGCScanned);915916/* Scan class */917GC_ClassIterator objectSlotIterator(env, clazz);918volatile j9object_t *objectSlotPtr = NULL;919while((objectSlotPtr = objectSlotIterator.nextSlot()) != NULL) {920didWork |= _markingScheme->markObject(env, *objectSlotPtr);921}922923GC_ClassIteratorClassSlots classSlotIterator(_javaVM, clazz);924J9Class *classPtr;925while (NULL != (classPtr = classSlotIterator.nextSlot())) {926didWork |= markClass(env, classPtr);927}928}929}930_realtimeGC->condYield(env, 0);931}932} else {933/* Check if the class loader has not been scanned but the class loader is live */934if( !(classLoader->gcFlags & J9_GC_CLASS_LOADER_SCANNED) && _markingScheme->isMarked((J9Object *)classLoader->classLoaderObject)) {935/* Flag the class loader as being scanned */936classLoader->gcFlags |= J9_GC_CLASS_LOADER_SCANNED;937938GC_ClassLoaderSegmentIterator segmentIterator(classLoader, MEMORY_TYPE_RAM_CLASS);939J9MemorySegment *segment = NULL;940J9Class *clazz;941942while(NULL != (segment = segmentIterator.nextSegment())) {943GC_ClassHeapIterator classHeapIterator(_javaVM, segment);944while(NULL != (clazz = classHeapIterator.nextClass())) {945/* Scan class */946GC_ClassIterator objectSlotIterator(env, clazz);947volatile j9object_t *objectSlotPtr = NULL;948while((objectSlotPtr = objectSlotIterator.nextSlot()) != NULL) {949didWork |= _markingScheme->markObject(env, *objectSlotPtr);950}951952GC_ClassIteratorClassSlots classSlotIterator(_javaVM, clazz);953J9Class *classPtr;954while (NULL != (classPtr = classSlotIterator.nextSlot())) {955didWork |= markClass(env, classPtr);956}957}958_realtimeGC->condYield(env, 0);959}960961/* CMVC 131487 */962J9HashTableState walkState;963/*964* We believe that (NULL == classLoader->classHashTable) is set ONLY for DEAD class loader965* so, if this pointer happened to be NULL at this point let it crash here966*/967Assert_MM_true(NULL != classLoader->classHashTable);968/*969* CMVC 178060 : disable hash table growth to prevent hash table entries from being rehashed during GC yield970* while GC was in the middle of iterating the hash table.971*/972hashTableSetFlag(classLoader->classHashTable, J9HASH_TABLE_DO_NOT_REHASH);973clazz = _javaVM->internalVMFunctions->hashClassTableStartDo(classLoader, &walkState, 0);974while (NULL != clazz) {975didWork |= markClass(env, clazz);976clazz = _javaVM->internalVMFunctions->hashClassTableNextDo(&walkState);977978/**979* Jazz103 55784: We cannot rehash the table in the middle of iteration and the Space-opt hashtable cannot grow if980* J9HASH_TABLE_DO_NOT_REHASH is enabled. Don't yield if the hashtable is space-optimized because we run the981* risk of the mutator not being able to grow to accommodate new elements.982*/983if (!hashTableIsSpaceOptimized(classLoader->classHashTable)) {984_realtimeGC->condYield(env, 0);985}986}987/*988* CMVC 178060 : re-enable hash table growth. disable hash table growth to prevent hash table entries from being rehashed during GC yield989* while GC was in the middle of iterating the hash table.990*/991hashTableResetFlag(classLoader->classHashTable, J9HASH_TABLE_DO_NOT_REHASH);992993if (NULL != classLoader->moduleHashTable) {994J9Module **modulePtr = (J9Module **)hashTableStartDo(classLoader->moduleHashTable, &walkState);995while (NULL != modulePtr) {996J9Module * const module = *modulePtr;997998didWork |= _markingScheme->markObject(env, module->moduleObject);999didWork |= _markingScheme->markObject(env, module->moduleName);1000didWork |= _markingScheme->markObject(env, module->version);1001modulePtr = (J9Module**)hashTableNextDo(&walkState);1002}10031004if (classLoader == _javaVM->systemClassLoader) {1005didWork |= _markingScheme->markObject(env, _javaVM->unamedModuleForSystemLoader->moduleObject);1006}1007}1008}1009}1010}1011/* This yield point is for the case when there are lots of classloaders that will be unloaded */1012_realtimeGC->condYield(env, 0);1013}1014return didWork;1015}1016#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */10171018bool1019MM_MetronomeDelegate::doTracing(MM_EnvironmentRealtime* env)1020{1021/* TODO CRGTMP make class tracing concurrent */1022#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)1023if(isDynamicClassUnloadingEnabled()) {1024return doClassTracing(env);1025}1026#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */1027return false;1028}10291030void1031MM_MetronomeDelegate::defaultMemorySpaceAllocated(MM_GCExtensionsBase *extensions, void* defaultMemorySpace)1032{1033J9JavaVM* vm = (J9JavaVM *)extensions->getOmrVM()->_language_vm;10341035vm->heapBase = extensions->heap->getHeapBase();1036vm->heapTop = extensions->heap->getHeapTop();1037}10381039/**1040* This function has to be called at the beginning of continueGC because requestExclusiveVMAccess1041* assumes the current J9VMThread does not have VM Access. All java threads that cause a GC (either1042* System.gc or allocation failure) will have VM access when entering the GC so we have to give it up.1043*1044* @param threadRequestingExclusive the J9VMThread for the MetronomeGCThread that will1045* be requesting exclusive vm access.1046*/1047void1048MM_MetronomeDelegate::preRequestExclusiveVMAccess(OMR_VMThread *threadRequestingExclusive)1049{1050if (threadRequestingExclusive == NULL) {1051return;1052}1053J9VMThread *vmThread = (J9VMThread *)threadRequestingExclusive->_language_vmthread;1054vmThread->javaVM->internalVMFunctions->internalReleaseVMAccess(vmThread);1055}10561057/**1058* This function is called when leaving continueGC so the J9VMThread associated with current1059* MetronomeGCThread will get its VM Access back before returning to run Java code.1060*1061* @param threadRequestingExclusive the J9VMThread for the MetronomeGCThread that requested1062* exclusive vm access.1063*/1064void1065MM_MetronomeDelegate::postRequestExclusiveVMAccess(OMR_VMThread *threadRequestingExclusive)1066{1067if (NULL == threadRequestingExclusive) {1068return;1069}1070J9VMThread *vmThread = (J9VMThread *)threadRequestingExclusive->_language_vmthread;1071vmThread->javaVM->internalVMFunctions->internalAcquireVMAccess(vmThread);1072}1073107410751076/**1077* A call to requestExclusiveVMAccess must be followed by a call to waitForExclusiveVMAccess,1078* but not necessarily by the same thread.1079*1080* @param env the requesting thread.1081* @param block boolean input parameter specifying whether we should block and wait, if another party is requesting at the same time, or we return1082* @return boolean returning whether request was successful or not (make sense only if block is set to FALSE)1083*/1084uintptr_t1085MM_MetronomeDelegate::requestExclusiveVMAccess(MM_EnvironmentBase *env, uintptr_t block, uintptr_t *gcPriority)1086{1087return _javaVM->internalVMFunctions->requestExclusiveVMAccessMetronomeTemp(_javaVM, block, &_vmResponsesRequiredForExclusiveVMAccess, &_jniResponsesRequiredForExclusiveVMAccess, gcPriority);1088}10891090/**1091* Block until the earlier request for exclusive VM access completes.1092* @note This can only be called by the MainGC thread.1093* @param The requesting thread.1094*/1095void1096MM_MetronomeDelegate::waitForExclusiveVMAccess(MM_EnvironmentBase *env, bool waitRequired)1097{1098J9VMThread *mainGCThread = (J9VMThread *)env->getLanguageVMThread();10991100if (waitRequired) {1101_javaVM->internalVMFunctions->waitForExclusiveVMAccessMetronomeTemp((J9VMThread *)env->getLanguageVMThread(), _vmResponsesRequiredForExclusiveVMAccess, _jniResponsesRequiredForExclusiveVMAccess);1102}1103++(mainGCThread->omrVMThread->exclusiveCount);1104}11051106/**1107* Acquire (request and block until success) exclusive VM access.1108* @note This can only be called by the MainGC thread.1109* @param The requesting thread.1110*/1111void1112MM_MetronomeDelegate::acquireExclusiveVMAccess(MM_EnvironmentBase *env, bool waitRequired)1113{1114J9VMThread *mainGCThread = (J9VMThread *)env->getLanguageVMThread();11151116if (waitRequired) {1117_javaVM->internalVMFunctions->acquireExclusiveVMAccessFromExternalThread(_javaVM);1118}1119++(mainGCThread->omrVMThread->exclusiveCount);11201121}11221123/**1124* Release the held exclusive VM access.1125* @note This can only be called by the MainGC thread.1126* @param The requesting thread.1127*/1128void1129MM_MetronomeDelegate::releaseExclusiveVMAccess(MM_EnvironmentBase *env, bool releaseRequired)1130{1131J9VMThread *mainGCThread = (J9VMThread *)env->getLanguageVMThread();11321133--(mainGCThread->omrVMThread->exclusiveCount);1134if (releaseRequired) {1135_javaVM->internalVMFunctions->releaseExclusiveVMAccessMetronome(mainGCThread);1136/* Set the exclusive access response counts to an unusual value,1137* just for debug purposes, so we can detect scenarios, when main1138* thread is waiting for Xaccess with noone requesting it before.1139*/1140_vmResponsesRequiredForExclusiveVMAccess = 0x7fffffff;1141_jniResponsesRequiredForExclusiveVMAccess = 0x7fffffff;1142}1143}11441145#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)1146/**1147* Mark this class1148*/1149bool1150MM_MetronomeDelegate::markClass(MM_EnvironmentRealtime *env, J9Class *clazz)1151{1152bool result = false;1153if (clazz != NULL) {1154result = markClassNoCheck(env, clazz);1155}1156return result;1157}1158#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */11591160#if defined(J9VM_GC_FINALIZATION)1161void1162MM_MetronomeDelegate::scanUnfinalizedObjects(MM_EnvironmentRealtime *env)1163{1164const UDATA maxIndex = getUnfinalizedObjectListCount(env);1165/* first we need to move the current list to the prior list and process the prior list,1166* because if object has not yet become finalizable, we have to re-insert it back to the current list.1167*/1168if (env->_currentTask->synchronizeGCThreadsAndReleaseMain(env, UNIQUE_ID)) {1169GC_OMRVMInterface::flushNonAllocationCaches(env);1170UDATA listIndex;1171for (listIndex = 0; listIndex < maxIndex; ++listIndex) {1172MM_UnfinalizedObjectList *unfinalizedObjectList = &_extensions->unfinalizedObjectLists[listIndex];1173unfinalizedObjectList->startUnfinalizedProcessing();1174}1175env->_currentTask->releaseSynchronizedGCThreads(env);1176}11771178GC_Environment *gcEnv = env->getGCEnvironment();1179GC_FinalizableObjectBuffer buffer(_extensions);1180UDATA listIndex;1181for (listIndex = 0; listIndex < maxIndex; ++listIndex) {1182if(J9MODRON_HANDLE_NEXT_WORK_UNIT(env)) {1183MM_UnfinalizedObjectList *unfinalizedObjectList = &_extensions->unfinalizedObjectLists[listIndex];1184J9Object *object = unfinalizedObjectList->getPriorList();1185UDATA objectsVisited = 0;11861187while (NULL != object) {1188objectsVisited += 1;1189gcEnv->_markJavaStats._unfinalizedCandidates += 1;1190J9Object* next = _extensions->accessBarrier->getFinalizeLink(object);1191if (_markingScheme->markObject(env, object)) {1192/* object was not previously marked -- it is now finalizable so push it to the local buffer */1193buffer.add(env, object);1194gcEnv->_markJavaStats._unfinalizedEnqueued += 1;1195_finalizationRequired = true;1196} else {1197/* object was already marked. It is still unfinalized */1198gcEnv->_unfinalizedObjectBuffer->add(env, object);1199}1200object = next;1201if (UNFINALIZED_OBJECT_YIELD_CHECK_INTERVAL == objectsVisited ) {1202_scheduler->condYieldFromGC(env);1203objectsVisited = 0;1204}1205}1206_scheduler->condYieldFromGC(env);1207}1208}12091210/* Flush the local buffer of finalizable objects to the global list */1211buffer.flush(env);12121213/* restore everything to a flushed state before exiting */1214gcEnv->_unfinalizedObjectBuffer->flush(env);1215}1216#endif /* J9VM_GC_FINALIZATION */12171218void1219MM_MetronomeDelegate::scanOwnableSynchronizerObjects(MM_EnvironmentRealtime *env)1220{1221const UDATA maxIndex = getOwnableSynchronizerObjectListCount(env);12221223/* first we need to move the current list to the prior list and process the prior list,1224* because if object has been marked, we have to re-insert it back to the current list.1225*/1226if (env->_currentTask->synchronizeGCThreadsAndReleaseMain(env, UNIQUE_ID)) {1227GC_OMRVMInterface::flushNonAllocationCaches(env);1228UDATA listIndex;1229for (listIndex = 0; listIndex < maxIndex; ++listIndex) {1230MM_OwnableSynchronizerObjectList *ownableSynchronizerObjectList = &_extensions->getOwnableSynchronizerObjectLists()[listIndex];1231ownableSynchronizerObjectList->startOwnableSynchronizerProcessing();1232}1233env->_currentTask->releaseSynchronizedGCThreads(env);1234}12351236GC_Environment *gcEnv = env->getGCEnvironment();1237MM_OwnableSynchronizerObjectBuffer *buffer = gcEnv->_ownableSynchronizerObjectBuffer;1238UDATA listIndex;1239for (listIndex = 0; listIndex < maxIndex; ++listIndex) {1240MM_OwnableSynchronizerObjectList *list = &_extensions->getOwnableSynchronizerObjectLists()[listIndex];1241if (!list->wasEmpty()) {1242if (J9MODRON_HANDLE_NEXT_WORK_UNIT(env)) {1243J9Object *object = list->getPriorList();1244UDATA objectsVisited = 0;1245while (NULL != object) {1246objectsVisited += 1;1247gcEnv->_markJavaStats._ownableSynchronizerCandidates += 1;12481249/* Get next before adding it to the buffer, as buffer modifies OwnableSynchronizerLink */1250J9Object* next = _extensions->accessBarrier->getOwnableSynchronizerLink(object);1251if (_markingScheme->isMarked(object)) {1252buffer->add(env, object);1253} else {1254gcEnv->_markJavaStats._ownableSynchronizerCleared += 1;1255}1256object = next;12571258if (OWNABLE_SYNCHRONIZER_OBJECT_YIELD_CHECK_INTERVAL == objectsVisited ) {1259_scheduler->condYieldFromGC(env);1260objectsVisited = 0;1261}1262}1263_scheduler->condYieldFromGC(env);1264}1265}1266}1267/* restore everything to a flushed state before exiting */1268buffer->flush(env);1269}12701271void1272MM_MetronomeDelegate::scanWeakReferenceObjects(MM_EnvironmentRealtime *env)1273{1274GC_Environment *gcEnv = env->getGCEnvironment();1275Assert_MM_true(gcEnv->_referenceObjectBuffer->isEmpty());1276const UDATA maxIndex = getReferenceObjectListCount(env);1277UDATA listIndex;1278for (listIndex = 0; listIndex < maxIndex; ++listIndex) {1279if(J9MODRON_HANDLE_NEXT_WORK_UNIT(env)) {1280MM_ReferenceObjectList *referenceObjectList = &_extensions->referenceObjectLists[listIndex];1281referenceObjectList->startWeakReferenceProcessing();1282processReferenceList(env, NULL, referenceObjectList->getPriorWeakList(), &gcEnv->_markJavaStats._weakReferenceStats);1283_scheduler->condYieldFromGC(env);1284}1285}1286Assert_MM_true(gcEnv->_referenceObjectBuffer->isEmpty());1287}12881289void1290MM_MetronomeDelegate::scanSoftReferenceObjects(MM_EnvironmentRealtime *env)1291{1292GC_Environment *gcEnv = env->getGCEnvironment();1293Assert_MM_true(gcEnv->_referenceObjectBuffer->isEmpty());1294const UDATA maxIndex = getReferenceObjectListCount(env);1295UDATA listIndex;1296for (listIndex = 0; listIndex < maxIndex; ++listIndex) {1297if(J9MODRON_HANDLE_NEXT_WORK_UNIT(env)) {1298MM_ReferenceObjectList *referenceObjectList = &_extensions->referenceObjectLists[listIndex];1299referenceObjectList->startSoftReferenceProcessing();1300processReferenceList(env, NULL, referenceObjectList->getPriorSoftList(), &gcEnv->_markJavaStats._softReferenceStats);1301_scheduler->condYieldFromGC(env);1302}1303}1304Assert_MM_true(gcEnv->_referenceObjectBuffer->isEmpty());1305}13061307void1308MM_MetronomeDelegate::scanPhantomReferenceObjects(MM_EnvironmentRealtime *env)1309{1310GC_Environment *gcEnv = env->getGCEnvironment();1311/* unfinalized processing may discover more phantom reference objects */1312gcEnv->_referenceObjectBuffer->flush(env);1313const UDATA maxIndex = getReferenceObjectListCount(env);1314UDATA listIndex;1315for (listIndex = 0; listIndex < maxIndex; ++listIndex) {1316if(J9MODRON_HANDLE_NEXT_WORK_UNIT(env)) {1317MM_ReferenceObjectList *referenceObjectList = &_extensions->referenceObjectLists[listIndex];1318referenceObjectList->startPhantomReferenceProcessing();1319processReferenceList(env, NULL, referenceObjectList->getPriorPhantomList(), &gcEnv->_markJavaStats._phantomReferenceStats);1320_scheduler->condYieldFromGC(env);1321}1322}1323Assert_MM_true(gcEnv->_referenceObjectBuffer->isEmpty());1324}13251326void1327MM_MetronomeDelegate::processReferenceList(MM_EnvironmentRealtime *env, MM_HeapRegionDescriptorRealtime *region, J9Object* headOfList, MM_ReferenceStats *referenceStats)1328{1329UDATA objectsVisited = 0;1330#if defined(J9VM_GC_FINALIZATION)1331GC_FinalizableReferenceBuffer buffer(_extensions);1332#endif /* J9VM_GC_FINALIZATION */1333J9Object* referenceObj = headOfList;13341335while (NULL != referenceObj) {1336objectsVisited += 1;1337referenceStats->_candidates += 1;13381339Assert_MM_true(_markingScheme->isMarked(referenceObj));13401341J9Object* nextReferenceObj = _extensions->accessBarrier->getReferenceLink(referenceObj);13421343GC_SlotObject referentSlotObject(_extensions->getOmrVM(), J9GC_J9VMJAVALANGREFERENCE_REFERENT_ADDRESS(env, referenceObj));1344J9Object *referent = referentSlotObject.readReferenceFromSlot();1345if (NULL != referent) {1346UDATA referenceObjectType = J9CLASS_FLAGS(J9GC_J9OBJECT_CLAZZ(referenceObj, env)) & J9AccClassReferenceMask;1347if (_markingScheme->isMarked(referent)) {1348if (J9AccClassReferenceSoft == referenceObjectType) {1349U_32 age = J9GC_J9VMJAVALANGSOFTREFERENCE_AGE(env, referenceObj);1350if (age < _extensions->getMaxSoftReferenceAge()) {1351/* Soft reference hasn't aged sufficiently yet - increment the age */1352J9GC_J9VMJAVALANGSOFTREFERENCE_AGE(env, referenceObj) = age + 1;1353}1354}1355} else {1356/* transition the state to cleared */1357Assert_MM_true(GC_ObjectModel::REF_STATE_INITIAL == J9GC_J9VMJAVALANGREFERENCE_STATE(env, referenceObj));1358J9GC_J9VMJAVALANGREFERENCE_STATE(env, referenceObj) = GC_ObjectModel::REF_STATE_CLEARED;13591360referenceStats->_cleared += 1;13611362/* Phantom references keep it's referent alive in Java 8 and doesn't in Java 9 and later */1363if ((J9AccClassReferencePhantom == referenceObjectType) && ((J2SE_VERSION(_javaVM) & J2SE_VERSION_MASK) <= J2SE_18)) {1364/* Scanning will be done after the enqueuing */1365_markingScheme->markObject(env, referent);1366} else {1367referentSlotObject.writeReferenceToSlot(NULL);1368}1369#if defined(J9VM_GC_FINALIZATION)1370/* Check if the reference has a queue */1371if (0 != J9GC_J9VMJAVALANGREFERENCE_QUEUE(env, referenceObj)) {1372/* Reference object can be enqueued onto the finalizable list */1373buffer.add(env, referenceObj);1374referenceStats->_enqueued += 1;1375/* Flag for the finalizer */1376_finalizationRequired = true;1377}1378#endif /* J9VM_GC_FINALIZATION */1379}1380}1381referenceObj = nextReferenceObj;1382if (REFERENCE_OBJECT_YIELD_CHECK_INTERVAL == objectsVisited) {1383_scheduler->condYieldFromGC(env);1384objectsVisited = 0;1385}1386}1387#if defined(J9VM_GC_FINALIZATION)1388buffer.flush(env);1389#endif /* J9VM_GC_FINALIZATION */1390}13911392/**1393* Mark all of the roots. The system and application classloaders need to be set1394* to marked/scanned before root marking begins.1395*1396* @note Once the root lists all have barriers this code may change to call rootScanner.scanRoots();1397*1398*/1399void1400MM_MetronomeDelegate::markLiveObjectsRoots(MM_EnvironmentRealtime *env)1401{1402MM_RealtimeMarkingSchemeRootMarker rootMarker(env, _realtimeGC);1403env->setRootScanner(&rootMarker);1404#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)1405rootMarker.setClassDataAsRoots(!isDynamicClassUnloadingEnabled());1406#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */14071408/* Mark root set classes */1409#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)1410if(env->isMainThread()) {1411/* TODO: This code belongs somewhere else? */1412/* Setting the permanent class loaders to scanned without a locked operation is safe1413* Class loaders will not be rescanned until a thread synchronize is executed1414*/1415if(isDynamicClassUnloadingEnabled()) {1416((J9ClassLoader *)_javaVM->systemClassLoader)->gcFlags |= J9_GC_CLASS_LOADER_SCANNED;1417_markingScheme->markObject(env, (J9Object *)((J9ClassLoader *)_javaVM->systemClassLoader)->classLoaderObject);1418if(_javaVM->applicationClassLoader) {1419((J9ClassLoader *)_javaVM->applicationClassLoader)->gcFlags |= J9_GC_CLASS_LOADER_SCANNED;1420_markingScheme->markObject(env, (J9Object *)((J9ClassLoader *)_javaVM->applicationClassLoader)->classLoaderObject);1421}1422}1423}1424#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */14251426/* Note: it's important to scan the finalizable objects queue (atomically) before the1427* threads, because the finalizer threads are among the threads and, once any one of1428* them is scanned and then allowed to execute, any object it takes off the finalizer1429* queue had better also be scanned. An alternative would be to put a special read1430* barrier in the queue-removal action but controlling the order is an easy solution.1431*1432* It is also important to scan JNI global references after scanning threads, because1433* the JNI global reference barrier is executed at deletion time, not creation time.1434* We could have barriers in both, but controlling the order is an easy solution.1435*1436*1437* The Metronome write barrier ensures that no unscanned thread can expose an object1438* to other threads without it becoming a root and no scanned thread can make an1439* object that was once reachable unreachable until it has been traced ("snapshot at1440* the beginning with a fuzzy snapshot"). This eliminates other order dependencies1441* between portions of the scan or requirements that multiple phases be done as an1442* atomic unit. However, some phases are still done atomically because we have not1443* yet determined whether the iterators that they use are safe and complete and have1444* not even analyzed in all cases whether correctness depends on completeness.1445*/1446if (env->_currentTask->synchronizeGCThreadsAndReleaseMain(env, UNIQUE_ID)) {1447#if defined(J9VM_GC_FINALIZATION)1448/* Note: if iterators are safe in scanFinalizableObjects, disableYield() could be1449* removed.1450*/1451env->disableYield();1452rootMarker.scanFinalizableObjects(env);1453env->enableYield();1454_scheduler->condYieldFromGC(env);1455#endif /* J9VM_GC_FINALIZATION */1456#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)1457if (!isDynamicClassUnloadingEnabled()) {1458#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */1459/* We are scanning all classes, no need to include stack frame references */1460rootMarker.setIncludeStackFrameClassReferences(false);1461#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)1462} else {1463rootMarker.setIncludeStackFrameClassReferences(true);1464}1465#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */1466env->_currentTask->releaseSynchronizedGCThreads(env);1467}14681469rootMarker.scanThreads(env);14701471if (env->_currentTask->synchronizeGCThreadsAndReleaseMain(env, UNIQUE_ID)) {1472_extensions->newThreadAllocationColor = GC_MARK;1473_realtimeGC->disableDoubleBarrier(env);1474if (_realtimeGC->verbose(env) >= 3) {1475rootMarker.reportThreadCount(env);1476}14771478/* Note: if iterators are safe for some or all remaining atomic root categories,1479* disableYield() could be removed or moved inside scanAtomicRoots.1480*/1481env->disableYield();1482rootMarker.scanAtomicRoots(env);1483env->enableYield();1484rootMarker.scanIncrementalRoots(env);14851486env->_currentTask->releaseSynchronizedGCThreads(env);1487}14881489env->setRootScanner(NULL);1490}14911492void1493MM_MetronomeDelegate::markLiveObjectsScan(MM_EnvironmentRealtime *env)1494{1495env->getGCEnvironment()->_referenceObjectBuffer->flush(env);1496}14971498void1499MM_MetronomeDelegate::markLiveObjectsComplete(MM_EnvironmentRealtime *env)1500{1501/* Process reference objects and finalizable objects. */1502MM_RealtimeMarkingSchemeRootClearer rootScanner(env, _realtimeGC);1503env->setRootScanner(&rootScanner);1504rootScanner.scanClearable(env);1505env->setRootScanner(NULL);1506Assert_MM_true(env->getGCEnvironment()->_referenceObjectBuffer->isEmpty());1507}15081509void1510MM_MetronomeDelegate::checkReferenceBuffer(MM_EnvironmentRealtime *env)1511{1512Assert_MM_true(env->getGCEnvironment()->_referenceObjectBuffer->isEmpty());1513}15141515void1516MM_MetronomeDelegate::setUnmarkedImpliesCleared()1517{1518_unmarkedImpliesCleared = true;1519}15201521void1522MM_MetronomeDelegate::unsetUnmarkedImpliesCleared()1523{1524/* This flag is set during the soft reference scanning just before unmarked references are to be1525* cleared. It's used to prevent objects that are going to be cleared (e.g. referent that is not marked,1526* or unmarked string constant) from escaping.1527*/1528_unmarkedImpliesCleared = false;1529_unmarkedImpliesStringsCleared = false;15301531#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)1532/* enable to use mark information to detect is class dead */1533_unmarkedImpliesClasses = true;1534#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */1535}15361537#endif /* defined(J9VM_GC_REALTIME) */1538153915401541