Path: blob/master/runtime/gc_base/FinalizerSupport.cpp
5986 views
/*******************************************************************************1* Copyright (c) 1991, 2020 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 "j9accessbarrier.h"24#include "j9cfg.h"25#include "j9protos.h"26#include "j9user.h"27#include "j9consts.h"28#include "omrthread.h"29#include "jni.h"30#include "ModronAssertions.h"3132#include "FinalizerSupport.hpp"3334#include "AtomicOperations.hpp"35#include "ClassLoaderIterator.hpp"36#include "EnvironmentBase.hpp"37#include "FinalizeListManager.hpp"38#include "FinalizableObjectBuffer.hpp"39#include "GCExtensions.hpp"40#include "Heap.hpp"41#include "ModronTypes.hpp"42#include "ObjectAccessBarrier.hpp"43#include "OMRVMInterface.hpp"44#include "SublistFragment.hpp"45#include "SublistIterator.hpp"46#include "SublistSlotIterator.hpp"47#include "SublistPuddle.hpp"48#include "UnfinalizedObjectBuffer.hpp"49#include "UnfinalizedObjectList.hpp"5051extern "C" {5253#if defined(J9VM_GC_FINALIZATION)5455/**56* finds and removes a class loader with thread notification from finalize list57*/58#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)59void *60finalizeForcedClassLoaderUnload(J9VMThread *vmThread)61{62void *returnValue = NULL;63J9ClassLoader *classLoader = NULL;64J9JavaVM *javaVM = vmThread->javaVM;65GC_FinalizeListManager *finalizeListManager = MM_GCExtensions::getExtensions(javaVM)->finalizeListManager;6667#if defined(J9VM_THR_PREEMPTIVE)68finalizeListManager->lock();69omrthread_monitor_enter(javaVM->classLoaderBlocksMutex);70#endif /* J9VM_THR_PREEMPTIVE */7172returnValue = (void *)finalizeListManager->popRequiredClassLoaderForForcedClassLoaderUnload();7374if (NULL == returnValue) {75/* we did not find anything on the finalizeList now try the regular classloader list */76GC_ClassLoaderIterator classLoaderIterator(javaVM->classLoaderBlocks);7778while (NULL != (classLoader = classLoaderIterator.nextSlot())) {79if (!(classLoader->gcFlags & J9_GC_CLASS_LOADER_UNLOADING)) {80if (classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD) {81if (NULL != classLoader->gcThreadNotification) {82// Class loader with pending threads found - process this class loader83returnValue = classLoader;84break;85}86}87}88} /* classLoaderIterator */89}9091#if defined(J9VM_THR_PREEMPTIVE)92omrthread_monitor_exit(javaVM->classLoaderBlocksMutex);93finalizeListManager->unlock();94#endif /* J9VM_THR_PREEMPTIVE */9596return returnValue;97}98#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */99100101/**102* Force objects from the unfinalized list to the finalizable lists.103*104* @note System class loader types are skipped until all other types of objects are processed (jck issue).105* @note Assumes the calling thread has VM access.106* @note This routine is a temporary hack while the finalizer is rewritten.107*/108void109finalizeForcedUnfinalizedToFinalizable(J9VMThread *vmThread)110{111#if defined(J9VM_GC_FINALIZATION)112MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);113MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(env);114GC_FinalizeListManager *finalizeListManager = extensions->finalizeListManager;115116/* Drop the lock and go for exclusive */117finalizeListManager->unlock();118env->acquireExclusiveVMAccess();119finalizeListManager->lock();120121/* ensure that all thread-local buffers of unfinalized objects are flushed */122GC_OMRVMInterface::flushNonAllocationCaches(env);123124GC_FinalizableObjectBuffer buffer(extensions);125/* process the lists */126MM_UnfinalizedObjectList *unfinalizedObjectList = extensions->unfinalizedObjectLists;127while(NULL != unfinalizedObjectList) {128/* Iterate directly the current list. Not calling startUnfinalizedProcessing() to create and iterate priorList.129* If forced finalize occurs in a middle of a CS cycle, startUnfinalizedProcessing have been already done.130* Doing it again would overwrite existing prior list, plus all of objects would then be processed131* at the end of CS as being in unfinalized list, while ther are actually in finalizable list.132*/133J9Object *objectPtr = unfinalizedObjectList->getHeadOfList();134while (NULL != objectPtr) {135J9Object* next = extensions->accessBarrier->getFinalizeLink(objectPtr);136/* CMVC 181817: need to remember all objects forced onto the finalizable list */137extensions->accessBarrier->forcedToFinalizableObject(vmThread, objectPtr);138buffer.add(env, objectPtr);139objectPtr = next;140}141/* Now that we moved out the content, make the list look empty (what typically startUnfinalizedProcessing does).142*/143unfinalizedObjectList->resetHeadOfList();144145/* Flush the local buffer of finalizable objects to the global list.146* This needs to be done once per unfinalized list to ensure that all147* the objects contained in the buffer are always from the same tenant148* in multi-tenant mode149*/150buffer.flush(env);151152unfinalizedObjectList = unfinalizedObjectList->getNextList();153}154155env->releaseExclusiveVMAccess();156#endif /* J9VM_GC_FINALIZATION */157}158159#define FINALIZE_WORKER_STAY_ALIVE 0160#define FINALIZE_WORKER_SHOULD_DIE 1161#define FINALIZE_WORKER_ABANDONED 2162#define FINALIZE_WORKER_SHOULD_ABANDON 3163164#define FINALIZE_WORKER_MODE_NORMAL 0165#define FINALIZE_WORKER_MODE_FORCED 1166#define FINALIZE_WORKER_MODE_CL_UNLOAD 2167168struct finalizeWorkerData {169omrthread_monitor_t monitor;170J9JavaVM *vm;171J9VMThread *vmThread;172IDATA finished;173IDATA die;174IDATA noWorkDone;175IDATA mode;176IDATA wakeUp;177};178179static int J9THREAD_PROC FinalizeWorkerThread(void *arg);180IDATA FinalizeMainRunFinalization(J9JavaVM * vm, omrthread_t * indirectWorkerThreadHandle, struct finalizeWorkerData **indirectWorkerData, IDATA finalizeCycleLimit, IDATA mode);181static int J9THREAD_PROC FinalizeMainThread(void *javaVM);182static int J9THREAD_PROC gpProtectedFinalizeWorkerThread(void *entryArg);183184static int J9THREAD_PROC FinalizeMainThread(void *javaVM)185{186J9JavaVM *vm = (J9JavaVM *)javaVM;187omrthread_t workerThreadHandle;188int doneRunFinalizersOnExit, noCycleWait;189struct finalizeWorkerData *workerData = NULL;190IDATA finalizeCycleInterval, finalizeCycleLimit, currentWaitTime, finalizableListUsed;191IDATA cycleIntervalWaitResult;192UDATA workerMode, savedFinalizeMainFlags;193GC_FinalizeListManager *finalizeListManager;194MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(vm->omrVM);195MM_Forge *forge = extensions->getForge();196197/* explicitly set the name for main finalizer thread as it is not attached to VM */198omrthread_set_name(omrthread_self(), "Finalizer main");199200vm->finalizeMainThread = omrthread_self();201workerThreadHandle = NULL;202noCycleWait = 0;203204finalizeListManager = extensions->finalizeListManager;205206/* Initialize the defaults */207finalizeCycleInterval = extensions->finalizeCycleInterval;208finalizeCycleLimit = extensions->finalizeCycleLimit;209210#if defined(J9VM_OPT_JAVA_OFFLOAD_SUPPORT)211if(NULL != vm->javaOffloadSwitchOnNoEnvWithReasonFunc) {212(*vm->javaOffloadSwitchOnNoEnvWithReasonFunc)(vm, vm->finalizeMainThread, J9_JNI_OFFLOAD_SWITCH_GC_FINALIZE_MAIN_THREAD);213}214#endif215216currentWaitTime = 0;217omrthread_monitor_enter(vm->finalizeMainMonitor);218vm->finalizeMainFlags |= J9_FINALIZE_FLAGS_ACTIVE;219omrthread_monitor_notify_all(vm->finalizeMainMonitor);220221do {222if(currentWaitTime != -1 && !noCycleWait) {223if(!(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_MAIN_WORK_REQUEST)) {224if(currentWaitTime == -2) {225omrthread_yield();226} else {227do {228cycleIntervalWaitResult = omrthread_monitor_wait_timed(vm->finalizeMainMonitor, currentWaitTime, 0);229} while(!(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_MAIN_WORK_REQUEST) && cycleIntervalWaitResult != J9THREAD_TIMED_OUT);230}231}232}233234/* Check for a shutdown request, which overrides all other requests */235if(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_SHUTDOWN)236break;237238/* Check for a wake up request, which means the garbage collector has placed objects on the finalizable queue for processing */239if(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_MAIN_WAKE_UP) {240vm->finalizeMainFlags &= ~J9_FINALIZE_FLAGS_MAIN_WAKE_UP;241currentWaitTime = finalizeCycleInterval;242}243244/* Adjust the wait time based on how full the finalizable queue is (magic for now) */245finalizableListUsed = finalizeListManager->getJobCount();246if(0 != finalizableListUsed) {247noCycleWait = 1;248} else {249noCycleWait = 0;250}251252/* If RUN_FINALIZATION is set, make the interval time is set to -1 -> This will override any "wake up" request made by the garbage collector */253if(vm->finalizeMainFlags & (J9_FINALIZE_FLAGS_RUN_FINALIZATION254#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)255| J9_FINALIZE_FLAGS_FORCE_CLASS_LOADER_UNLOAD256#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */257))258currentWaitTime = -1;259260/* There is work to be done - run one finalization cycle */261#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)262if(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_FORCE_CLASS_LOADER_UNLOAD) {263workerMode = FINALIZE_WORKER_MODE_CL_UNLOAD;264} else {265workerMode = FINALIZE_WORKER_MODE_NORMAL;266}267#else /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */268workerMode = FINALIZE_WORKER_MODE_NORMAL;269#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */270271savedFinalizeMainFlags = vm->finalizeMainFlags;272273IDATA result = FinalizeMainRunFinalization(vm, &workerThreadHandle, &workerData, finalizeCycleLimit, workerMode);274if(result < 0) {275/* give up this run and hope next time will be better */276currentWaitTime = 0;277noCycleWait = 0;278continue;279}280281/* Determine whether the worker actually did finish it's work */282omrthread_monitor_enter(workerData->monitor);283if(workerData->finished) {284if(workerData->noWorkDone) {285workerData->noWorkDone = 0;286#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)287if(!(savedFinalizeMainFlags & J9_FINALIZE_FLAGS_FORCE_CLASS_LOADER_UNLOAD)) {288#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */289currentWaitTime = 0;290if(savedFinalizeMainFlags & J9_FINALIZE_FLAGS_RUN_FINALIZATION) {291vm->finalizeMainFlags &= ~J9_FINALIZE_FLAGS_RUN_FINALIZATION;292omrthread_monitor_enter(vm->finalizeRunFinalizationMutex);293omrthread_monitor_notify_all(vm->finalizeRunFinalizationMutex);294omrthread_monitor_exit(vm->finalizeRunFinalizationMutex);295}296#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)297}298#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */299}300} else {301/* The worker never finished during the allocated time - abandon it */302workerData->die = FINALIZE_WORKER_ABANDONED;303workerThreadHandle = NULL;304}305omrthread_monitor_exit(workerData->monitor);306} while(!(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_SHUTDOWN));307308/* Check if finalizers should be run on exit */309if(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_RUN_FINALIZERS_ON_EXIT) {310doneRunFinalizersOnExit = 0;311while(!doneRunFinalizersOnExit) {312IDATA result = 0;313do {314/* Keep trying, even if a worker requests that it be abandoned */315result = FinalizeMainRunFinalization(vm, &workerThreadHandle, &workerData, finalizeCycleLimit, FINALIZE_WORKER_MODE_FORCED);316} while(result == -2);317318if(result == -1) {319/* There was a bad error - just move to the actual quit phase */320break;321}322323omrthread_monitor_enter(workerData->monitor);324if(workerData->finished && workerData->noWorkDone) {325/* No more work to be done */326doneRunFinalizersOnExit = 1;327}328if(!workerData->finished) {329/* The worker seems to be hung - just quit */330doneRunFinalizersOnExit = 1;331workerData->die = FINALIZE_WORKER_ABANDONED;332workerThreadHandle = NULL;333}334omrthread_monitor_exit(workerData->monitor);335}336}337338/* We've been told to die */339if(NULL != workerThreadHandle) {340omrthread_monitor_exit((omrthread_monitor_t)vm->finalizeMainMonitor);341omrthread_monitor_enter(workerData->monitor);342workerData->die = FINALIZE_WORKER_SHOULD_DIE;343omrthread_monitor_notify_all(workerData->monitor);344omrthread_monitor_wait(workerData->monitor);345omrthread_monitor_exit(workerData->monitor);346omrthread_monitor_destroy(workerData->monitor);347forge->free(workerData);348omrthread_monitor_enter((omrthread_monitor_t)vm->finalizeMainMonitor);349}350351#if defined(J9VM_OPT_JAVA_OFFLOAD_SUPPORT)352if(NULL != vm->javaOffloadSwitchOffNoEnvWithReasonFunc) {353(*vm->javaOffloadSwitchOffNoEnvWithReasonFunc)(vm, vm->finalizeMainThread, J9_JNI_OFFLOAD_SWITCH_GC_FINALIZE_MAIN_THREAD);354}355#endif356357/* Notify the main thread that we have shut down */358vm->finalizeMainFlags |= J9_FINALIZE_FLAGS_SHUTDOWN_COMPLETE;359vm->finalizeMainFlags &= ~J9_FINALIZE_FLAGS_ACTIVE;360omrthread_monitor_notify_all((omrthread_monitor_t)vm->finalizeMainMonitor);361362/* If anyone is left waiting for forced finalization to complete, wake them up */363if(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_RUN_FINALIZATION) {364vm->finalizeMainFlags &= ~J9_FINALIZE_FLAGS_RUN_FINALIZATION;365omrthread_monitor_enter(vm->finalizeRunFinalizationMutex);366omrthread_monitor_notify_all(vm->finalizeRunFinalizationMutex);367omrthread_monitor_exit(vm->finalizeRunFinalizationMutex);368}369370omrthread_exit((omrthread_monitor_t)vm->finalizeMainMonitor); /* exit the monitor and terminate the thread */371372/* NO GUARANTEED EXECUTION BEYOND THIS POINT */373374return 0;375}376377static void378process_finalizable(J9VMThread *vmThread, j9object_t object, jclass j9VMInternalsClass, jmethodID runFinalizeMID)379{380J9InternalVMFunctions* fns;381J9JavaVM *vm;382383vm = vmThread->javaVM;384fns = vm->internalVMFunctions;385386jobject localRef = fns->j9jni_createLocalRef((JNIEnv *)vmThread, object);387388fns->internalReleaseVMAccess(vmThread);389390if((NULL != j9VMInternalsClass) && (NULL != runFinalizeMID)) {391#if defined(J9VM_PORT_ZOS_CEEHDLRSUPPORT)392/* Tell the interpreter to not register a user condition handler for this callin.393* Note: the interpreter clears J9_PRIVATE_FLAGS_SKIP_THREAD_SIGNAL_PROTECTION after reading it,394* so subsequent callins are not affected */395vmThread->privateFlags |= J9_PRIVATE_FLAGS_SKIP_THREAD_SIGNAL_PROTECTION;396#endif397((JNIEnv *)vmThread)->CallStaticVoidMethod(j9VMInternalsClass, runFinalizeMID, localRef);398((JNIEnv *)vmThread)->ExceptionClear();399}400401((JNIEnv *)vmThread)->DeleteLocalRef(localRef);402403fns->internalEnterVMFromJNI(vmThread);404}405406static void407process_reference(J9VMThread *vmThread, j9object_t reference, jmethodID refMID)408{409J9InternalVMFunctions* fns;410J9JavaVM *vm;411412vm = vmThread->javaVM;413fns = vm->internalVMFunctions;414415jobject localRef = fns->j9jni_createLocalRef((JNIEnv *)vmThread, reference);416417fns->internalReleaseVMAccess(vmThread);418419if (refMID) {420#if defined(J9VM_PORT_ZOS_CEEHDLRSUPPORT)421/* Tell the interpreter to not register a user condition handler for this callin.422* Note: the interpreter clears J9_PRIVATE_FLAGS_SKIP_THREAD_SIGNAL_PROTECTION after reading it,423* so subsequent callins are not affected */424vmThread->privateFlags |= J9_PRIVATE_FLAGS_SKIP_THREAD_SIGNAL_PROTECTION;425#endif426((JNIEnv *)vmThread)->CallBooleanMethod(localRef, refMID);427((JNIEnv *)vmThread)->ExceptionClear();428}429430((JNIEnv *)vmThread)->DeleteLocalRef(localRef);431432fns->internalEnterVMFromJNI(vmThread);433}434435static void436process_classloader(J9VMThread *vmThread, J9ClassLoader *classLoader)437{438J9InternalVMFunctions* fns;439J9JavaVM *vm;440441vm = vmThread->javaVM;442fns = vm->internalVMFunctions;443444fns->internalReleaseVMAccess(vmThread);445446fns->internalEnterVMFromJNI(vmThread);447Assert_MM_true(NULL == classLoader->classSegments);448fns->freeClassLoader(classLoader, vm, vmThread, JNI_FALSE);449fns->internalReleaseVMAccess(vmThread);450451fns->internalEnterVMFromJNI(vmThread);452}453454static void455process(J9VMThread *vmThread, const GC_FinalizeJob *finalizeJob, jclass j9VMInternalsClass, jmethodID runFinalizeMID, jmethodID referenceEnqueueImplMID)456{457if (FINALIZE_JOB_TYPE_OBJECT == (finalizeJob->type & FINALIZE_JOB_TYPE_OBJECT)) {458process_finalizable(vmThread, finalizeJob->object, j9VMInternalsClass, runFinalizeMID);459} else if (FINALIZE_JOB_TYPE_REFERENCE == (finalizeJob->type & FINALIZE_JOB_TYPE_REFERENCE)) {460process_reference(vmThread, finalizeJob->reference, referenceEnqueueImplMID);461} else if (FINALIZE_JOB_TYPE_CLASSLOADER == (finalizeJob->type & FINALIZE_JOB_TYPE_CLASSLOADER)) {462process_classloader(vmThread, finalizeJob->classLoader);463} else {464Assert_MM_unreachable();465}466}467468/**469* Worker thread consumes jobs from Finalize List Manager and process them470*/471static int J9THREAD_PROC FinalizeWorkerThread(void *arg)472{473struct finalizeWorkerData *workerData = (struct finalizeWorkerData *)arg;474J9VMThread *env;475const GC_FinalizeJob *finalizeJob;476GC_FinalizeJob localJob;477jclass referenceClazz, j9VMInternalsClass = NULL;478jmethodID referenceEnqueueImplMID = NULL, runFinalizeMID = NULL;479J9InternalVMFunctions* fns;480omrthread_monitor_t monitor;481GC_FinalizeListManager *finalizeListManager;482J9JavaVM *vm = (J9JavaVM *)(workerData->vm);483MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(vm);484MM_Forge *forge = extensions->getForge();485486fns = vm->internalVMFunctions;487monitor = workerData->monitor;488489finalizeListManager = extensions->finalizeListManager;490491if (JNI_OK != vm->internalVMFunctions->attachSystemDaemonThread(vm, &env, "Finalizer thread")) {492/* Failed to attach the thread - very bad, most likely out of memory */493workerData->vmThread = (J9VMThread *)NULL;494omrthread_monitor_enter(monitor);495omrthread_monitor_notify_all(monitor);496omrthread_monitor_exit(monitor);497return 0;498}499500#if defined(J9VM_OPT_JAVA_OFFLOAD_SUPPORT)501if( vm->javaOffloadSwitchOnWithReasonFunc != NULL ) {502(*vm->javaOffloadSwitchOnWithReasonFunc)((J9VMThread *)env, J9_JNI_OFFLOAD_SWITCH_FINALIZE_WORKER_THREAD);503((J9VMThread *)env)->javaOffloadState = 1;504}505#endif506507fns->internalEnterVMFromJNI(env);508env->privateFlags |= (J9_PRIVATE_FLAGS_FINALIZE_WORKER | J9_PRIVATE_FLAGS_USE_BOOTSTRAP_LOADER);509fns->internalReleaseVMAccess(env);510511/* Remember that the thread was gpProtected -- important for the JIT */512env->gpProtected = 1;513514if(vm->jclFlags & J9_JCL_FLAG_FINALIZATION) {515/* Only look up finalization methods if the class library supports them */516j9VMInternalsClass = ((JNIEnv *)env)->FindClass("java/lang/J9VMInternals");517if (j9VMInternalsClass) {518j9VMInternalsClass = (jclass)((JNIEnv *)env)->NewGlobalRef(j9VMInternalsClass);519if (j9VMInternalsClass) {520runFinalizeMID = ((JNIEnv *)env)->GetStaticMethodID(j9VMInternalsClass, "runFinalize", "(Ljava/lang/Object;)V");521}522}523if (!runFinalizeMID) {524((JNIEnv *)env)->ExceptionClear();525}526527referenceClazz = ((JNIEnv *)env)->FindClass("java/lang/ref/Reference");528if (referenceClazz) {529referenceEnqueueImplMID = ((JNIEnv *)env)->GetMethodID(referenceClazz, "enqueueImpl", "()Z");530}531if (!referenceEnqueueImplMID) {532((JNIEnv *)env)->ExceptionClear();533}534}535workerData->vmThread = env;536537/* Notify that the worker has come on line (We should check the result from above) */538omrthread_monitor_enter(monitor);539omrthread_monitor_notify_all(monitor);540541do {542if(!workerData->wakeUp) {543omrthread_monitor_wait(monitor);544}545workerData->wakeUp = 0;546547if(workerData->die != FINALIZE_WORKER_STAY_ALIVE) {548continue;549}550551omrthread_monitor_exit(monitor);552553fns->internalEnterVMFromJNI(env);554555#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)556if(workerData->mode != FINALIZE_WORKER_MODE_CL_UNLOAD)557#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */558{559if ((NULL != vm->processReferenceMonitor) && (0 != finalizeListManager->getReferenceCount())) {560omrthread_monitor_enter(vm->processReferenceMonitor);561vm->processReferenceActive = 1;562omrthread_monitor_exit(vm->processReferenceMonitor);563}564}565566do {567568#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)569if(workerData->mode == FINALIZE_WORKER_MODE_CL_UNLOAD) {570571if (NULL == (localJob.classLoader = (J9ClassLoader *)finalizeForcedClassLoaderUnload((J9VMThread *)env))) {572break;573} else {574localJob.type = FINALIZE_JOB_TYPE_CLASSLOADER;575finalizeJob = &localJob;576}577578} else {579#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */580581582finalizeListManager->lock();583584finalizeJob = finalizeListManager->consumeJob(env, &localJob);585if(finalizeJob == NULL) {586if(workerData->mode == FINALIZE_WORKER_MODE_FORCED) {587finalizeForcedUnfinalizedToFinalizable(env);588finalizeJob = finalizeListManager->consumeJob(env, &localJob);589}590}591592finalizeListManager->unlock();593594if(NULL != finalizeJob) {595workerData->noWorkDone = 0;596} else {597workerData->noWorkDone = 1;598break;599}600601#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)602}603#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */604605/* processing will release/acquire VM access */606process(env, finalizeJob, j9VMInternalsClass, runFinalizeMID, referenceEnqueueImplMID);607608if ((NULL != vm->processReferenceMonitor) && (0 != vm->processReferenceActive)) {609omrthread_monitor_enter(vm->processReferenceMonitor);610if (0 == finalizeListManager->getReferenceCount()) {611/* There is no more pending reference. */612vm->processReferenceActive = 0;613}614/*615* Notify any waiters that progress has been made.616* This improves latency for Reference.waitForReferenceProcessing() and try to617* avoid the performance issue if there are many of pending references in the queue.618*/619omrthread_monitor_notify_all(vm->processReferenceMonitor);620omrthread_monitor_exit(vm->processReferenceMonitor);621}622623fns->jniResetStackReferences((JNIEnv *)env);624625if(FINALIZE_WORKER_SHOULD_ABANDON == workerData->die) {626/* We've been abandoned, finish up */627break;628}629} while (true);630631fns->internalReleaseVMAccess(env);632633workerData->finished = 1;634635/* Notify the main that the work is complete */636omrthread_monitor_enter(monitor);637omrthread_monitor_notify_all(monitor);638} while(workerData->die == FINALIZE_WORKER_STAY_ALIVE);639640if (j9VMInternalsClass) {641((JNIEnv *)env)->DeleteGlobalRef(j9VMInternalsClass);642}643644((JavaVM *)vm)->DetachCurrentThread();645646#if defined(J9VM_OPT_JAVA_OFFLOAD_SUPPORT)647if( vm->javaOffloadSwitchOffNoEnvWithReasonFunc != NULL ) {648(*vm->javaOffloadSwitchOffNoEnvWithReasonFunc)(vm, omrthread_self(), J9_JNI_OFFLOAD_SWITCH_FINALIZE_WORKER_THREAD);649}650#endif651652switch(workerData->die) {653case FINALIZE_WORKER_SHOULD_ABANDON:654/* Poke the main in case it missed the notify */655omrthread_monitor_notify_all(workerData->monitor);656657/* Now wait for the main to give us the OK to die */658while(FINALIZE_WORKER_SHOULD_ABANDON == workerData->die) {659omrthread_monitor_wait(workerData->monitor);660}661Assert_MM_true(FINALIZE_WORKER_ABANDONED == workerData->die);662/* Fallthrough to the abandoned case */663664case FINALIZE_WORKER_ABANDONED:665/* Clean up communication data structures data structures */666omrthread_monitor_exit(workerData->monitor);667omrthread_monitor_destroy(workerData->monitor);668forge->free(workerData);669break;670case FINALIZE_WORKER_SHOULD_DIE:671omrthread_monitor_notify_all(workerData->monitor);672omrthread_exit(workerData->monitor); /* exit the monitor, and terminate the thread */673/* NO EXECUTION GUARANTEE BEYOND THIS POINT */674}675676/* NO EXECUTION GUARANTEE BEYOND THIS POINT */677678return 0;679}680681/*682* Preconditions:683* holds finalizeMainMonitor684* does not hold workerData->monitor685* Postconditions:686* holds finalizeMainMonitor687* does not hold workerData->monitor688*/689IDATA FinalizeMainRunFinalization(J9JavaVM * vm, omrthread_t * indirectWorkerThreadHandle,690struct finalizeWorkerData **indirectWorkerData, IDATA finalizeCycleLimit,691IDATA mode)692{693omrthread_t workerThreadHandle;694struct finalizeWorkerData *workerData;695IDATA workerWaitResult;696UDATA publicFlags;697MM_Forge *forge = MM_GCExtensionsBase::getExtensions(vm->omrVM)->getForge();698699workerThreadHandle = *indirectWorkerThreadHandle;700workerData = *indirectWorkerData;701702/* There is work to be done - if no worker thread exists, create one */703if (NULL == workerThreadHandle) {704/* Initialize a workerData structure */705workerData = (struct finalizeWorkerData *) forge->allocate(sizeof(struct finalizeWorkerData), MM_AllocationCategory::FINALIZE, J9_GET_CALLSITE());706if (NULL == workerData) {707/* What should be done here! */708return -1;709}710workerData->vm = vm;711workerData->die = FINALIZE_WORKER_STAY_ALIVE;712workerData->noWorkDone = 0;713workerData->mode = FINALIZE_WORKER_MODE_NORMAL;714workerData->wakeUp = 0;715716if (0 != omrthread_monitor_init(&(workerData->monitor), 0)) {717forge->free(workerData);718719/* What should be done here! */720return -1;721}722omrthread_monitor_exit(vm->finalizeMainMonitor);723omrthread_monitor_enter(workerData->monitor);724725/* Fork the worker thread */726IDATA result = vm->internalVMFunctions->createThreadWithCategory(727&workerThreadHandle,728vm->defaultOSStackSize,729MM_GCExtensions::getExtensions(vm)->finalizeWorkerPriority,7300,731&gpProtectedFinalizeWorkerThread,732workerData,733J9THREAD_CATEGORY_APPLICATION_THREAD);734735if (result != 0) {736omrthread_monitor_exit(workerData->monitor);737omrthread_monitor_destroy(workerData->monitor);738forge->free(workerData);739omrthread_monitor_enter(vm->finalizeMainMonitor);740return -1;741}742omrthread_monitor_wait(workerData->monitor);743if (!workerData->vmThread) {744/* The worker thread failed to initialize/attach - this is really bad */745omrthread_monitor_exit(workerData->monitor);746omrthread_monitor_destroy(workerData->monitor);747forge->free(workerData);748omrthread_monitor_enter(vm->finalizeMainMonitor);749return -1;750}751omrthread_monitor_exit(workerData->monitor);752omrthread_monitor_enter(vm->finalizeMainMonitor);753754*indirectWorkerData = workerData;755*indirectWorkerThreadHandle = workerThreadHandle;756757/* Connect the worker */758vm->finalizeWorkerData = workerData;759}760761/* A worker exists - set it to work */762omrthread_monitor_exit(vm->finalizeMainMonitor);763764omrthread_monitor_enter(workerData->monitor);765workerData->wakeUp = 1;766workerData->mode = mode;767workerData->finished = 0;768omrthread_monitor_notify_all(workerData->monitor); /* Wake worker up */769do {770workerWaitResult = omrthread_monitor_wait_timed(workerData->monitor, finalizeCycleLimit, 0);771772omrthread_monitor_enter(workerData->vmThread->publicFlagsMutex);773publicFlags = workerData->vmThread->publicFlags;774omrthread_monitor_exit(workerData->vmThread->publicFlagsMutex);775}776while (777(workerWaitResult == J9THREAD_TIMED_OUT && publicFlags & J9_PUBLIC_FLAGS_HALT_VM_DUTIES &&778!workerData->finished) || (workerWaitResult != J9THREAD_TIMED_OUT && !workerData->finished));779omrthread_monitor_exit(workerData->monitor);780781omrthread_monitor_enter(vm->finalizeMainMonitor);782783if(FINALIZE_WORKER_SHOULD_ABANDON == workerData->die) {784/* The worker thread has requested that we abandon it */785786/* Disconnect the worker */787vm->finalizeWorkerData = NULL;788*indirectWorkerThreadHandle = NULL;789*indirectWorkerData = NULL;790791/* Let the abandoned worker know that it can clean up */792omrthread_monitor_enter(workerData->monitor);793workerData->die = FINALIZE_WORKER_ABANDONED;794omrthread_monitor_notify_all(workerData->monitor);795omrthread_monitor_exit(workerData->monitor);796797return -2;798}799800return workerWaitResult;801}802803static UDATA804FinalizeWorkerThreadGlue(J9PortLibrary* portLib, void* userData)805{806return FinalizeWorkerThread(userData);807}808809static int J9THREAD_PROC810gpProtectedFinalizeWorkerThread(void *entryArg)811{812struct finalizeWorkerData *workerData = (struct finalizeWorkerData *) entryArg;813PORT_ACCESS_FROM_PORT(workerData->vm->portLibrary);814UDATA rc;815816j9sig_protect(FinalizeWorkerThreadGlue, workerData,817workerData->vm->internalVMFunctions->structuredSignalHandlerVM, workerData->vm,818J9PORT_SIG_FLAG_SIGALLSYNC | J9PORT_SIG_FLAG_MAY_CONTINUE_EXECUTION,819&rc);820821return 0;822}823824void825j9gc_finalizer_completeFinalizersOnExit(J9VMThread* vmThread)826{827J9JavaVM* vm = vmThread->javaVM;828829/* If finalization has already been shut down, do nothing */830if (!J9_ARE_ALL_BITS_SET(vm->finalizeMainFlags, J9_FINALIZE_FLAGS_ACTIVE)) {831return;832}833834/* Set the run finalizers on exit flag and initiate finalizer shutdown. */835omrthread_monitor_enter(vm->finalizeMainMonitor);836vm->finalizeMainFlags |= J9_FINALIZE_FLAGS_RUN_FINALIZERS_ON_EXIT;837if (!J9_ARE_ALL_BITS_SET(vm->finalizeMainFlags, J9_FINALIZE_FLAGS_SHUTDOWN)) {838vm->finalizeMainFlags |= J9_FINALIZE_FLAGS_SHUTDOWN;839omrthread_monitor_notify_all(vm->finalizeMainMonitor);840}841/* Is there an active worker thread? */842if (NULL != vm->finalizeWorkerData) {843struct finalizeWorkerData *workerData = (struct finalizeWorkerData*)vm->finalizeWorkerData;844if ((NULL != workerData) && (0 == workerData->finished)) {845/* An active worker thread exists (possibly the current thread).846* Abandon it so that a new worker can be created.847*/848omrthread_monitor_enter(workerData->monitor);849if (0 == workerData->finished) {850workerData->finished = 1;851workerData->die = FINALIZE_WORKER_SHOULD_ABANDON;852omrthread_monitor_notify_all(workerData->monitor);853}854omrthread_monitor_exit(workerData->monitor);855}856}857858/* Now block until finalizer shutdown is complete */859omrthread_monitor_notify_all(vm->finalizeMainMonitor);860while (!(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_SHUTDOWN_COMPLETE)) {861omrthread_monitor_wait(vm->finalizeMainMonitor);862}863omrthread_monitor_exit(vm->finalizeMainMonitor);864}865866int j9gc_finalizer_startup(J9JavaVM * vm)867{868IDATA result;869870omrthread_monitor_enter(vm->finalizeMainMonitor);871872result = vm->internalVMFunctions->createThreadWithCategory(873NULL,874vm->defaultOSStackSize,875MM_GCExtensions::getExtensions(vm)->finalizeMainPriority,8760,877&FinalizeMainThread,878vm,879J9THREAD_CATEGORY_SYSTEM_GC_THREAD);880881if (0 != result) {882omrthread_monitor_exit(vm->finalizeMainMonitor);883return -1;884}885886while (!(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_ACTIVE)) {887omrthread_monitor_wait(vm->finalizeMainMonitor);888}889omrthread_monitor_exit(vm->finalizeMainMonitor);890891return 0;892}893894/**895* Check if processing reference is active896*897* @param vm Pointer to the Java VM898* @return 1 if processing reference is active, otherwise return 0.899*/900UDATA j9gc_wait_for_reference_processing(J9JavaVM *vm)901{902UDATA ret = 0;903if (NULL != vm->processReferenceMonitor) {904omrthread_monitor_enter(vm->processReferenceMonitor);905if (0 != vm->processReferenceActive) {906omrthread_monitor_wait(vm->processReferenceMonitor);907ret = 1;908}909omrthread_monitor_exit(vm->processReferenceMonitor);910}911return ret;912}913914/**915* Precondition: JVMTI must be in JVMTI_PHASE_DEAD916*/917void j9gc_finalizer_shutdown(J9JavaVM * vm)918{919J9VMThread *vmThread = vm->internalVMFunctions->currentVMThread(vm);920921omrthread_monitor_enter(vm->finalizeMainMonitor);922if(!(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_SHUTDOWN)) {923if ( (vm->finalizeMainFlags & J9_FINALIZE_FLAGS_ACTIVE)924&& ( (vmThread && !(vmThread->privateFlags & J9_PRIVATE_FLAGS_FINALIZE_WORKER)) || !vmThread) ) {925bool waitForFinalizer = true;926struct finalizeWorkerData *workerData = (struct finalizeWorkerData*)vm->finalizeWorkerData;927928vm->finalizeMainFlags |= J9_FINALIZE_FLAGS_SHUTDOWN;929omrthread_monitor_notify_all(vm->finalizeMainMonitor);930if ((NULL != workerData) && (NULL != workerData->vmThread)931&& J9_ARE_ANY_BITS_SET(workerData->vmThread->publicFlags, J9_PUBLIC_FLAGS_HALT_THREAD_JAVA_SUSPEND)) {932/*933* PR 87639 - don't wait for the finalizer if it has been suspended.934* This will cause jniinv:terminateRemainingThreads() to fail.935*/936waitForFinalizer = false;937}938if (waitForFinalizer) {939while (!(vm->finalizeMainFlags & J9_FINALIZE_FLAGS_SHUTDOWN_COMPLETE)) {940omrthread_monitor_wait(vm->finalizeMainMonitor);941}942}943}944}945omrthread_monitor_exit(vm->finalizeMainMonitor);946}947948/**949* Called whenever a finalizeable object is created. Places the object on the unfinalized List.950* @param vmThread951* @param object The object to be finalized.952* @returns 0 if the object was successfully placed on the unfinalized list, UDATA_MAX otherwise.953*/954UDATA955finalizeObjectCreated(J9VMThread *vmThread, j9object_t object)956{957Trc_FinalizeSupport_finalizeObjectCreated_Entry(vmThread, object);958959MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);960env->getGCEnvironment()->_unfinalizedObjectBuffer->add(env, object);961962Trc_FinalizeSupport_finalizeObjectCreated_Exit(vmThread, 0);963return 0;964}965966/**967* Must not hold VM access when calling runFinalization.968*/969void970runFinalization(J9VMThread *vmThread)971{972Trc_FinalizeSupport_runFinalization_Entry(vmThread);973J9JavaVM *jvm = vmThread->javaVM;974975Assert_MM_mustNotHaveVMAccess(vmThread);976977/* Bump the run finalizers count and signal the main finalize thread if necessary */978omrthread_monitor_enter(jvm->finalizeMainMonitor);979if ( 0 == jvm->finalizeRunFinalizationCount ) {980omrthread_monitor_notify_all(jvm->finalizeMainMonitor);981}982jvm->finalizeMainFlags |= J9_FINALIZE_FLAGS_RUN_FINALIZATION;983jvm->finalizeRunFinalizationCount += 1;984omrthread_monitor_exit(jvm->finalizeMainMonitor);985986/* The main flags are checked without mutex protection, but no writes, so it's safe */987omrthread_monitor_enter(jvm->finalizeRunFinalizationMutex);988if ( 0 != (jvm->finalizeMainFlags & J9_FINALIZE_FLAGS_RUN_FINALIZATION) ) {989/* TODO: The 1000ms wait time arbitrary. This number should probably come from somewhere else. */990omrthread_monitor_wait_timed(jvm->finalizeRunFinalizationMutex,1000,0);991}992omrthread_monitor_exit(jvm->finalizeRunFinalizationMutex);993994/* stop the run finalizers request and signal the main monitor if necessary */995omrthread_monitor_enter(jvm->finalizeMainMonitor);996jvm->finalizeRunFinalizationCount -= 1;997if ( 0 == jvm->finalizeRunFinalizationCount ) {998jvm->finalizeMainFlags &= ~((UDATA)J9_FINALIZE_FLAGS_RUN_FINALIZATION);999omrthread_monitor_notify_all(jvm->finalizeMainMonitor);1000}1001omrthread_monitor_exit(jvm->finalizeMainMonitor);10021003Trc_FinalizeSupport_runFinalization_Exit(vmThread);1004}10051006#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)1007/**1008* Forcibly unloads the given class loader provided its gcFlags has the J9_GC_CLASS_LOADER_DEAD1009* bit set. Otherwise, calls runFinalization() and invoke the GC twice.1010* Assumptions: The current thread has the classLoaderBlocksMutex. Must not hold VM Access.1011*1012* @param vmThread1013* @param classLoader the class loader to forcibly unload1014* @return 0 on success if the class loader is unloaded, 1 if a failure occurred.1015*/1016UDATA1017forceClassLoaderUnload(J9VMThread *vmThread, J9ClassLoader *classLoader)1018{1019Trc_FinalizeSupport_forceClassLoaderUnload_Entry(vmThread,classLoader);1020UDATA result = 0;1021bool setForceFlag = false;1022J9JavaVM *jvm = vmThread->javaVM;10231024Assert_MM_mustNotHaveVMAccess(vmThread);10251026if ( 0 != (classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD) ) {1027if ( 0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_UNLOADING) ) {1028/* Class loader is not being unloaded but is eligible. Post the request and wait. */1029Trc_FinalizeSupport_forceClassLoaderUnload_classLoaderNotBeingUnloaded(vmThread,classLoader);1030setForceFlag = true;1031}1032/* The class loader is in the process of being unloaded.1033* Enqueue the thread for a response and wait1034*/1035if ( NULL == vmThread->gcClassUnloadingMutex ) {1036/* There's no signaling mutex for this thread yet, so allocate one */1037if ( 0 != omrthread_monitor_init(&vmThread->gcClassUnloadingMutex,0) ) {1038/* Failed to initialize the gcClassUnloadingMutex */1039Trc_FinalizeSupport_forceClassLoaderUnload_failedToInitializeClassUnloadingMutex(vmThread);1040result = 1;1041}1042}10431044if ( NULL != vmThread->gcClassUnloadingMutex ) {1045/* Link the thread into the notification list of the class loader */1046J9VMThread *tempNextThread = classLoader->gcThreadNotification;1047classLoader->gcThreadNotification = vmThread;1048vmThread->gcClassUnloadingThreadNext = tempNextThread;1049if ( NULL != tempNextThread ) {1050tempNextThread->gcClassUnloadingThreadPrevious = vmThread;1051}10521053/* If we need to signal the finalzer to force unloading a class loader, do so */1054if ( setForceFlag ) {1055omrthread_monitor_enter(jvm->finalizeMainMonitor);1056jvm->finalizeMainFlags |= J9_FINALIZE_FLAGS_FORCE_CLASS_LOADER_UNLOAD;1057jvm->finalizeForceClassLoaderUnloadCount += 1;1058omrthread_monitor_notify_all(jvm->finalizeMainMonitor);1059omrthread_monitor_exit(jvm->finalizeMainMonitor);1060}10611062/* Wait for notification that the class loader has finished unloading */1063omrthread_monitor_exit(jvm->classLoaderBlocksMutex);1064omrthread_monitor_enter(vmThread->gcClassUnloadingMutex);10651066IDATA waitResult = omrthread_monitor_wait_timed(vmThread->gcClassUnloadingMutex,5000,0);1067omrthread_monitor_exit(vmThread->gcClassUnloadingMutex);1068omrthread_monitor_enter(jvm->classLoaderBlocksMutex);10691070/* If force finalize flag was set, remove 1 from the count and clear the flag if necessary */1071if ( setForceFlag ) {1072omrthread_monitor_enter(jvm->finalizeMainMonitor);1073jvm->finalizeForceClassLoaderUnloadCount -= 1;1074if ( 0 == jvm->finalizeForceClassLoaderUnloadCount ) {1075jvm->finalizeMainFlags |= J9_FINALIZE_FLAGS_FORCE_CLASS_LOADER_UNLOAD;1076}1077omrthread_monitor_notify_all(jvm->finalizeMainMonitor);1078omrthread_monitor_exit(jvm->finalizeMainMonitor);1079}10801081if ( J9THREAD_TIMED_OUT == waitResult ) {1082/* Lock down the signalling mechanism. If we appear to be still linked up, it is safe to unlink1083* (the class loader mutex is still in our possession)1084*/1085Trc_FinalizeSupport_forceClassLoaderUnload_timedOut(vmThread,classLoader);1086omrthread_monitor_enter(vmThread->gcClassUnloadingMutex);1087if ( (NULL != vmThread->gcClassUnloadingThreadPrevious) || (NULL != vmThread->gcClassUnloadingThreadNext) ) {1088if (NULL == vmThread->gcClassUnloadingThreadPrevious) {1089classLoader->gcThreadNotification = vmThread->gcClassUnloadingThreadNext;1090} else {1091vmThread->gcClassUnloadingThreadPrevious->gcClassUnloadingThreadNext = vmThread->gcClassUnloadingThreadNext;1092}1093if (NULL != vmThread->gcClassUnloadingThreadNext) {1094vmThread->gcClassUnloadingThreadNext->gcClassUnloadingThreadPrevious = vmThread->gcClassUnloadingThreadPrevious;1095}1096vmThread->gcClassUnloadingThreadNext = NULL;1097vmThread->gcClassUnloadingThreadPrevious = NULL;1098}1099omrthread_monitor_exit(vmThread->gcClassUnloadingMutex);1100result = 1;1101}1102}1103} else {1104/** If the classLoader is not dead, run the GC aggressively to see if that causes the classLoader1105* to be marked as dead.1106*/1107Trc_FinalizeSupport_forceClassLoaderUnload_classLoaderNotDead(vmThread,classLoader);1108omrthread_monitor_exit(jvm->classLoaderBlocksMutex);1109runFinalization(vmThread);1110jvm->internalVMFunctions->internalEnterVMFromJNI(vmThread);1111j9gc_modron_global_collect(vmThread);1112jvm->internalVMFunctions->internalReleaseVMAccess(vmThread);1113runFinalization(vmThread);1114jvm->internalVMFunctions->internalEnterVMFromJNI(vmThread);1115j9gc_modron_global_collect(vmThread);1116jvm->internalVMFunctions->internalReleaseVMAccess(vmThread);1117omrthread_monitor_enter(jvm->classLoaderBlocksMutex);1118}11191120Trc_FinalizeSupport_forceClassLoaderUnload_Exit(vmThread,result);1121return result;1122}1123#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */11241125/**1126* Set a global flag which determines if finalizers are run on exit.1127* This is used to implement java.lang.Runtime.runFinalizersOnExit().1128*1129* @param vmThread[in] the current thread1130* @param run TRUE if finalizers should be run, false otherwise1131*/1132void1133j9gc_runFinalizersOnExit(J9VMThread* vmThread, UDATA run)1134{1135J9JavaVM* jvm = vmThread->javaVM;11361137omrthread_monitor_enter(jvm->finalizeMainMonitor);1138if (FALSE == run) {1139jvm->finalizeMainFlags &= ~(UDATA)J9_FINALIZE_FLAGS_RUN_FINALIZERS_ON_EXIT;1140} else {1141jvm->finalizeMainFlags |= (UDATA)J9_FINALIZE_FLAGS_RUN_FINALIZERS_ON_EXIT;1142}1143omrthread_monitor_exit(jvm->finalizeMainMonitor);1144}11451146#endif /* J9VM_GC_FINALIZATION */11471148} /* extern "C" */1149115011511152