Path: blob/master/runtime/gc_glue_java/GlobalCollectorDelegate.cpp
5985 views
/*******************************************************************************1* Copyright (c) 2017, 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 "j9consts.h"25#include "j9nonbuilder.h"26#include "j9nongenerated.h"27#include "mmprivatehook.h"28#include "ModronAssertions.h"29#include "omrgcconsts.h"30#include "omrhookable.h"3132#include "ClassHeapIterator.hpp"33#include "ClassLoaderIterator.hpp"34#include "ClassLoaderManager.hpp"35#include "ClassLoaderSegmentIterator.hpp"36#include "ClassUnloadStats.hpp"37#include "ConfigurationDelegate.hpp"38#include "EnvironmentBase.hpp"39#include "FinalizerSupport.hpp"40#include "FrequentObjectsStats.hpp"41#include "GCExtensionsBase.hpp"42#include "GCObjectEvents.hpp"43#include "GlobalCollectorDelegate.hpp"44#include "Heap.hpp"45#include "HeapRegionDescriptorStandard.hpp"46#include "HeapRegionIteratorStandard.hpp"47#include "MarkingDelegate.hpp"48#include "MarkingScheme.hpp"49#include "MemorySubSpace.hpp"50#include "ObjectModel.hpp"51#include "ParallelGlobalGC.hpp"52#include "ParallelHeapWalker.hpp"53#if defined(OMR_ENV_DATA64) && defined(OMR_GC_FULL_POINTERS)54#include "ReadBarrierVerifier.hpp"55#endif /* defined(OMR_ENV_DATA64) && defined(OMR_GC_FULL_POINTERS) */56#include "ReferenceChainWalkerMarkMap.hpp"57#include "ReferenceObjectList.hpp"58#include "ScavengerJavaStats.hpp"59#include "StandardAccessBarrier.hpp"60#include "VMThreadListIterator.hpp"6162#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)63/**64* Function to fix single object on the heap but only if its class is marked as dying. Other dead objects65* who still have valid classes (that is, classes not about to be unloaded) are ignored (see CMVC 12295966* for the rationale behind this function).67*/68static void69fixObjectIfClassDying(OMR_VMThread *omrVMThread, MM_HeapRegionDescriptor *region, omrobjectptr_t object, void *userData)70{71/* Check to see if the object's class is being unloaded. If so, it can't be left as dark matter so abandon it */72uintptr_t classFlags = J9CLASS_FLAGS(J9GC_J9OBJECT_CLAZZ_CMP(object, OMRVMTHREAD_COMPRESS_OBJECT_REFERENCES(omrVMThread)));73if (0 != (classFlags & J9AccClassDying)) {74MM_MemorySubSpace *memorySubSpace = region->getSubSpace();75uintptr_t deadObjectByteSize = MM_GCExtensions::getExtensions(omrVMThread)->objectModel.getConsumedSizeInBytesWithHeader(object);76memorySubSpace->abandonHeapChunk(object, ((U_8*)object) + deadObjectByteSize);77/* the userdata is a counter of dead objects fixed up so increment it here as a uintptr_t */78*((uintptr_t *)userData) += 1;79}80}81#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */8283bool84MM_GlobalCollectorDelegate::initialize(MM_EnvironmentBase *env, MM_GlobalCollector *globalCollector, MM_MarkingScheme *markingScheme)85{86_markingScheme = markingScheme;87_globalCollector = globalCollector;88_javaVM = (J9JavaVM*)env->getLanguageVM();89_extensions = MM_GCExtensions::getExtensions(env);9091/* This delegate is used primarily by MM_ParallelGlobalGC but is declared in base MM_GlobalCollector92* class which is base class for MM_IncrementalGlobalGC (balanced) and MM_RealtimeGC (realtime). The93* only MM_GlobalCollector methods (postCollect and isTimeForGlobalGCKickoff) that use this94* delegate do not use _globalCollector or _markingScheme, so these are required to be NULLed for the95* balanced and realtime GC policies (for safety), and not NULL for standard GC policies.96*/97Assert_MM_true((NULL != _globalCollector) == _extensions->isStandardGC());98Assert_MM_true((NULL != _markingScheme) == _extensions->isStandardGC());99100/* Balanced and realtime polices will instantiate their own access barrier */101if (_extensions->isStandardGC()) {102103#if defined(OMR_ENV_DATA64) && defined(OMR_GC_FULL_POINTERS)104if (1 == _extensions->fvtest_enableReadBarrierVerification) {105_extensions->accessBarrier = MM_ReadBarrierVerifier::newInstance(env, _markingScheme);106} else107#endif /* defined(OMR_ENV_DATA64) && defined(OMR_GC_FULL_POINTERS) */108{109_extensions->accessBarrier = MM_StandardAccessBarrier::newInstance(env, _markingScheme);110}111112if (NULL == _extensions->accessBarrier) {113return false;114}115}116117return true;118}119120void121MM_GlobalCollectorDelegate::tearDown(MM_EnvironmentBase *env)122{123if (_extensions->isStandardGC() && (NULL != _extensions->accessBarrier)) {124_extensions->accessBarrier->kill(env);125_extensions->accessBarrier = NULL;126}127}128129void130MM_GlobalCollectorDelegate::mainThreadGarbageCollectStarted(MM_EnvironmentBase *env)131{132/* Clear the java specific mark stats */133_extensions->markJavaStats.clear();134135#if defined(J9VM_GC_MODRON_COMPACTION)136_criticalSectionCount = MM_StandardAccessBarrier::getJNICriticalRegionCount(_extensions);137#endif /* J9VM_GC_MODRON_COMPACTION */138139#if defined(J9VM_GC_MODRON_SCAVENGER)140if (_extensions->scavengerEnabled) {141/* clear scavenger stats for correcting the ownableSynchronizerObjects stats, only in generational gc */142_extensions->scavengerJavaStats.clearOwnableSynchronizerCounts();143}144#endif /* defined(J9VM_GC_MODRON_SCAVENGER) */145146#if defined(J9VM_GC_FINALIZATION)147/* this should not be set by the GC since it is used by components in order to record that they performed some operation which will require that we do some finalization */148_finalizationRequired = false;149#endif /* J9VM_GC_FINALIZATION */150151#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)152bool forceUnloading = false;153154/* Set the dynamic class unloading flag based on command line and runtime state */155switch (_extensions->dynamicClassUnloading) {156case MM_GCExtensions::DYNAMIC_CLASS_UNLOADING_NEVER:157_extensions->runtimeCheckDynamicClassUnloading = false;158forceUnloading = false;159break;160case MM_GCExtensions::DYNAMIC_CLASS_UNLOADING_ALWAYS:161_extensions->runtimeCheckDynamicClassUnloading = true;162forceUnloading = true;163break;164case MM_GCExtensions::DYNAMIC_CLASS_UNLOADING_ON_CLASS_LOADER_CHANGES:165forceUnloading = env->_cycleState->_gcCode.isAggressiveGC();166_extensions->runtimeCheckDynamicClassUnloading = forceUnloading || _extensions->classLoaderManager->isTimeForClassUnloading(env);167break;168default:169break;170}171172if (_extensions->runtimeCheckDynamicClassUnloading) {173/* request collector enter classUnloadMutex if possible (if forceUnloading is set - always)*/174_extensions->runtimeCheckDynamicClassUnloading = enterClassUnloadMutex(env, forceUnloading);175}176#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */177}178179void180MM_GlobalCollectorDelegate::mainThreadGarbageCollectFinished(MM_EnvironmentBase *env, bool compactedThisCycle)181{182/* Check that all reference object lists are empty:183* lists must be processed at Mark and nothing should be flushed after184*/185UDATA listCount = _extensions->gcThreadCount;186MM_HeapRegionDescriptorStandard *region = NULL;187GC_HeapRegionIteratorStandard regionIterator(_extensions->heap->getHeapRegionManager());188while(NULL != (region = regionIterator.nextRegion())) {189/* check all lists for regions, they should be empty */190MM_HeapRegionDescriptorStandardExtension *regionExtension = MM_ConfigurationDelegate::getHeapRegionDescriptorStandardExtension(env, region);191for (UDATA i = 0; i < listCount; i++) {192MM_ReferenceObjectList *list = ®ionExtension->_referenceObjectLists[i];193Assert_MM_true(list->isWeakListEmpty());194Assert_MM_true(list->isSoftListEmpty());195Assert_MM_true(list->isPhantomListEmpty());196}197}198199#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)200MM_MarkingDelegate::clearClassLoadersScannedFlag(env);201202/* If we allowed class unloading during this gc, we must release the classUnloadMutex */203if (_extensions->runtimeCheckDynamicClassUnloading) {204exitClassUnloadMutex(env);205}206207/* make sure that we are going to get at least some number of bytes back since this will otherwise waste time in monitor operations and potentially get exclusive in order to do nothing */208J9VMThread *vmThread = (J9VMThread *)env->getLanguageVMThread();209uintptr_t reclaimableMemory = _extensions->classLoaderManager->reclaimableMemory();210if (reclaimableMemory > 0) {211if (!compactedThisCycle) {212bool isExplicitGC = env->_cycleState->_gcCode.isExplicitGC();213if (isExplicitGC || (reclaimableMemory > _extensions->deadClassLoaderCacheSize)) {214/* fix the heap */215Trc_MM_DoFixHeapForUnload_Entry(vmThread, MEMORY_TYPE_RAM);216MM_ParallelGlobalGC *parallelGlobalCollector = (MM_ParallelGlobalGC *)_globalCollector;217UDATA fixedObjectCount = parallelGlobalCollector->fixHeapForWalk(env, MEMORY_TYPE_RAM, FIXUP_CLASS_UNLOADING, fixObjectIfClassDying);218if (0 < fixedObjectCount) {219Trc_MM_DoFixHeapForUnload_Exit(vmThread, fixedObjectCount);220} else {221Trc_MM_DoFixHeapForUnload_ExitNotNeeded(vmThread);222}223/* now flush the cached segments */224Trc_MM_FlushUndeadSegments_Entry(vmThread, isExplicitGC ? "SystemGC" : "Dead Class Loader Cache Full");225_extensions->classLoaderManager->flushUndeadSegments(env);226Trc_MM_FlushUndeadSegments_Exit(vmThread);227}228} else {229#if defined(J9VM_GC_MODRON_COMPACTION)230Trc_MM_FlushUndeadSegments_Entry(vmThread, "Compaction");231_extensions->classLoaderManager->flushUndeadSegments(env);232Trc_MM_FlushUndeadSegments_Exit(vmThread);233#else234Assert_MM_unreachable();235#endif /* defined(J9VM_GC_MODRON_COMPACTION) */236}237}238#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */239}240241void242MM_GlobalCollectorDelegate::postMarkProcessing(MM_EnvironmentBase *env)243{244#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)245if (_extensions->runtimeCheckDynamicClassUnloading != 0) {246PORT_ACCESS_FROM_ENVIRONMENT(env);247OMR_VMThread *vmThread = env->getOmrVMThread();248Trc_MM_ClassUnloadingStart((J9VMThread *)vmThread->_language_vmthread);249TRIGGER_J9HOOK_MM_PRIVATE_CLASS_UNLOADING_START(250_extensions->privateHookInterface,251vmThread,252j9time_hires_clock(),253J9HOOK_MM_PRIVATE_CLASS_UNLOADING_START);254255unloadDeadClassLoaders(env);256257MM_ClassUnloadStats *classUnloadStats = &_extensions->globalGCStats.classUnloadStats;258Trc_MM_ClassUnloadingEnd((J9VMThread *)vmThread->_language_vmthread,259classUnloadStats->_classLoaderUnloadedCount,260classUnloadStats->_classesUnloadedCount);261TRIGGER_J9HOOK_MM_CLASS_UNLOADING_END(262_extensions->hookInterface,263(J9VMThread *)vmThread->_language_vmthread,264j9time_hires_clock(),265J9HOOK_MM_CLASS_UNLOADING_END,266classUnloadStats->_endTime - classUnloadStats->_startTime,267classUnloadStats->_classLoaderUnloadedCount,268classUnloadStats->_classesUnloadedCount,269classUnloadStats->_classUnloadMutexQuiesceTime,270classUnloadStats->_endSetupTime - classUnloadStats->_startSetupTime,271classUnloadStats->_endScanTime - classUnloadStats->_startScanTime,272classUnloadStats->_endPostTime - classUnloadStats->_startPostTime);273274/* If there was dynamic class unloading checks during the run, record the new number of class275* loaders last seen during a DCU pass276*/277_extensions->classLoaderManager->setLastUnloadNumOfClassLoaders();278_extensions->classLoaderManager->setLastUnloadNumOfAnonymousClasses();279}280#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */281282#if defined(J9VM_GC_FINALIZATION)283if (_finalizationRequired) {284/* Signal the finalizer */285omrthread_monitor_enter(_javaVM->finalizeMainMonitor);286_javaVM->finalizeMainFlags |= J9_FINALIZE_FLAGS_MAIN_WAKE_UP;287omrthread_monitor_notify_all(_javaVM->finalizeMainMonitor);288omrthread_monitor_exit(_javaVM->finalizeMainMonitor);289}290#endif /* J9VM_GC_FINALIZATION */291}292293bool294MM_GlobalCollectorDelegate::isAllowUserHeapWalk()295{296/* Enable only if required for debugging. */297return (0 != (_javaVM->requiredDebugAttributes & J9VM_DEBUG_ATTRIBUTE_ALLOW_USER_HEAP_WALK));298}299300void301MM_GlobalCollectorDelegate::prepareHeapForWalk(MM_EnvironmentBase *env)302{303#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)304/* Clear the appropriate flags of all classLoaders */305GC_ClassLoaderIterator classLoaderIterator(_javaVM->classLoaderBlocks);306J9ClassLoader *classLoader;307while((classLoader = classLoaderIterator.nextSlot()) != NULL) {308classLoader->gcFlags &= ~J9_GC_CLASS_LOADER_SCANNED;309}310#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */311}312313#if defined(OMR_ENV_DATA64) && defined(OMR_GC_FULL_POINTERS)314void315MM_GlobalCollectorDelegate::poisonSlots(MM_EnvironmentBase *env)316{317((MM_ReadBarrierVerifier *)_extensions->accessBarrier)->poisonSlots(env);318}319320void321MM_GlobalCollectorDelegate::healSlots(MM_EnvironmentBase *env)322{323((MM_ReadBarrierVerifier *)_extensions->accessBarrier)->healSlots(env);324}325#endif /* defined(OMR_ENV_DATA64) && defined(OMR_GC_FULL_POINTERS) */326327bool328MM_GlobalCollectorDelegate::heapAddRange(MM_EnvironmentBase *env, MM_MemorySubSpace *subspace, UDATA size, void *lowAddress, void *highAddress)329{330if(NULL != _extensions->referenceChainWalkerMarkMap) {331return _extensions->referenceChainWalkerMarkMap->heapAddRange(env, size, lowAddress, highAddress);332}333return true;334}335336bool337MM_GlobalCollectorDelegate::heapRemoveRange(MM_EnvironmentBase *env, MM_MemorySubSpace *subspace, UDATA size, void *lowAddress, void *highAddress, void *lowValidAddress, void *highValidAddress)338{339if (NULL != _extensions->referenceChainWalkerMarkMap) {340return _extensions->referenceChainWalkerMarkMap->heapRemoveRange(env, size, lowAddress, highAddress, lowValidAddress, highValidAddress);341}342return true;343}344345bool346MM_GlobalCollectorDelegate::isTimeForGlobalGCKickoff()347{348bool result = false;349350#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)351uintptr_t numClassLoaderBlocks = pool_numElements(_javaVM->classLoaderBlocks);352uintptr_t numAnonymousClasses = _javaVM->anonClassCount;353354Trc_MM_GlobalCollector_isTimeForGlobalGCKickoff_Entry(355_extensions->dynamicClassUnloading,356numClassLoaderBlocks,357_extensions->dynamicClassUnloadingKickoffThreshold,358_extensions->classLoaderManager->getLastUnloadNumOfClassLoaders());359360Trc_MM_GlobalCollector_isTimeForGlobalGCKickoff_anonClasses(361numAnonymousClasses,362_extensions->classLoaderManager->getLastUnloadNumOfAnonymousClasses(),363_extensions->classUnloadingAnonymousClassWeight364);365366Assert_MM_true(numAnonymousClasses >= _extensions->classLoaderManager->getLastUnloadNumOfAnonymousClasses());367368if ((0 != _extensions->dynamicClassUnloadingKickoffThreshold) && (_extensions->dynamicClassUnloading != MM_GCExtensions::DYNAMIC_CLASS_UNLOADING_NEVER)) {369uintptr_t recentlyLoaded = (uintptr_t) ((numAnonymousClasses - _extensions->classLoaderManager->getLastUnloadNumOfAnonymousClasses()) * _extensions->classUnloadingAnonymousClassWeight);370/* todo aryoung: getLastUnloadNumOfClassLoaders() includes the class loaders which371* were unloaded but still required finalization when the last classUnloading occured.372* This means that the threshold check is wrong when there are classes which require finalization.373* Temporarily make sure that we do not create a negative recently loaded.374*/375if (numClassLoaderBlocks > _extensions->classLoaderManager->getLastUnloadNumOfClassLoaders()) {376recentlyLoaded += (numClassLoaderBlocks - _extensions->classLoaderManager->getLastUnloadNumOfClassLoaders());377}378result = recentlyLoaded >= _extensions->dynamicClassUnloadingKickoffThreshold;379}380381Trc_MM_GlobalCollector_isTimeForGlobalGCKickoff_Exit(result ? "true" : "false");382#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */383384return result;385}386387void388MM_GlobalCollectorDelegate::postCollect(MM_EnvironmentBase* env, MM_MemorySubSpace* subSpace)389{390/* update the dynamic soft reference age based on the free space in the oldest generation after this collection so we know how many to clear next time */391MM_Heap* heap = (MM_Heap*)_extensions->heap;392uintptr_t heapSize = heap->getActiveMemorySize(MEMORY_TYPE_OLD);393uintptr_t freeSize = heap->getApproximateActiveFreeMemorySize(MEMORY_TYPE_OLD);394double percentFree = ((double)freeSize) / ((double)heapSize);395_extensions->dynamicMaxSoftReferenceAge = (uintptr_t)(percentFree * (double)(_extensions->maxSoftReferenceAge));396Assert_MM_true(_extensions->dynamicMaxSoftReferenceAge <= _extensions->maxSoftReferenceAge);397}398399#if defined(J9VM_GC_MODRON_COMPACTION)400CompactPreventedReason401MM_GlobalCollectorDelegate::checkIfCompactionShouldBePrevented(MM_EnvironmentBase *env)402{403CompactPreventedReason reason = COMPACT_PREVENTED_NONE;404/* If there are active JNI critical regions then objects can't be moved. */405406if (0 < _criticalSectionCount) {407reason = COMPACT_PREVENTED_CRITICAL_REGIONS;408}409410return reason;411}412#endif /* J9VM_GC_MODRON_COMPACTION */413414415#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)416bool417MM_GlobalCollectorDelegate::enterClassUnloadMutex(MM_EnvironmentBase *env, bool force)418{419bool result = true;420421MM_ClassUnloadStats *classUnloadStats = &_extensions->globalGCStats.classUnloadStats;422if (force) {423classUnloadStats->_classUnloadMutexQuiesceTime = _extensions->classLoaderManager->enterClassUnloadMutex(env);424} else {425classUnloadStats->_classUnloadMutexQuiesceTime = J9CONST64(0);426result = _extensions->classLoaderManager->tryEnterClassUnloadMutex(env);427}428429return result;430}431432void433MM_GlobalCollectorDelegate::exitClassUnloadMutex(MM_EnvironmentBase *env)434{435_extensions->classLoaderManager->exitClassUnloadMutex(env);436}437438void439MM_GlobalCollectorDelegate::unloadDeadClassLoaders(MM_EnvironmentBase *env)440{441Trc_MM_ParallelGlobalGC_unloadDeadClassLoaders_entry(env->getLanguageVMThread());442PORT_ACCESS_FROM_ENVIRONMENT(env);443MM_ClassUnloadStats *classUnloadStats = &_extensions->globalGCStats.classUnloadStats;444445/* The list of classLoaders to be unloaded by cleanUpClassLoadersEnd is rooted in unloadLink */446447/* set the vmState whilst we're unloading classes */448UDATA vmState = env->pushVMstate(OMRVMSTATE_GC_CLEANING_METADATA);449450/* Count the classes we're unloading and perform class-specific clean up work for each unloading class.451* If we're unloading any classes, perform common class-unloading clean up.452*/453classUnloadStats->_startTime = j9time_hires_clock();454classUnloadStats->_startSetupTime = classUnloadStats->_startTime;455456J9ClassLoader *classLoadersUnloadedList = _extensions->classLoaderManager->identifyClassLoadersToUnload(env, _markingScheme->getMarkMap(), classUnloadStats);457_extensions->classLoaderManager->cleanUpClassLoadersStart(env, classLoadersUnloadedList, _markingScheme->getMarkMap(), classUnloadStats);458459classUnloadStats->_endSetupTime = j9time_hires_clock();460classUnloadStats->_startScanTime = classUnloadStats->_endSetupTime;461462/* The list of classLoaders to be unloaded by cleanUpClassLoadersEnd is rooted in unloadLink */463J9ClassLoader *unloadLink = NULL;464J9MemorySegment *reclaimedSegments = NULL;465_extensions->classLoaderManager->cleanUpClassLoaders(env, classLoadersUnloadedList, &reclaimedSegments, &unloadLink, &_finalizationRequired);466467/* Free the class memory segments associated with dead classLoaders, unload (free) the dead classLoaders that don't468* require finalization, and perform any final clean up after the dead classLoaders are gone.469*/470classUnloadStats->_endScanTime = j9time_hires_clock();471classUnloadStats->_startPostTime = classUnloadStats->_endScanTime;472473/* 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) */474_extensions->classLoaderManager->enqueueUndeadClassSegments(reclaimedSegments);475_extensions->classLoaderManager->cleanUpClassLoadersEnd(env, unloadLink);476477classUnloadStats->_endPostTime = j9time_hires_clock();478classUnloadStats->_endTime = classUnloadStats->_endPostTime;479480env->popVMstate(vmState);481482Trc_MM_ParallelGlobalGC_unloadDeadClassLoaders_exit(env->getLanguageVMThread());483}484#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */485486487488