Path: blob/master/runtime/gc_base/ClassLoaderManager.cpp
5986 views
1/*******************************************************************************2* Copyright (c) 1991, 2021 IBM Corp. and others3*4* This program and the accompanying materials are made available under5* the terms of the Eclipse Public License 2.0 which accompanies this6* distribution and is available at https://www.eclipse.org/legal/epl-2.0/7* or the Apache License, Version 2.0 which accompanies this distribution and8* is available at https://www.apache.org/licenses/LICENSE-2.0.9*10* This Source Code may also be made available under the following11* Secondary Licenses when the conditions for such availability set12* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU13* General Public License, version 2 with the GNU Classpath14* Exception [1] and GNU General Public License, version 2 with the15* OpenJDK Assembly Exception [2].16*17* [1] https://www.gnu.org/software/classpath/license.html18* [2] http://openjdk.java.net/legal/assembly-exception.html19*20* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception21*******************************************************************************/2223#include "j9.h"24#include "j9cfg.h"25#include "j9protos.h"26#include "j9consts.h"27#include "segment.h"28#include "ModronAssertions.h"29#include "vmhook_internal.h" /* this file triggers a VM hook, so we need the internal version */3031#include "ClassLoaderManager.hpp"3233#include "ClassHeapIterator.hpp"34#include "ClassLoaderIterator.hpp"35#include "ClassLoaderSegmentIterator.hpp"36#include "ClassUnloadStats.hpp"37#include "EnvironmentBase.hpp"38#include "FinalizableClassLoaderBuffer.hpp"39#include "GCExtensions.hpp"40#include "GlobalCollector.hpp"41#include "HeapMap.hpp"42#include "ClassLoaderRememberedSet.hpp"4344#if defined(J9VM_GC_REALTIME)45extern "C" {46void classLoaderLoadHook(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData);47}48#endif /* defined(J9VM_GC_REALTIME) */4950MM_ClassLoaderManager *51MM_ClassLoaderManager::newInstance(MM_EnvironmentBase *env, MM_GlobalCollector *globalCollector)52{53MM_ClassLoaderManager *classLoaderManager = (MM_ClassLoaderManager *)env->getForge()->allocate(sizeof(MM_ClassLoaderManager), MM_AllocationCategory::FIXED, J9_GET_CALLSITE());54if (classLoaderManager) {55new(classLoaderManager) MM_ClassLoaderManager(env, globalCollector);56if (!classLoaderManager->initialize(env)) {57classLoaderManager->kill(env);58classLoaderManager = NULL;59}60}61return classLoaderManager;62}6364void65MM_ClassLoaderManager::kill(MM_EnvironmentBase *env)66{67tearDown(env);68env->getForge()->free(this);69}7071void72MM_ClassLoaderManager::tearDown(MM_EnvironmentBase *env)73{74#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)75if (_undeadSegmentListMonitor) {76omrthread_monitor_destroy(_undeadSegmentListMonitor);77_undeadSegmentListMonitor = NULL;78}79#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */8081if (_classLoaderListMonitor) {82omrthread_monitor_destroy(_classLoaderListMonitor);83_classLoaderListMonitor = NULL;84}8586#if defined(J9VM_GC_REALTIME)87if (MM_GCExtensions::getExtensions(env)->isMetronomeGC()) {88J9HookInterface **vmHookInterface = _javaVM->internalVMFunctions->getVMHookInterface(_javaVM);89if (NULL != vmHookInterface) {90(*vmHookInterface)->J9HookUnregister(vmHookInterface, J9HOOK_VM_CLASS_LOADER_INITIALIZED, classLoaderLoadHook, this);91}92}93#endif /* defined(J9VM_GC_REALTIME) */94}9596/**97* Initialize the class unload manager structure. This just means that the monitor and the pointers are initialized.98*99* @return true if the initialization was successful, false otherwise.100*/101bool102MM_ClassLoaderManager::initialize(MM_EnvironmentBase *env)103{104#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)105_firstUndeadSegment = NULL;106_undeadSegmentsTotalSize = 0;107108if (0 != omrthread_monitor_init_with_name(&_undeadSegmentListMonitor, 0, "Undead Segment List Monitor")) {109return false;110}111#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */112113if (0 != omrthread_monitor_init_with_name(&_classLoaderListMonitor, 0, "Class Loader List Monitor")) {114return false;115}116117J9HookInterface **vmHookInterface = _javaVM->internalVMFunctions->getVMHookInterface(_javaVM);118if (NULL == vmHookInterface) {119return false;120}121122#if defined(J9VM_GC_REALTIME)123/* TODO CRGTMP Remove if once non-realtime collectors use classLoaderManager during124* unloadDeadClassLoaders. This was added to fix CMVC 127599 until stability week is over125*/126if (MM_GCExtensions::getExtensions(env)->isMetronomeGC()) {127if ((*vmHookInterface)->J9HookRegisterWithCallSite(vmHookInterface, J9HOOK_VM_CLASS_LOADER_INITIALIZED, classLoaderLoadHook, OMR_GET_CALLSITE(), this)) {128return false;129}130}131#endif /* defined(J9VM_GC_REALTIME) */132133return true;134}135136#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)137void138MM_ClassLoaderManager::enqueueUndeadClassSegments(J9MemorySegment *listRoot)139{140if (NULL != listRoot) {141omrthread_monitor_enter(_undeadSegmentListMonitor);142143while (NULL != listRoot) {144_undeadSegmentsTotalSize += listRoot->size;145J9MemorySegment *nextSegment = listRoot->nextSegmentInClassLoader;146listRoot->nextSegmentInClassLoader = _firstUndeadSegment;147_firstUndeadSegment = listRoot;148listRoot = nextSegment;149}150omrthread_monitor_exit(_undeadSegmentListMonitor);151}152}153154void155MM_ClassLoaderManager::flushUndeadSegments(MM_EnvironmentBase *env)156{157omrthread_monitor_enter(_undeadSegmentListMonitor);158/* now free all the segments */159J9MemorySegment *walker = _firstUndeadSegment;160_firstUndeadSegment = NULL;161_undeadSegmentsTotalSize = 0;162omrthread_monitor_exit(_undeadSegmentListMonitor);163164while (NULL != walker) {165J9MemorySegment *thisWalk = walker;166walker = thisWalk->nextSegmentInClassLoader;167_javaVM->internalVMFunctions->freeMemorySegment(_javaVM, thisWalk, TRUE);168_globalCollector->condYield(env, 0);169}170}171172void173MM_ClassLoaderManager::setLastUnloadNumOfClassLoaders()174{175_lastUnloadNumOfClassLoaders = (UDATA)pool_numElements(_javaVM->classLoaderBlocks);176}177178void179MM_ClassLoaderManager::setLastUnloadNumOfAnonymousClasses()180{181_lastUnloadNumOfAnonymousClasses = _javaVM->anonClassCount;182}183#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */184185void186MM_ClassLoaderManager::unlinkClassLoader(J9ClassLoader *classLoader)187{188omrthread_monitor_enter(_classLoaderListMonitor);189J9_LINEAR_LINKED_LIST_REMOVE(gcLinkNext, gcLinkPrevious, _classLoaders, classLoader);190omrthread_monitor_exit(_classLoaderListMonitor);191192}193194void195MM_ClassLoaderManager::linkClassLoader(J9ClassLoader *classLoader)196{197omrthread_monitor_enter(_classLoaderListMonitor);198J9_LINEAR_LINKED_LIST_ADD(gcLinkNext, gcLinkPrevious, _classLoaders, classLoader);199omrthread_monitor_exit(_classLoaderListMonitor);200}201202bool203MM_ClassLoaderManager::isTimeForClassUnloading(MM_EnvironmentBase *env)204{205bool result = false;206207#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)208209UDATA numClassLoaderBlocks = pool_numElements(_javaVM->classLoaderBlocks);210UDATA numAnonymousClasses = _javaVM->anonClassCount;211212Trc_MM_GlobalCollector_isTimeForClassUnloading_Entry(213_extensions->dynamicClassUnloading,214numClassLoaderBlocks,215_extensions->dynamicClassUnloadingThreshold,216_lastUnloadNumOfClassLoaders217);218219Trc_MM_GlobalCollector_isTimeForClassUnloading_anonClasses(220numAnonymousClasses,221_lastUnloadNumOfAnonymousClasses,222_extensions->classUnloadingAnonymousClassWeight223);224225Assert_MM_true(numAnonymousClasses >= _lastUnloadNumOfAnonymousClasses);226227if ( _extensions->dynamicClassUnloading != MM_GCExtensions::DYNAMIC_CLASS_UNLOADING_NEVER ) {228UDATA recentlyLoaded = (UDATA) ((numAnonymousClasses - _lastUnloadNumOfAnonymousClasses) * _extensions->classUnloadingAnonymousClassWeight);229/* todo aryoung: _lastUnloadNumOfClassLoaders includes the class loaders which230* were unloaded but still required finalization when the last classUnloading occured.231* This means that the threshold check is wrong when there are classes which require finalization.232* Temporarily make sure that we do not create a negative recently loaded.233*/234if (numClassLoaderBlocks >= _lastUnloadNumOfClassLoaders) {235recentlyLoaded += (numClassLoaderBlocks - _lastUnloadNumOfClassLoaders);236}237result = recentlyLoaded >= _extensions->dynamicClassUnloadingThreshold;238}239240Trc_MM_GlobalCollector_isTimeForClassUnloading_Exit(result ? "true" : "false");241242#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */243return result;244}245246#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)247J9ClassLoader *248MM_ClassLoaderManager::identifyClassLoadersToUnload(MM_EnvironmentBase *env, MM_HeapMap *markMap, MM_ClassUnloadStats* classUnloadStats)249{250Trc_MM_identifyClassLoadersToUnload_Entry(env->getLanguageVMThread());251252Assert_MM_true(NULL != markMap);253J9ClassLoader *unloadLink = NULL;254classUnloadStats->_classLoaderCandidates = 0;255256GC_ClassLoaderIterator classLoaderIterator(_javaVM->classLoaderBlocks);257J9ClassLoader * classLoader = NULL;258while( NULL != (classLoader = classLoaderIterator.nextSlot()) ) {259classUnloadStats->_classLoaderCandidates += 1;260/* Check if the class loader is already DEAD - ignore if it is */261if( J9_GC_CLASS_LOADER_DEAD == (classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD) ) {262/* If the class loader is already dead, it should be enqueued or unloading by now */263Assert_MM_true( 0 != (classLoader->gcFlags & (J9_GC_CLASS_LOADER_UNLOADING | J9_GC_CLASS_LOADER_ENQ_UNLOAD)) );264Assert_MM_true( 0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_SCANNED) );265} else {266/* If the class loader isn't already dead, it must not be enqueued or unloading */267Assert_MM_true( 0 == (classLoader->gcFlags & (J9_GC_CLASS_LOADER_UNLOADING | J9_GC_CLASS_LOADER_ENQ_UNLOAD)) );268Assert_MM_true(NULL == classLoader->unloadLink);269270/* Is the class loader still alive? (object may be NULL while the loader is being initialized) */271J9Object *classLoaderObject = classLoader->classLoaderObject;272if( (NULL != classLoaderObject) && (!markMap->isBitSet(classLoaderObject)) ) {273/* Anonymous classloader should not be unloaded */274Assert_MM_true(0 == (classLoader->flags & J9CLASSLOADER_ANON_CLASS_LOADER));275Assert_MM_true( 0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_SCANNED) );276277/* add this loader to the linked list of loaders being unloaded in this cycle */278classLoader->unloadLink = unloadLink;279unloadLink = classLoader;280} else {281if (MM_GCExtensions::getExtensions(env)->isVLHGC()) {282/* we don't use the SCANNED flag in VLHGC */283Assert_MM_true(0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_SCANNED));284} else {285/* TODO: Once SE stops using the SCANNED flag this path can be removed */286/* Anonymous classloader might not have SCANNED flag set */287if (0 == (classLoader->flags & J9CLASSLOADER_ANON_CLASS_LOADER)) {288Assert_MM_true(J9_GC_CLASS_LOADER_SCANNED == (classLoader->gcFlags & J9_GC_CLASS_LOADER_SCANNED));289}290classLoader->gcFlags &= ~J9_GC_CLASS_LOADER_SCANNED;291}292}293}294}295296Trc_MM_identifyClassLoadersToUnload_Exit(env->getLanguageVMThread());297298return unloadLink;299}300301void302MM_ClassLoaderManager::cleanUpClassLoadersStart(MM_EnvironmentBase *env, J9ClassLoader* classLoaderUnloadList, MM_HeapMap *markMap, MM_ClassUnloadStats *classUnloadStats)303{304UDATA classUnloadCount = 0;305UDATA anonymousClassUnloadCount = 0;306UDATA classLoaderUnloadCount = 0;307J9VMThread *vmThread = (J9VMThread *)env->getLanguageVMThread();308309J9Class *classUnloadList = NULL;310J9Class *anonymousClassUnloadList = NULL;311312Trc_MM_cleanUpClassLoadersStart_Entry(env->getLanguageVMThread());313314/*315* Verify that boolean array class has been marked. Assertion is done to ensure correctness316* of an optimization in ClassIteratorClassSlots that only checks booleanArrayClass Interfaces317* since all array claseses share the same ITable.318*/319Assert_MM_true(markMap->isBitSet(_javaVM->booleanArrayClass->classObject));320321/*322* Walk anonymous classes and set unmarked as dying323*324* Do this walk before classloaders to be unloaded walk to create list of anonymous classes to be unloaded and use it325* as sublist to continue to build general list of classes to be unloaded326*327* Anonymous classes suppose to be allocated one per segment328* This is not relevant here however becomes important at segment removal time329*/330anonymousClassUnloadList = addDyingClassesToList(env, _javaVM->anonClassLoader, markMap, false, anonymousClassUnloadList, &anonymousClassUnloadCount);331332/* class unload list includes anonymous class unload list */333classUnloadList = anonymousClassUnloadList;334classUnloadCount += anonymousClassUnloadCount;335336/* Count all classes loaded by dying class loaders */337J9ClassLoader * classLoader = classLoaderUnloadList;338while (NULL != classLoader) {339Assert_MM_true( 0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_SCANNED) );340classLoaderUnloadCount += 1;341classLoader->gcFlags |= J9_GC_CLASS_LOADER_DEAD;342343/* mark all of its classes as dying */344classUnloadList = addDyingClassesToList(env, classLoader, markMap, true, classUnloadList, &classUnloadCount);345346classLoader = classLoader->unloadLink;347}348349if (0 != classUnloadCount) {350/* Call classes unload hook */351Trc_MM_cleanUpClassLoadersStart_triggerClassesUnload(env->getLanguageVMThread(), classUnloadCount);352TRIGGER_J9HOOK_VM_CLASSES_UNLOAD(_javaVM->hookInterface, vmThread, classUnloadCount, classUnloadList);353}354355if (0 != anonymousClassUnloadCount) {356/* Call anonymous classes unload hook */357Trc_MM_cleanUpClassLoadersStart_triggerAnonymousClassesUnload(env->getLanguageVMThread(), anonymousClassUnloadCount);358TRIGGER_J9HOOK_VM_ANON_CLASSES_UNLOAD(_javaVM->hookInterface, vmThread, anonymousClassUnloadCount, anonymousClassUnloadList);359}360361if (0 != classLoaderUnloadCount) {362/* Call classloader unload hook */363Trc_MM_cleanUpClassLoadersStart_triggerClassLoadersUnload(env->getLanguageVMThread(), classLoaderUnloadCount);364TRIGGER_J9HOOK_VM_CLASS_LOADERS_UNLOAD(_javaVM->hookInterface, vmThread, classLoaderUnloadList);365}366367classUnloadStats->_classesUnloadedCount = classUnloadCount;368classUnloadStats->_classLoaderUnloadedCount = classLoaderUnloadCount;369classUnloadStats->_anonymousClassesUnloadedCount = anonymousClassUnloadCount;370371/* Ensure that the vm has an accurate number of currently loaded anonymous classes */372_javaVM->anonClassCount -= anonymousClassUnloadCount;373374Trc_MM_cleanUpClassLoadersStart_Exit(env->getLanguageVMThread());375}376377J9Class *378MM_ClassLoaderManager::addDyingClassesToList(MM_EnvironmentBase *env, J9ClassLoader * classLoader, MM_HeapMap *markMap, bool setAll, J9Class *classUnloadListStart, UDATA *classUnloadCountResult)379{380J9VMThread *vmThread = (J9VMThread *)env->getLanguageVMThread();381J9Class *classUnloadList = classUnloadListStart;382UDATA classUnloadCount = 0;383384if (NULL != classLoader) {385GC_ClassLoaderSegmentIterator segmentIterator(classLoader, MEMORY_TYPE_RAM_CLASS);386J9MemorySegment *segment = NULL;387while(NULL != (segment = segmentIterator.nextSegment())) {388GC_ClassHeapIterator classHeapIterator(_javaVM, segment);389J9Class *clazz = NULL;390while(NULL != (clazz = classHeapIterator.nextClass())) {391J9Object *classObject = clazz->classObject;392if (setAll || !markMap->isBitSet(classObject)) {393394/* with setAll all classes must be unmarked */395Assert_MM_true(!markMap->isBitSet(classObject));396397classUnloadCount += 1;398399/* Remove the class from the subclass traversal list */400removeFromSubclassHierarchy(env, clazz);401402/* Mark class as dying */403clazz->classDepthAndFlags |= J9AccClassDying;404405/* For CMVC 137275. For all dying classes we poison the classObject406* field to J9_INVALID_OBJECT to investigate the origin of a class object407* reference whose class has been unloaded.408*/409clazz->classObject = (j9object_t) J9_INVALID_OBJECT;410411/* Call class unload hook */412Trc_MM_cleanUpClassLoadersStart_triggerClassUnload(env->getLanguageVMThread(),clazz,413(UDATA) J9UTF8_LENGTH(J9ROMCLASS_CLASSNAME(clazz->romClass)),414J9UTF8_DATA(J9ROMCLASS_CLASSNAME(clazz->romClass)));415TRIGGER_J9HOOK_VM_CLASS_UNLOAD(_javaVM->hookInterface, vmThread, clazz);416417/* add class to dying classes link list */418clazz->gcLink = classUnloadList;419classUnloadList = clazz;420}421}422}423}424425*classUnloadCountResult += classUnloadCount;426return classUnloadList;427}428429void430MM_ClassLoaderManager::cleanUpClassLoadersEnd(MM_EnvironmentBase *env, J9ClassLoader* unloadLink)431{432J9VMThread *vmThread = (J9VMThread *)env->getLanguageVMThread();433J9MemorySegment *reclaimedSegments = NULL;434435Trc_MM_cleanUpClassLoadersEnd_Entry(vmThread);436437/* Unload classes in the unload link and pass back any RAM Classes that we encounter so the caller can decide when to free them */438Trc_MM_cleanUpClassLoadersEnd_deleteDeadClassLoaderClassSegmentsStart(env->getLanguageVMThread());439Trc_MM_cleanUpClassLoadersEnd_unloadClassLoadersNotRequiringFinalizerStart(env->getLanguageVMThread());440while (NULL != unloadLink) {441J9ClassLoader *nextUnloadLink = unloadLink->unloadLink;442J9MemorySegment *segment = unloadLink->classSegments;443/* now clean up all the segments for this class loader */444cleanUpSegmentsAlongClassLoaderLink(_javaVM, segment, &reclaimedSegments);445_javaVM->internalVMFunctions->freeClassLoader(unloadLink, _javaVM, vmThread, 1);446unloadLink = nextUnloadLink;447}448449/* we should have already cleaned up the segments attached to this class so it should return none reclaimed */450Assert_MM_true(NULL == reclaimedSegments);451452Trc_MM_cleanUpClassLoadersEnd_Exit(env->getLanguageVMThread());453}454455void456MM_ClassLoaderManager::cleanUpSegmentsAlongClassLoaderLink(J9JavaVM *javaVM, J9MemorySegment *segment, J9MemorySegment **reclaimedSegments)457{458while (NULL != segment) {459J9MemorySegment *nextSegment = segment->nextSegmentInClassLoader;460if (segment->type & MEMORY_TYPE_RAM_CLASS) {461segment->type |= MEMORY_TYPE_UNDEAD_CLASS;462/* we also need to unset the fact that this is a RAM CLASS since some code which walks the segment list is looking for still-valid ones */463segment->type &= ~MEMORY_TYPE_RAM_CLASS;464segment->nextSegmentInClassLoader = *reclaimedSegments;465*reclaimedSegments = segment;466segment->classLoader = NULL;467} else if (!(segment->type & MEMORY_TYPE_UNDEAD_CLASS)) {468javaVM->internalVMFunctions->freeMemorySegment(javaVM, segment, 1);469}470segment = nextSegment;471}472}473474void475MM_ClassLoaderManager::cleanUpSegmentsInAnonymousClassLoader(MM_EnvironmentBase *env, J9MemorySegment **reclaimedSegments)476{477if (NULL != _javaVM->anonClassLoader) {478J9MemorySegment **previousSegmentPointer = &_javaVM->anonClassLoader->classSegments;479J9MemorySegment *segment = *previousSegmentPointer;480481while (NULL != segment) {482J9MemorySegment *nextSegment = segment->nextSegmentInClassLoader;483bool removed = false;484if (MEMORY_TYPE_RAM_CLASS == (segment->type & MEMORY_TYPE_RAM_CLASS)) {485GC_ClassHeapIterator classHeapIterator(_javaVM, segment);486/* Get anonymous class from this segment */487J9Class *clazz = classHeapIterator.nextClass();488/* Anonymous classes expected to be allocated one per segment */489Assert_MM_true(NULL == classHeapIterator.nextClass());490491if (J9AccClassDying == (J9CLASS_FLAGS(clazz) & J9AccClassDying)) {492/* TODO replace this to better algorithm */493/* Try to find ROM class for unloading anonymous RAM class if it is not an array */494if (!_extensions->objectModel.isIndexable(clazz)) {495J9ROMClass *romClass = clazz->romClass;496if (NULL != romClass) {497J9MemorySegment **previousSegmentPointerROM = &_javaVM->anonClassLoader->classSegments;498J9MemorySegment *segmentROM = *previousSegmentPointerROM;499500/*501* Walk all anonymous classloader's ROM memory segments502* If ROM class is allocated there it would be one per segment503*/504while (NULL != segmentROM) {505J9MemorySegment *nextSegmentROM = segmentROM->nextSegmentInClassLoader;506if ((MEMORY_TYPE_ROM_CLASS == (segmentROM->type & MEMORY_TYPE_ROM_CLASS)) && ((J9ROMClass *)segmentROM->heapBase == romClass)) {507/* found! */508/* remove memory segment from list */509*previousSegmentPointerROM = nextSegmentROM;510/* correct pre-cached next for RAM iteration cycle if necessary */511if (nextSegment == segmentROM) {512nextSegment = nextSegmentROM;513}514/* correct pre-cached previous for RAM iteration cycle if necessary */515if (segmentROM->nextSegmentInClassLoader == segment) {516previousSegmentPointer = previousSegmentPointerROM;517}518/* ...and free memory segment */519_javaVM->internalVMFunctions->freeMemorySegment(_javaVM, segmentROM, 1);520break;521}522previousSegmentPointerROM = &segmentROM->nextSegmentInClassLoader;523segmentROM = nextSegmentROM;524}525}526}527528segment->type |= MEMORY_TYPE_UNDEAD_CLASS;529/* we also need to unset the fact that this is a RAM CLASS since some code which walks the segment list is looking for still-valid ones */530segment->type &= ~MEMORY_TYPE_RAM_CLASS;531segment->nextSegmentInClassLoader = *reclaimedSegments;532*reclaimedSegments = segment;533segment->classLoader = NULL;534/* remove RAM memory segment from classloader segments list */535*previousSegmentPointer = nextSegment;536removed = true;537}538}539if (!removed) {540previousSegmentPointer = &segment->nextSegmentInClassLoader;541}542segment = nextSegment;543}544}545}546547void548MM_ClassLoaderManager::removeFromSubclassHierarchy(MM_EnvironmentBase *env, J9Class *clazzPtr)549{550J9Class* nextLink = clazzPtr->subclassTraversalLink;551J9Class* reverseLink = clazzPtr->subclassTraversalReverseLink;552553reverseLink->subclassTraversalLink = nextLink;554nextLink->subclassTraversalReverseLink = reverseLink;555556/* link this obsolete class to itself so that it won't have dangling pointers into the subclass traversal list */557clazzPtr->subclassTraversalLink = clazzPtr;558clazzPtr->subclassTraversalReverseLink = clazzPtr;559}560561void562MM_ClassLoaderManager::cleanUpClassLoaders(MM_EnvironmentBase *env, J9ClassLoader *classLoadersUnloadedList, J9MemorySegment** reclaimedSegments, J9ClassLoader ** unloadLink, volatile bool* finalizationRequired)563{564*reclaimedSegments = NULL;565*unloadLink = NULL;566567/*568* Cleanup segments in anonymous classloader569*/570cleanUpSegmentsInAnonymousClassLoader(env, reclaimedSegments);571572/* For each classLoader that is not already unloading, not scanned and not enqueued for finalization:573* perform classLoader-specific clean up, if it died on the current collection cycle; and either enqueue it for574* finalization, if it needs any shared libraries to be unloaded, or add it to the list of classLoaders to be575* unloaded by cleanUpClassLoadersEnd.576*/577GC_FinalizableClassLoaderBuffer buffer(_extensions);578while (NULL != classLoadersUnloadedList) {579/* fetch the next loader immediately, since we will re-use the unloadLink in this loop */580J9ClassLoader* classLoader = classLoadersUnloadedList;581classLoadersUnloadedList = classLoader->unloadLink;582583Assert_MM_true(0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_SCANNED)); /* we don't use the SCANNED flag in VLHGC */584Assert_MM_true(J9_GC_CLASS_LOADER_DEAD == (classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD));585Assert_MM_true(0 == (classLoader->gcFlags & (J9_GC_CLASS_LOADER_UNLOADING | J9_GC_CLASS_LOADER_ENQ_UNLOAD)));586587Trc_MM_ClassLoaderUnload(env->getLanguageVMThread());588589/* Perform classLoader-specific clean up work, including freeing the classLoader's class hash table and590* class path entries.591*/592_javaVM->internalVMFunctions->cleanUpClassLoader((J9VMThread *)env->getLanguageVMThread(), classLoader);593594#if defined(J9VM_GC_FINALIZATION)595/* Determine if the classLoader needs to be enqueued for finalization (for shared library unloading),596* otherwise add it to the list of classLoaders to be unloaded by cleanUpClassLoadersEnd.597*/598if(((NULL != classLoader->sharedLibraries)599&& (0 != pool_numElements(classLoader->sharedLibraries)))600|| (_extensions->fvtest_forceFinalizeClassLoaders)) {601/* Enqueue the class loader for the finalizer */602buffer.add(env, classLoader);603classLoader->gcFlags |= J9_GC_CLASS_LOADER_ENQ_UNLOAD;604*finalizationRequired = true;605} else {606/* Add the classLoader to the list of classLoaders to be unloaded by cleanUpClassLoadersEnd */607classLoader->unloadLink = *unloadLink;608*unloadLink = classLoader;609}610#endif /* J9VM_GC_FINALIZATION */611612/* free any ROM classes now and enqueue any RAM classes */613cleanUpSegmentsAlongClassLoaderLink(_javaVM, classLoader->classSegments, reclaimedSegments);614615/* we are taking responsibility for cleaning these here so free them */616classLoader->classSegments = NULL;617618/* perform any configuration specific clean up */619if (_extensions->isVLHGC()) {620MM_ClassLoaderRememberedSet *classLoaderRememberedSet = _extensions->classLoaderRememberedSet;621if (MM_CycleState::CT_PARTIAL_GARBAGE_COLLECTION == env->_cycleState->_collectionType) {622/* during PGCs we should never unload a class loader which is remembered because it could have instances */623Assert_MM_false(classLoaderRememberedSet->isRemembered(env, classLoader));624}625classLoaderRememberedSet->killRememberedSet(env, classLoader);626}627}628buffer.flush(env);629}630631bool632MM_ClassLoaderManager::tryEnterClassUnloadMutex(MM_EnvironmentBase *env)633{634bool result = true;635636/* now, perform any actions that the global collector needs to take before a collection starts */637#if defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR)638if (0 != omrthread_rwmutex_try_enter_write(_javaVM->classUnloadMutex))639#else640if (0 != omrthread_monitor_try_enter(_javaVM->classUnloadMutex))641#endif /* J9VM_JIT_CLASS_UNLOAD_RWMONITOR */642{643/* The JIT currently is in the monitor */644/* We can simply skip unloading classes for this GC */645result = false;646}647return result;648}649650U_64651MM_ClassLoaderManager::enterClassUnloadMutex(MM_EnvironmentBase *env)652{653PORT_ACCESS_FROM_ENVIRONMENT(env);654U_64 quiesceTime = J9CONST64(0);655656/* now, perform any actions that the global collector needs to take before a collection starts */657#if defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR)658if (0 != omrthread_rwmutex_try_enter_write(_javaVM->classUnloadMutex))659#else660if (0 != omrthread_monitor_try_enter(_javaVM->classUnloadMutex))661#endif /* J9VM_JIT_CLASS_UNLOAD_RWMONITOR */662{663/* The JIT currently is in the monitor */664/* We must interrupt the JIT compilation so the GC can unload classes */665U_64 startTime = j9time_hires_clock();666TRIGGER_J9HOOK_MM_INTERRUPT_COMPILATION(_extensions->hookInterface, (J9VMThread *)env->getLanguageVMThread());667#if defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR)668omrthread_rwmutex_enter_write(_javaVM->classUnloadMutex);669#else670omrthread_monitor_enter(_javaVM->classUnloadMutex);671#endif /* J9VM_JIT_CLASS_UNLOAD_RWMONITOR */672U_64 endTime = j9time_hires_clock();673quiesceTime = j9time_hires_delta(startTime, endTime, J9PORT_TIME_DELTA_IN_MICROSECONDS);674}675return quiesceTime;676}677678void679MM_ClassLoaderManager::exitClassUnloadMutex(MM_EnvironmentBase *env)680{681/* If we allowed class unloading during this gc, we must release the classUnloadMutex */682#if defined(J9VM_JIT_CLASS_UNLOAD_RWMONITOR)683omrthread_rwmutex_exit_write(_javaVM->classUnloadMutex);684#else685omrthread_monitor_exit(_javaVM->classUnloadMutex);686#endif /* J9VM_JIT_CLASS_UNLOAD_RWMONITOR */687}688689#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */690691692#if defined(J9VM_GC_REALTIME)693/* TODO CRGTMP Remove #if defined once non-realtime collectors use classLoaderManager during694* unloadDeadClassLoaders. This was added to fix CMVC 127599 until stability week is over695*/696extern "C" {697698void classLoaderLoadHook(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData)699{700J9VMClassLoaderInitializedEvent* event = (J9VMClassLoaderInitializedEvent*)eventData;701MM_ClassLoaderManager *manager = (MM_ClassLoaderManager *)userData;702/* TODO CRGTMP should we be setting the SCANNED bit on the classloader if we are in trace phase? */703manager->linkClassLoader(event->classLoader);704}705706}707#endif /* J9VM_GC_REALTIME */708709710