Path: blob/master/runtime/gc_modron_standard/StandardAccessBarrier.cpp
5986 views
/*******************************************************************************1* Copyright (c) 1991, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/212223/**24* @file25* @ingroup GC_Modron_Base26*/2728#include "j9.h"29#include "j9cfg.h"30#include "j9consts.h"31#include "j9protos.h"32#include "ModronAssertions.h"3334#include "StandardAccessBarrier.hpp"35#include "AtomicOperations.hpp"36#if defined(OMR_GC_MODRON_CONCURRENT_MARK)37#include "ConcurrentGC.hpp"38#endif /* OMR_GC_MODRON_CONCURRENT_MARK */39#include "Debug.hpp"40#include "EnvironmentStandard.hpp"41#include "mmhook_internal.h"42#include "GCExtensions.hpp"43#include "HeapRegionManager.hpp"44#include "JNICriticalRegion.hpp"45#include "ObjectModel.hpp"46#include "Scavenger.hpp"47#include "SublistFragment.hpp"4849MM_StandardAccessBarrier *50MM_StandardAccessBarrier::newInstance(MM_EnvironmentBase *env, MM_MarkingScheme *markingScheme)51{52MM_StandardAccessBarrier *barrier;5354barrier = (MM_StandardAccessBarrier *)env->getForge()->allocate(sizeof(MM_StandardAccessBarrier), MM_AllocationCategory::FIXED, J9_GET_CALLSITE());55if (barrier) {56new(barrier) MM_StandardAccessBarrier(env, markingScheme);57if (!barrier->initialize(env)) {58barrier->kill(env);59barrier = NULL;60}61}62return barrier;63}6465void66MM_StandardAccessBarrier::initializeForNewThread(MM_EnvironmentBase* env)67{68#if defined(OMR_GC_REALTIME)69if (_extensions->usingSATBBarrier()) {70_extensions->sATBBarrierRememberedSet->initializeFragment(env, &(((J9VMThread *)env->getLanguageVMThread())->sATBBarrierRememberedSetFragment));71}72#endif /* OMR_GC_REALTIME */73}7475bool76MM_StandardAccessBarrier::initialize(MM_EnvironmentBase *env)77{78#if defined(J9VM_GC_GENERATIONAL)79if (!_generationalAccessBarrierComponent.initialize(env)) {80return false;81}82#endif /* J9VM_GC_GENERATIONAL */8384return MM_ObjectAccessBarrier::initialize(env);85}8687void88MM_StandardAccessBarrier::kill(MM_EnvironmentBase *env)89{90tearDown(env);91env->getForge()->free(this);92}9394void95MM_StandardAccessBarrier::tearDown(MM_EnvironmentBase *env)96{97#if defined(J9VM_GC_GENERATIONAL)98_generationalAccessBarrierComponent.tearDown(env);99#endif /* J9VM_GC_GENERATIONAL */100101MM_ObjectAccessBarrier::tearDown(env);102}103104/**105* Unmarked, heap reference, about to be deleted (or overwritten), while marking106* is in progress is to be remembered for later marking and scanning.107*/108void109MM_StandardAccessBarrier::rememberObjectToRescan(MM_EnvironmentBase *env, J9Object *object)110{111if (_markingScheme->markObject(env, object, true)) {112rememberObjectImpl(env, object);113}114}115116/**117* Unmarked, heap reference, about to be deleted (or overwritten), while marking118* is in progress is to be remembered for later marking and scanning.119* This method is called by MM_StandardAccessBarrier::rememberObject()120*/121void122MM_StandardAccessBarrier::rememberObjectImpl(MM_EnvironmentBase *env, J9Object* object)123{124#if defined(OMR_GC_REALTIME)125J9VMThread *vmThread = (J9VMThread *)env->getLanguageVMThread();126_extensions->sATBBarrierRememberedSet->storeInFragment(env, &vmThread->sATBBarrierRememberedSetFragment, (UDATA *)object);127#endif /* OMR_GC_REALTIME */128}129130131bool132MM_StandardAccessBarrier::preObjectStoreImpl(J9VMThread *vmThread, J9Object *destObject, fj9object_t *destAddress, J9Object *value, bool isVolatile)133{134MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);135136if (_extensions->isSATBBarrierActive()) {137if (NULL != destObject) {138J9Object *oldObject = NULL;139protectIfVolatileBefore(vmThread, isVolatile, true, false);140GC_SlotObject slotObject(vmThread->javaVM->omrVM, destAddress);141oldObject = slotObject.readReferenceFromSlot();142protectIfVolatileAfter(vmThread, isVolatile, true, false);143rememberObjectToRescan(env, oldObject);144}145}146147return true;148}149150bool151MM_StandardAccessBarrier::preObjectStoreImpl(J9VMThread *vmThread, J9Object **destAddress, J9Object *value, bool isVolatile)152{153MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);154155if (_extensions->isSATBBarrierActive()) {156J9Object* oldObject = NULL;157protectIfVolatileBefore(vmThread, isVolatile, true, false);158oldObject = *destAddress;159protectIfVolatileAfter(vmThread, isVolatile, true, false);160rememberObjectToRescan(env, oldObject);161}162163return true;164}165/**166* @copydoc MM_ObjectAccessBarrier::preObjectStore()167*168* Metronome uses a snapshot-at-the-beginning algorithm, but with a fuzzy snapshot in the169* sense that threads are allowed to run during the root scan. This requires a "double170* barrier." The barrier is active from the start of root scanning through the end of171* tracing. For an unscanned thread performing a store, the new value is remembered by172* the collector. For any thread performing a store (whether scanned or not), the old173* value is remembered by the collector before being overwritten (thus this barrier must be174* positioned as a pre-store barrier). For the latter ("Yuasa barrier") aspect of the175* double barrier, only the first overwritten value needs to be remembered (remembering176* others is harmless but not needed), and so we omit synchronization on the reading of the177* old value.178**/179bool180MM_StandardAccessBarrier::preObjectStore(J9VMThread *vmThread, J9Object *destObject, fj9object_t *destAddress, J9Object *value, bool isVolatile)181{182return preObjectStoreImpl(vmThread, destObject, destAddress, value, isVolatile);183}184185/**186* @copydoc MM_MetronomeAccessBarrier::preObjectStore()187*188* Used for stores into classes189*/190bool191MM_StandardAccessBarrier::preObjectStore(J9VMThread *vmThread, J9Object *destClass, J9Object **destAddress, J9Object *value, bool isVolatile)192{193return preObjectStoreImpl(vmThread, destAddress, value, isVolatile);194}195196/**197* @copydoc MM_MetronomeAccessBarrier::preObjectStore()198*199* Used for stores into internal structures200*/201bool202MM_StandardAccessBarrier::preObjectStore(J9VMThread *vmThread, J9Object **destAddress, J9Object *value, bool isVolatile)203{204return preObjectStoreImpl(vmThread, destAddress, value, isVolatile);205}206/**207* Called after an object is stored into another object.208*/209void210MM_StandardAccessBarrier::postObjectStore(J9VMThread *vmThread, J9Object *destObject, fj9object_t *destAddress, J9Object *value, bool isVolatile)211{212postObjectStoreImpl(vmThread, destObject, value);213214}215216/**217* Called after an object is stored into a class.218*/219void220MM_StandardAccessBarrier::postObjectStore(J9VMThread *vmThread, J9Class *destClass, J9Object **destAddress, J9Object *value, bool isVolatile)221{222j9object_t destObject = J9VM_J9CLASS_TO_HEAPCLASS(destClass);223224/* destObject is guaranteed to be in old space, so the common code path will remember objects appropriately here */225postObjectStoreImpl(vmThread, destObject, value);226}227228bool229MM_StandardAccessBarrier::postBatchObjectStore(J9VMThread *vmThread, J9Object *destObject, bool isVolatile)230{231postBatchObjectStoreImpl(vmThread, destObject);232233return true;234}235236bool237MM_StandardAccessBarrier::postBatchObjectStore(J9VMThread *vmThread, J9Class *destClass, bool isVolatile)238{239j9object_t destObject = J9VM_J9CLASS_TO_HEAPCLASS(destClass);240241postBatchObjectStoreImpl(vmThread, destObject);242243return true;244}245246/**247* Generational write barrier call when a single object is stored into another.248* The remembered set system consists of a physical list of objects in the OLD area that249* may contain references to the new area. The mutator is responsible for adding these old250* area objects to the remembered set; the collectors are responsible for removing these objects251* from the list when they no longer contain references. Objects that are to be remembered have their252* REMEMBERED bit set in the flags field. For performance reasons, sublists are used to maintain the253* remembered set.254*255* @param vmThread The current thread that has performed the store.256* @param dstObject The object which is being stored into.257* @param srcObject The object being stored.258*259* @note The write barrier can be called with minimal, all, or no validation checking.260* @note Any object that contains a new reference MUST have its REMEMBERED bit set.261*/262void263MM_StandardAccessBarrier::postObjectStoreImpl(J9VMThread *vmThread, J9Object *dstObject, J9Object *srcObject)264{265/* If the source object is NULL, there is no need for a write barrier. */266if(NULL != srcObject) {267if (_extensions->isConcurrentScavengerEnabled() && !_extensions->isScavengerBackOutFlagRaised()) {268Assert_MM_false(_extensions->scavenger->isObjectInEvacuateMemory(dstObject));269Assert_MM_false(_extensions->scavenger->isObjectInEvacuateMemory(srcObject));270}271272#if defined(OMR_GC_MODRON_CONCURRENT_MARK)273/* Call the concurrent write barrier if required */274if(isIncrementalUpdateBarrierActive(vmThread) && _extensions->isOld(dstObject)) {275concurrentPostWriteBarrierStore(vmThread->omrVMThread, dstObject, srcObject);276}277#endif /* OMR_GC_MODRON_CONCURRENT_MARK */278279#if defined(J9VM_GC_GENERATIONAL)280_generationalAccessBarrierComponent.postObjectStore(vmThread, dstObject, srcObject);281#endif /* J9VM_GC_GENERATIONAL */282}283}284285/**286* Generational write barrier call when a group of objects are stored into a single object.287* The remembered set system consists of a physical list of objects in the OLD area that288* may contain references to the new area. The mutator is responsible for adding these old289* area objects to the remembered set; the collectors are responsible for removing these objects290* from the list when they no longer contain references. Objects that are to be remembered have their291* REMEMBERED bit set in the flags field. For performance reasons, sublists are used to maintain the292* remembered set.293*294* @param vmThread The current thread that has performed the store.295* @param dstObject The object which is being stored into.296*297* @note The write barrier can be called with minimal, all, or no validation checking.298* @note Any object that contains a new reference MUST have its REMEMBERED bit set.299* @note This call is typically used by array copies, when it may be more efficient300* to optimistically add an object to the remembered set without checking too hard.301*/302void303MM_StandardAccessBarrier::postBatchObjectStoreImpl(J9VMThread *vmThread, J9Object *dstObject)304{305#if defined(OMR_GC_MODRON_CONCURRENT_MARK)306Assert_MM_true(!_extensions->usingSATBBarrier());307/* Call the concurrent write barrier if required */308if(_extensions->concurrentMark &&309(vmThread->privateFlags & J9_PRIVATE_FLAGS_CONCURRENT_MARK_ACTIVE) &&310_extensions->isOld(dstObject)) {311concurrentPostWriteBarrierBatchStore(vmThread->omrVMThread, dstObject);312}313#endif /* OMR_GC_MODRON_CONCURRENT_MARK */314315#if defined(J9VM_GC_GENERATIONAL)316_generationalAccessBarrierComponent.postBatchObjectStore(vmThread, dstObject);317#endif /* J9VM_GC_GENERATIONAL */318}319320/**321* VMDESIGN 2048322* Special barrier for auto-remembering stack-referenced objects. This must be called323* in two cases:324* 1) an object which was allocated directly into old space.325* 2) an object which is being constructed via JNI326*327* @param vmThread[in] the current thread328* @param object[in] the object to be remembered329*/330void331MM_StandardAccessBarrier::recentlyAllocatedObject(J9VMThread *vmThread, J9Object *dstObject)332{333#if defined(J9VM_GC_GENERATIONAL)334MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(vmThread);335336if(extensions->scavengerEnabled && !extensions->isConcurrentScavengerEnabled() && extensions->isOld(dstObject) && !extensions->objectModel.isPrimitiveArray(dstObject)) {337338Trc_MM_StandardAccessBarrier_treatObjectAsRecentlyAllocated(vmThread, dstObject);339340if(extensions->objectModel.atomicSwitchReferencedState(dstObject, OMR_TENURED_STACK_OBJECT_CURRENTLY_REFERENCED)) {341/* Successfully set the remembered bit in the object. Now allocate an entry from the342* remembered set fragment of the current thread and store the destination object into343* the remembered set. */344MM_EnvironmentStandard *env = MM_EnvironmentStandard::getEnvironment(vmThread->omrVMThread);345MM_SublistFragment fragment((J9VMGC_SublistFragment*)&vmThread->gcRememberedSet);346if (!fragment.add(env, (UDATA)dstObject )) {347/* No slot was available from any fragment. Set the remembered set overflow flag.348* The REMEMBERED bit is kept in the object for optimization purposes (only scan objects349* whose REMEMBERED bit is set in an overflow scan)350*/351extensions->setRememberedSetOverflowState();352#if defined(J9VM_GC_MODRON_EVENTS)353reportRememberedSetOverflow(vmThread);354#endif /* J9VM_GC_MODRON_EVENTS */355}356}357}358#endif /* J9VM_GC_GENERATIONAL */359}360361void*362MM_StandardAccessBarrier::jniGetPrimitiveArrayCritical(J9VMThread* vmThread, jarray array, jboolean *isCopy)363{364void *data = NULL;365J9JavaVM *javaVM = vmThread->javaVM;366J9InternalVMFunctions *functions = javaVM->internalVMFunctions;367368bool shouldCopy = false;369bool alwaysCopyInCritical = (javaVM->runtimeFlags & J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) == J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL;370if (alwaysCopyInCritical) {371shouldCopy = true;372}373374if(shouldCopy) {375VM_VMAccess::inlineEnterVMFromJNI(vmThread);376J9IndexableObject *arrayObject = (J9IndexableObject*)J9_JNI_UNWRAP_REFERENCE(array);377GC_ArrayObjectModel* indexableObjectModel = &_extensions->indexableObjectModel;378I_32 sizeInElements = (I_32)indexableObjectModel->getSizeInElements(arrayObject);379UDATA sizeInBytes = indexableObjectModel->getDataSizeInBytes(arrayObject);380data = functions->jniArrayAllocateMemoryFromThread(vmThread, sizeInBytes);381if(NULL == data) {382functions->setNativeOutOfMemoryError(vmThread, 0, 0); // better error message here?383} else {384indexableObjectModel->memcpyFromArray(data, arrayObject, 0, sizeInElements);385if(NULL != isCopy) {386*isCopy = JNI_TRUE;387}388}389vmThread->jniCriticalCopyCount += 1;390VM_VMAccess::inlineExitVMToJNI(vmThread);391} else {392// acquire access and return a direct pointer393MM_JNICriticalRegion::enterCriticalRegion(vmThread, false);394J9IndexableObject *arrayObject = (J9IndexableObject*)J9_JNI_UNWRAP_REFERENCE(array);395data = (void *)_extensions->indexableObjectModel.getDataPointerForContiguous(arrayObject);396if(NULL != isCopy) {397*isCopy = JNI_FALSE;398}399}400return data;401}402403void404MM_StandardAccessBarrier::jniReleasePrimitiveArrayCritical(J9VMThread* vmThread, jarray array, void * elems, jint mode)405{406J9JavaVM *javaVM = vmThread->javaVM;407J9InternalVMFunctions *functions = javaVM->internalVMFunctions;408409bool shouldCopy = false;410bool alwaysCopyInCritical = (javaVM->runtimeFlags & J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) == J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL;411if (alwaysCopyInCritical) {412shouldCopy = true;413}414415if(shouldCopy) {416VM_VMAccess::inlineEnterVMFromJNI(vmThread);417if(JNI_ABORT != mode) {418J9IndexableObject *arrayObject = (J9IndexableObject*)J9_JNI_UNWRAP_REFERENCE(array);419GC_ArrayObjectModel* indexableObjectModel = &_extensions->indexableObjectModel;420I_32 sizeInElements = (I_32)indexableObjectModel->getSizeInElements(arrayObject);421_extensions->indexableObjectModel.memcpyToArray(arrayObject, 0, sizeInElements, elems);422}423424// Commit means copy the data but do not free the buffer.425// All other modes free the buffer.426if(JNI_COMMIT != mode) {427functions->jniArrayFreeMemoryFromThread(vmThread, elems);428}429430if(vmThread->jniCriticalCopyCount > 0) {431vmThread->jniCriticalCopyCount -= 1;432} else {433Assert_MM_invalidJNICall();434}435436VM_VMAccess::inlineExitVMToJNI(vmThread);437} else {438/*439* Objects can not be moved if critical section is active440* This trace point will be generated if object has been moved or passed value of elems is corrupted441*/442J9IndexableObject *arrayObject = (J9IndexableObject*)J9_JNI_UNWRAP_REFERENCE(array);443void *data = (void *)_extensions->indexableObjectModel.getDataPointerForContiguous(arrayObject);444if(elems != data) {445Trc_MM_JNIReleasePrimitiveArrayCritical_invalid(vmThread, arrayObject, elems, data);446}447448MM_JNICriticalRegion::exitCriticalRegion(vmThread, false);449}450}451452const jchar*453MM_StandardAccessBarrier::jniGetStringCritical(J9VMThread* vmThread, jstring str, jboolean *isCopy)454{455jchar *data = NULL;456J9JavaVM *javaVM = vmThread->javaVM;457J9InternalVMFunctions *functions = javaVM->internalVMFunctions;458bool isCompressed = false;459bool shouldCopy = false;460bool hasVMAccess = false;461462if ((javaVM->runtimeFlags & J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) == J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) {463VM_VMAccess::inlineEnterVMFromJNI(vmThread);464hasVMAccess = true;465shouldCopy = true;466} else if (IS_STRING_COMPRESSION_ENABLED_VM(javaVM)) {467/* If the string bytes are in compressed UNICODE, then we need to copy to decompress */468VM_VMAccess::inlineEnterVMFromJNI(vmThread);469hasVMAccess = true;470J9Object *stringObject = (J9Object*)J9_JNI_UNWRAP_REFERENCE(str);471if (IS_STRING_COMPRESSED(vmThread,stringObject)) {472isCompressed = true;473shouldCopy = true;474}475} else if (_extensions->isConcurrentScavengerEnabled()) {476/* reading value field from String object may trigger object movement */477VM_VMAccess::inlineEnterVMFromJNI(vmThread);478hasVMAccess = true;479}480481if (shouldCopy) {482J9Object *stringObject = (J9Object*)J9_JNI_UNWRAP_REFERENCE(str);483J9IndexableObject *valueObject = (J9IndexableObject*)J9VMJAVALANGSTRING_VALUE(vmThread, stringObject);484jint length = J9VMJAVALANGSTRING_LENGTH(vmThread, stringObject);485UDATA sizeInBytes = length * sizeof(jchar);486487if (IS_STRING_COMPRESSED(vmThread, stringObject)) {488isCompressed = true;489}490491data = (jchar*)functions->jniArrayAllocateMemoryFromThread(vmThread, sizeInBytes);492if (NULL == data) {493functions->setNativeOutOfMemoryError(vmThread, 0, 0); // better error message here?494} else {495GC_ArrayObjectModel* indexableObjectModel = &_extensions->indexableObjectModel;496if (isCompressed) {497jint i;498499for (i = 0; i < length; i++) {500data[i] = (jchar)(U_8)J9JAVAARRAYOFBYTE_LOAD(vmThread, (j9object_t)valueObject, i);501}502} else {503if (J9_ARE_ANY_BITS_SET(javaVM->runtimeFlags, J9_RUNTIME_STRING_BYTE_ARRAY)) {504// This API determines the stride based on the type of valueObject so in the [B case we must passin the length in bytes505indexableObjectModel->memcpyFromArray(data, valueObject, 0, (I_32)sizeInBytes);506} else {507indexableObjectModel->memcpyFromArray(data, valueObject, 0, length);508}509}510if (NULL != isCopy) {511*isCopy = JNI_TRUE;512}513}514vmThread->jniCriticalCopyCount += 1;515} else {516// acquire access and return a direct pointer517MM_JNICriticalRegion::enterCriticalRegion(vmThread, hasVMAccess);518J9Object *stringObject = (J9Object*)J9_JNI_UNWRAP_REFERENCE(str);519J9IndexableObject *valueObject = (J9IndexableObject*)J9VMJAVALANGSTRING_VALUE(vmThread, stringObject);520521data = (jchar*)_extensions->indexableObjectModel.getDataPointerForContiguous(valueObject);522523if (NULL != isCopy) {524*isCopy = JNI_FALSE;525}526}527if (hasVMAccess) {528VM_VMAccess::inlineExitVMToJNI(vmThread);529}530return data;531}532533void534MM_StandardAccessBarrier::jniReleaseStringCritical(J9VMThread* vmThread, jstring str, const jchar* elems)535{536J9JavaVM *javaVM = vmThread->javaVM;537J9InternalVMFunctions *functions = javaVM->internalVMFunctions;538bool hasVMAccess = false;539bool shouldCopy = false;540541if ((javaVM->runtimeFlags & J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) == J9_RUNTIME_ALWAYS_COPY_JNI_CRITICAL) {542shouldCopy = true;543} else if (IS_STRING_COMPRESSION_ENABLED_VM(javaVM)) {544VM_VMAccess::inlineEnterVMFromJNI(vmThread);545hasVMAccess = true;546J9Object *stringObject = (J9Object*)J9_JNI_UNWRAP_REFERENCE(str);547if (IS_STRING_COMPRESSED(vmThread, stringObject)) {548shouldCopy = true;549}550}551552if (shouldCopy) {553// String data is not copied back554functions->jniArrayFreeMemoryFromThread(vmThread, (void*)elems);555556if(vmThread->jniCriticalCopyCount > 0) {557vmThread->jniCriticalCopyCount -= 1;558} else {559Assert_MM_invalidJNICall();560}561} else {562/*563* We have not put assertion here to check is elems valid for str similar way as in jniReleasePrimitiveArrayCritical564* because of complexity of required code565*/566// direct pointer, just drop access567MM_JNICriticalRegion::exitCriticalRegion(vmThread, hasVMAccess);568}569570if (hasVMAccess) {571VM_VMAccess::inlineExitVMToJNI(vmThread);572}573}574575UDATA576MM_StandardAccessBarrier::getJNICriticalRegionCount(MM_GCExtensions *extensions)577{578/* TODO kmt : This is probably the slowest way to get this information */579GC_VMThreadListIterator threadIterator(((J9JavaVM *)extensions->getOmrVM()->_language_vm));580J9VMThread *walkThread;581UDATA activeCriticals = 0;582583// TODO kmt : Should get public flags mutex here -- worst case is a false positive584while((walkThread = threadIterator.nextVMThread()) != NULL) {585activeCriticals += walkThread->jniCriticalDirectCount;586}587return activeCriticals;588}589590#if defined(OMR_GC_CONCURRENT_SCAVENGER)591I_32592MM_StandardAccessBarrier::doCopyContiguousBackwardWithReadBarrier(J9VMThread *vmThread, J9IndexableObject *srcObject, J9IndexableObject *destObject, I_32 srcIndex, I_32 destIndex, I_32 lengthInSlots)593{594srcIndex += lengthInSlots;595destIndex += lengthInSlots;596597if (J9VMTHREAD_COMPRESS_OBJECT_REFERENCES(vmThread)) {598uint32_t *srcSlot = (uint32_t *)indexableEffectiveAddress(vmThread, srcObject, srcIndex, sizeof(uint32_t));599uint32_t *destSlot = (uint32_t *)indexableEffectiveAddress(vmThread, destObject, destIndex, sizeof(uint32_t));600uint32_t *srcEndSlot = srcSlot - lengthInSlots;601602while (srcSlot-- > srcEndSlot) {603preObjectRead(vmThread, (J9Object *)srcObject, (fj9object_t*)srcSlot);604605*--destSlot = *srcSlot;606}607} else {608uintptr_t *srcSlot = (uintptr_t *)indexableEffectiveAddress(vmThread, srcObject, srcIndex, sizeof(uintptr_t));609uintptr_t *destSlot = (uintptr_t *)indexableEffectiveAddress(vmThread, destObject, destIndex, sizeof(uintptr_t));610uintptr_t *srcEndSlot = srcSlot - lengthInSlots;611612while (srcSlot-- > srcEndSlot) {613preObjectRead(vmThread, (J9Object *)srcObject, (fj9object_t*)srcSlot);614615*--destSlot = *srcSlot;616}617}618619return ARRAY_COPY_SUCCESSFUL;620}621622I_32623MM_StandardAccessBarrier::doCopyContiguousForwardWithReadBarrier(J9VMThread *vmThread, J9IndexableObject *srcObject, J9IndexableObject *destObject, I_32 srcIndex, I_32 destIndex, I_32 lengthInSlots)624{625if (J9VMTHREAD_COMPRESS_OBJECT_REFERENCES(vmThread)) {626uint32_t *srcSlot = (uint32_t *)indexableEffectiveAddress(vmThread, srcObject, srcIndex, sizeof(uint32_t));627uint32_t *destSlot = (uint32_t *)indexableEffectiveAddress(vmThread, destObject, destIndex, sizeof(uint32_t));628uint32_t *srcEndSlot = srcSlot + lengthInSlots;629630while (srcSlot < srcEndSlot) {631preObjectRead(vmThread, (J9Object *)srcObject, (fj9object_t*)srcSlot);632633*destSlot++ = *srcSlot++;634}635} else {636uintptr_t *srcSlot = (uintptr_t *)indexableEffectiveAddress(vmThread, srcObject, srcIndex, sizeof(uintptr_t));637uintptr_t *destSlot = (uintptr_t *)indexableEffectiveAddress(vmThread, destObject, destIndex, sizeof(uintptr_t));638uintptr_t *srcEndSlot = srcSlot + lengthInSlots;639640while (srcSlot < srcEndSlot) {641preObjectRead(vmThread, (J9Object *)srcObject, (fj9object_t*)srcSlot);642643*destSlot++ = *srcSlot++;644}645}646647return ARRAY_COPY_SUCCESSFUL;648}649#endif /* OMR_GC_CONCURRENT_SCAVENGER */650651/**652* Finds opportunities for doing the copy without or partially executing writeBarrier.653* @return ARRAY_COPY_SUCCESSFUL if copy was successful, ARRAY_COPY_NOT_DONE no copy is done654*/655I_32656MM_StandardAccessBarrier::backwardReferenceArrayCopyIndex(J9VMThread *vmThread, J9IndexableObject *srcObject, J9IndexableObject *destObject, I_32 srcIndex, I_32 destIndex, I_32 lengthInSlots)657{658I_32 retValue = ARRAY_COPY_NOT_DONE;659/* TODO SATB re-enable opt? */660if (_extensions->usingSATBBarrier()) {661return retValue;662}663664if(0 == lengthInSlots) {665retValue = ARRAY_COPY_SUCCESSFUL;666} else {667/* a high level caller ensured destObject == srcObject */668Assert_MM_true(destObject == srcObject);669Assert_MM_true(_extensions->indexableObjectModel.isInlineContiguousArraylet(destObject));670671#if defined(OMR_GC_CONCURRENT_SCAVENGER)672if (_extensions->isConcurrentScavengerInProgress()) {673/* During active CS cycle, we need a RB for every slot being copied.674* For WB same rules apply - just need the final batch barrier.675*/676retValue = doCopyContiguousBackwardWithReadBarrier(vmThread, srcObject, destObject, srcIndex, destIndex, lengthInSlots);677} else678#endif /* OMR_GC_CONCURRENT_SCAVENGER */679{680retValue = doCopyContiguousBackward(vmThread, srcObject, destObject, srcIndex, destIndex, lengthInSlots);681}682Assert_MM_true(retValue == ARRAY_COPY_SUCCESSFUL);683684postBatchObjectStoreImpl(vmThread, (J9Object *)destObject);685}686return retValue;687}688689/**690* Finds opportunities for doing the copy without or partially executing writeBarrier.691* @return ARRAY_COPY_SUCCESSFUL if copy was successful, ARRAY_COPY_NOT_DONE no copy is done692*/693I_32694MM_StandardAccessBarrier::forwardReferenceArrayCopyIndex(J9VMThread *vmThread, J9IndexableObject *srcObject, J9IndexableObject *destObject, I_32 srcIndex, I_32 destIndex, I_32 lengthInSlots)695{696/* TODO SATB re-enable opt */697I_32 retValue = ARRAY_COPY_NOT_DONE;698699if (_extensions->usingSATBBarrier()) {700return retValue;701}702703if(0 == lengthInSlots) {704retValue = ARRAY_COPY_SUCCESSFUL;705} else {706Assert_MM_true(_extensions->indexableObjectModel.isInlineContiguousArraylet(destObject));707Assert_MM_true(_extensions->indexableObjectModel.isInlineContiguousArraylet(srcObject));708709#if defined(OMR_GC_CONCURRENT_SCAVENGER)710if (_extensions->isConcurrentScavengerInProgress()) {711/* During active CS cycle, we need a RB for every slot being copied.712* For WB same rules apply - just need the final batch barrier.713*/714retValue = doCopyContiguousForwardWithReadBarrier(vmThread, srcObject, destObject, srcIndex, destIndex, lengthInSlots);715} else716#endif /* OMR_GC_CONCURRENT_SCAVENGER */717{718retValue = doCopyContiguousForward(vmThread, srcObject, destObject, srcIndex, destIndex, lengthInSlots);719}720721Assert_MM_true(retValue == ARRAY_COPY_SUCCESSFUL);722postBatchObjectStoreImpl(vmThread, (J9Object *)destObject);723}724return retValue;725}726727J9Object*728MM_StandardAccessBarrier::asConstantPoolObject(J9VMThread *vmThread, J9Object* toConvert, UDATA allocationFlags)729{730j9object_t cpObject = toConvert;731732Assert_MM_true(allocationFlags & (J9_GC_ALLOCATE_OBJECT_TENURED | J9_GC_ALLOCATE_OBJECT_NON_INSTRUMENTABLE));733734if (NULL != toConvert) {735Assert_MM_false(_extensions->objectModel.isIndexable(toConvert));736if (!_extensions->isOld(toConvert)) {737MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);738if (!env->saveObjects(toConvert)) {739Assert_MM_unreachable();740}741J9Class *j9class = J9GC_J9OBJECT_CLAZZ_THREAD(toConvert, vmThread);742cpObject = J9AllocateObject(vmThread, j9class, allocationFlags);743env->restoreObjects(&toConvert);744if (cpObject != NULL) {745cloneObject(vmThread, toConvert, cpObject);746}747}748}749return cpObject;750}751752#if defined(OMR_GC_CONCURRENT_SCAVENGER)753bool754MM_StandardAccessBarrier::preWeakRootSlotRead(J9VMThread *vmThread, j9object_t *srcAddress)755{756omrobjectptr_t object = (omrobjectptr_t)*srcAddress;757bool const compressed = compressObjectReferences();758759if ((NULL != _extensions->scavenger) && _extensions->scavenger->isObjectInEvacuateMemory(object)) {760MM_EnvironmentStandard *env = MM_EnvironmentStandard::getEnvironment(vmThread->omrVMThread);761Assert_MM_true(_extensions->scavenger->isConcurrentCycleInProgress());762Assert_MM_true(_extensions->scavenger->isMutatorThreadInSyncWithCycle(env));763764MM_ForwardedHeader forwardHeader(object, compressed);765omrobjectptr_t forwardPtr = forwardHeader.getForwardedObject();766767if (NULL != forwardPtr) {768/* Object has been or is being copied - ensure the object is fully copied before exposing it, update the slot and return.769* Slot update needs to be atomic, only if there is a mutator thread racing with a write operation to this same slot.770* The barrier is typically used to update Monitor table entries, which should not be ever reinitialized by any mutator.771* Updated can occur, but only by GC threads during STW clearing phase, thus no race with this barrier. */772forwardHeader.copyOrWait(forwardPtr);773*srcAddress = forwardPtr;774}775/* Do nothing if the object is not copied already, since it might be dead.776* This object is found without real reference to it,777* for example by iterating monitor table to dump info about all monitors */778}779780return true;781}782783bool784MM_StandardAccessBarrier::preWeakRootSlotRead(J9JavaVM *vm, j9object_t *srcAddress)785{786omrobjectptr_t object = (omrobjectptr_t)*srcAddress;787bool const compressed = compressObjectReferences();788789if ((NULL != _extensions->scavenger) && _extensions->scavenger->isObjectInEvacuateMemory(object)) {790Assert_MM_true(_extensions->scavenger->isConcurrentCycleInProgress());791792MM_ForwardedHeader forwardHeader(object, compressed);793omrobjectptr_t forwardPtr = forwardHeader.getForwardedObject();794795if (NULL != forwardPtr) {796/* Object has been or is being copied - ensure the object is fully copied before exposing it, update the slot and return */797forwardHeader.copyOrWait(forwardPtr);798*srcAddress = forwardPtr;799}800/* Do nothing if the object is not copied already, since it might be dead.801* This object is found unintentionally (without real reference to it),802* for example by iterating colliding entries in monitor hash table */803}804805return true;806}807808bool809MM_StandardAccessBarrier::preObjectRead(J9VMThread *vmThread, J9Class *srcClass, j9object_t *srcAddress)810{811omrobjectptr_t object = *(volatile omrobjectptr_t *)srcAddress;812bool const compressed = compressObjectReferences();813814if ((NULL != _extensions->scavenger) && _extensions->scavenger->isObjectInEvacuateMemory(object)) {815MM_EnvironmentStandard *env = MM_EnvironmentStandard::getEnvironment(vmThread->omrVMThread);816Assert_MM_true(_extensions->scavenger->isConcurrentCycleInProgress());817Assert_MM_true(_extensions->scavenger->isMutatorThreadInSyncWithCycle(env));818819MM_ForwardedHeader forwardHeader(object, compressed);820omrobjectptr_t forwardPtr = forwardHeader.getForwardedObject();821822if (NULL != forwardPtr) {823/* Object has been strictly (remotely) forwarded. Ensure the object is fully copied before exposing it, update the slot and return. */824forwardHeader.copyOrWait(forwardPtr);825MM_AtomicOperations::lockCompareExchange((uintptr_t *)srcAddress, (uintptr_t)object, (uintptr_t)forwardPtr);826} else {827omrobjectptr_t destinationObjectPtr = _extensions->scavenger->copyObject(env, &forwardHeader);828if (NULL == destinationObjectPtr) {829/* Failure - the scavenger must back out the work it has done. Attempt to return the original object. */830forwardPtr = forwardHeader.setSelfForwardedObject();831if (forwardPtr != object) {832/* Another thread successfully copied the object. Re-fetch forwarding pointer,833* and ensure the object is fully copied before exposing it. */834MM_ForwardedHeader(object, compressed).copyOrWait(forwardPtr);835MM_AtomicOperations::lockCompareExchange((uintptr_t *)srcAddress, (uintptr_t)object, (uintptr_t)forwardPtr);836}837} else {838/* Update the slot. copyObject() ensures that the object is fully copied. */839MM_AtomicOperations::lockCompareExchange((uintptr_t *)srcAddress, (uintptr_t)object, (uintptr_t)destinationObjectPtr);840}841}842}843844return true;845}846847848#define GLOBAL_READ_BARRIR_STATS_UPDATE_THRESHOLD 32849850bool851MM_StandardAccessBarrier::preObjectRead(J9VMThread *vmThread, J9Object *srcObject, fj9object_t *srcAddress)852{853/* with volatile cast, ensure that we are really getting a snapshot (instead of the slot being re-read at later points with possibly different values) */854bool const compressed = compressObjectReferences();855fomrobject_t objectToken = (fomrobject_t)(compressed ? (uintptr_t)*(volatile uint32_t *)srcAddress: *(volatile uintptr_t *)srcAddress);856omrobjectptr_t object = convertPointerFromToken(objectToken);857858if (NULL != _extensions->scavenger) {859MM_EnvironmentStandard *env = MM_EnvironmentStandard::getEnvironment(vmThread->omrVMThread);860Assert_GC_true_with_message(env, !_extensions->scavenger->isObjectInEvacuateMemory((omrobjectptr_t)srcAddress) || _extensions->isScavengerBackOutFlagRaised(), "readObject %llx in Evacuate\n", srcAddress);861if (_extensions->scavenger->isObjectInEvacuateMemory(object)) {862Assert_GC_true_with_message2(env, _extensions->scavenger->isConcurrentCycleInProgress(),863"CS not in progress, found a object in Survivor: slot %llx object %llx\n", srcAddress, object);864Assert_MM_true(_extensions->scavenger->isMutatorThreadInSyncWithCycle(env));865/* since object is still in evacuate, srcObject has not been scanned yet => we cannot assert866* if srcObject should (already) be remembered (even if it's old)867*/868869env->_scavengerStats._readObjectBarrierUpdate += 1;870if (GLOBAL_READ_BARRIR_STATS_UPDATE_THRESHOLD == env->_scavengerStats._readObjectBarrierUpdate) {871MM_AtomicOperations::addU64(&_extensions->scavengerStats._readObjectBarrierUpdate, GLOBAL_READ_BARRIR_STATS_UPDATE_THRESHOLD);872env->_scavengerStats._readObjectBarrierUpdate = 0;873}874875GC_SlotObject slotObject(env->getOmrVM(), srcAddress);876MM_ForwardedHeader forwardHeader(object, compressed);877omrobjectptr_t forwardPtr = forwardHeader.getForwardedObject();878if (NULL != forwardPtr) {879/* Object has been strictly (remotely) forwarded. Ensure the object is fully copied before exposing it, update the slot and return. */880forwardHeader.copyOrWait(forwardPtr);881slotObject.atomicWriteReferenceToSlot(object, forwardPtr);882} else {883omrobjectptr_t destinationObjectPtr = _extensions->scavenger->copyObject(env, &forwardHeader);884if (NULL == destinationObjectPtr) {885/* We have no place to copy (or less likely, we lost to another thread forwarding it).886* We are forced to return the original location of the object.887* But we must prevent any other thread of making a copy of this object.888* So we will attempt to atomically self forward it. */889forwardPtr = forwardHeader.setSelfForwardedObject();890if (forwardPtr != object) {891/* Some other thread successfully copied this object. Re-fetch forwarding pointer,892* and ensure the object is fully copied before exposing it. */893MM_ForwardedHeader(object, compressed).copyOrWait(forwardPtr);894slotObject.atomicWriteReferenceToSlot(object, forwardPtr);895}896/* ... else it's self-forwarded -> no need to update the src slot */897} else {898/* Successfully copied (or copied by another thread). copyObject() ensures that the object is fully copied. */899slotObject.atomicWriteReferenceToSlot(object, destinationObjectPtr);900901env->_scavengerStats._readObjectBarrierCopy += 1;902if (GLOBAL_READ_BARRIR_STATS_UPDATE_THRESHOLD == env->_scavengerStats._readObjectBarrierCopy) {903MM_AtomicOperations::addU64(&_extensions->scavengerStats._readObjectBarrierCopy, GLOBAL_READ_BARRIR_STATS_UPDATE_THRESHOLD);904env->_scavengerStats._readObjectBarrierCopy = 0;905}906}907}908}909}910return true;911}912#endif913914void915MM_StandardAccessBarrier::forcedToFinalizableObject(J9VMThread* vmThread, J9Object* object)916{917MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);918if (_extensions->isSATBBarrierActive()) {919rememberObjectToRescan(env, object);920}921}922923/**924* Override of referenceGet. When the collector is tracing, it makes any gotten object "grey" to ensure925* that it is eventually traced.926*927* @param refObject the SoftReference or WeakReference object on which get() is being called.928* This barrier must not be called for PhantomReferences. The parameter must not be NULL.929*/930J9Object *931MM_StandardAccessBarrier::referenceGet(J9VMThread *vmThread, J9Object *refObject)932{933J9Object *referent = J9VMJAVALANGREFREFERENCE_REFERENT_VM(vmThread->javaVM, refObject);934935/* SATB - Throughout tracing, we must turn any gotten reference into a root, because the936* thread doing the getting may already have been scanned. However, since we are937* running on a mutator thread and not a gc thread we do this indirectly by putting938* the object in the barrier buffer.939*940* Do nothing exceptional for NULL or marked referents941*/942if ((_extensions->isSATBBarrierActive()) && (NULL != referent) && (!_markingScheme->isMarked(referent))) {943MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);944rememberObjectToRescan(env, referent);945}946947/* We must return the external reference */948return referent;949}950951void952MM_StandardAccessBarrier::referenceReprocess(J9VMThread *vmThread, J9Object *refObject)953{954if (_extensions->usingSATBBarrier()) {955referenceGet(vmThread, refObject);956} else {957postBatchObjectStore(vmThread, refObject);958}959}960961void962MM_StandardAccessBarrier::jniDeleteGlobalReference(J9VMThread *vmThread, J9Object *reference)963{964if (_extensions->isSATBBarrierActive()) {965MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);966rememberObjectToRescan(env, reference);967}968}969970void971MM_StandardAccessBarrier::stringConstantEscaped(J9VMThread *vmThread, J9Object *stringConst)972{973MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);974975if (_extensions->isSATBBarrierActive()) {976rememberObjectToRescan(env, stringConst);977}978}979980bool981MM_StandardAccessBarrier::checkStringConstantsLive(J9JavaVM *javaVM, j9object_t stringOne, j9object_t stringTwo)982{983if (_extensions->isSATBBarrierActive()) {984J9VMThread* vmThread = javaVM->internalVMFunctions->currentVMThread(javaVM);985stringConstantEscaped(vmThread, (J9Object *)stringOne);986stringConstantEscaped(vmThread, (J9Object *)stringTwo);987}988989return true;990}991992/**993* Equivalent to checkStringConstantsLive but for a single string constant994*/995bool996MM_StandardAccessBarrier::checkStringConstantLive(J9JavaVM *javaVM, j9object_t string)997{998if (_extensions->isSATBBarrierActive()) {999J9VMThread* vmThread = javaVM->internalVMFunctions->currentVMThread(javaVM);1000stringConstantEscaped(vmThread, (J9Object *)string);1001}10021003return true;1004}10051006#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)1007bool1008MM_StandardAccessBarrier::checkClassLive(J9JavaVM *javaVM, J9Class *classPtr)1009{1010bool result = true;10111012if (_extensions->usingSATBBarrier()) {1013J9ClassLoader *classLoader = classPtr->classLoader;1014result = false;10151016if ((0 == (classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD)) && (0 == (J9CLASS_FLAGS(classPtr) & J9AccClassDying))) {1017result = true;1018/* this class has not been discovered dead yet so mark it if necessary to force it to be alive */1019J9Object *classLoaderObject = classLoader->classLoaderObject;10201021if (NULL != classLoaderObject) {1022/* If mark is active but not completed yet force this class to be marked to survive this GC */1023J9VMThread* vmThread = javaVM->internalVMFunctions->currentVMThread(javaVM);1024MM_EnvironmentBase* env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread);1025if (_extensions->isSATBBarrierActive()) {1026rememberObjectToRescan(env, classLoaderObject);1027}1028}1029/* else this class loader probably is in initialization process and class loader object has not been attached yet */1030}1031}10321033return result;1034}1035#endif /* defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */1036103710381039