Path: blob/master/runtime/gc_base/IndexableObjectAllocationModel.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*******************************************************************************/2122#include "ArrayletObjectModel.hpp"23#include "GCExtensions.hpp"24#include "IndexableObjectAllocationModel.hpp"25#include "Math.hpp"26#include "MemorySpace.hpp"27#if defined(J9VM_GC_ENABLE_DOUBLE_MAP)28#include "ArrayletLeafIterator.hpp"29#include "HeapRegionManager.hpp"30#include "HeapRegionDescriptorVLHGC.hpp"31#include "Heap.hpp"32#endif /* J9VM_GC_ENABLE_DOUBLE_MAP */3334/**35* Allocation description and layout initialization. This is called before OMR allocates36* (and possibly zeroes) the raw bytes for the arraylet spine.37*/38bool39MM_IndexableObjectAllocationModel::initializeAllocateDescription(MM_EnvironmentBase *env)40{41/* prerequisite base class initialization of description */42if (!isAllocatable()) {43return false;44}4546/* continue, with reservations */47setAllocatable(false);48MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(env);49uintptr_t spineBytes = extensions->indexableObjectModel.getSpineSize(_class, _layout, _numberOfArraylets, _dataSize, _alignSpineDataSection);50#if defined (J9VM_GC_MODRON_COMPACTION) || defined (J9VM_GC_GENERATIONAL)51if (_allocateDescription.getPreHashFlag()) {52if (spineBytes == extensions->indexableObjectModel.getHashcodeOffset(_class, _layout, _numberOfIndexedFields)) {53/* Add extra uintptr_t for hash */54spineBytes += sizeof(uintptr_t);55}56}57#endif /* defined (J9VM_GC_MODRON_COMPACTION) || defined (J9VM_GC_GENERATIONAL) */58spineBytes = extensions->objectModel.adjustSizeInBytes(spineBytes);5960/* determine size of layout overhead (additional to spine bytes) and finalize allocation description */61uintptr_t layoutSizeInBytes = 0;62switch (_layout) {63case GC_ArrayletObjectModel::Illegal:64/* invalid layout - not allocatable */65Assert_MM_unreachable();66break;6768case GC_ArrayletObjectModel::InlineContiguous:69/* all good */70setAllocatable(true);71break;7273case GC_ArrayletObjectModel::Discontiguous:74/* non-empty discontiguous arrays require slow-path allocate */75if (isGCAllowed() || (0 == _numberOfIndexedFields)) {76/* _numberOfArraylets discontiguous leaves, all contains leaf size bytes */77layoutSizeInBytes = _dataSize;78_allocateDescription.setChunkedArray(true);79Trc_MM_allocateAndConnectNonContiguousArraylet_Entry(env->getLanguageVMThread(),80_numberOfIndexedFields, spineBytes, _numberOfArraylets);81setAllocatable(true);82}83break;8485case GC_ArrayletObjectModel::Hybrid:86Assert_MM_true(0 < _numberOfArraylets);87/* hybrid arrays always require slow-path allocate */88if (isGCAllowed()) {89/* (_dataSize % leaf size) bytes in spine, ((n-1) * leaf size) bytes in (_numberOfArraylets - 1) leaves */90layoutSizeInBytes = env->getOmrVM()->_arrayletLeafSize * (_numberOfArraylets - 1);91_allocateDescription.setChunkedArray(true);92Trc_MM_allocateAndConnectNonContiguousArraylet_Entry(env->getLanguageVMThread(),93_numberOfIndexedFields, spineBytes, _numberOfArraylets);94setAllocatable(true);95}96break;9798default:99Assert_MM_unreachable();100break;101}102103if (isAllocatable()) {104/* set total request size and layout metadata to finalize the description */105_allocateDescription.setBytesRequested(spineBytes + layoutSizeInBytes);106_allocateDescription.setNumArraylets(_numberOfArraylets);107_allocateDescription.setSpineBytes(spineBytes);108return true;109}110return false;111}112113/**114* Initializer. This is called after OMR has allocated raw (possibly zeroed) bytes for the spine115*/116omrobjectptr_t117MM_IndexableObjectAllocationModel::initializeIndexableObject(MM_EnvironmentBase *env, void *allocatedBytes)118{119/* Set array object header and size (in elements) and set description spine pointer */120MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(env);121GC_ArrayObjectModel *indexableObjectModel = &extensions->indexableObjectModel;122J9IndexableObject *spine = (J9IndexableObject*)initializeJavaObject(env, allocatedBytes);123_allocateDescription.setSpine(spine);124if (NULL != spine) {125/* Set the array size */126if (getAllocateDescription()->isChunkedArray()) {127indexableObjectModel->setSizeInElementsForDiscontiguous(spine, _numberOfIndexedFields);128#if defined(J9VM_ENV_DATA64)129indexableObjectModel->setDataAddrForDiscontiguous(spine, NULL);130#endif /* J9VM_ENV_DATA64 */131} else {132indexableObjectModel->setSizeInElementsForContiguous(spine, _numberOfIndexedFields);133#if defined(J9VM_ENV_DATA64)134indexableObjectModel->setDataAddrForContiguous(spine);135#endif /* J9VM_ENV_DATA64 */136}137}138139140/* Lay out arraylet and arrayoid pointers */141switch (_layout) {142case GC_ArrayletObjectModel::InlineContiguous:143Assert_MM_true(1 == _numberOfArraylets);144break;145146case GC_ArrayletObjectModel::Discontiguous:147case GC_ArrayletObjectModel::Hybrid:148if (NULL != spine) {149if(0 == _numberOfIndexedFields) {150/* Don't try to initialize the arrayoid for an empty NUA */151Trc_MM_allocateAndConnectNonContiguousArraylet_Exit(env->getLanguageVMThread(), spine);152break;153}154Trc_MM_allocateAndConnectNonContiguousArraylet_Summary(env->getLanguageVMThread(),155_numberOfIndexedFields, getAllocateDescription()->getContiguousBytes(), _numberOfArraylets);156spine = layoutDiscontiguousArraylet(env, spine);157Trc_MM_allocateAndConnectNonContiguousArraylet_Exit(env->getLanguageVMThread(), spine);158} else {159Trc_MM_allocateAndConnectNonContiguousArraylet_spineFailure(env->getLanguageVMThread());160}161break;162163default:164Assert_MM_unreachable();165break;166}167168if (NULL != spine) {169/* Initialize hashcode slot */170if (getAllocateDescription()->getPreHashFlag()) {171env->getExtensions()->objectModel.initializeHashSlot((J9JavaVM*)env->getLanguageVM(), (omrobjectptr_t)spine);172}173Assert_MM_true(env->getExtensions()->objectModel.isIndexable((omrobjectptr_t)spine));174}175176Assert_MM_true(spine == _allocateDescription.getSpine());177return (omrobjectptr_t)spine;178}179180/**181* For contiguous arraylet all data is in the spine but arrayoid pointers must still be laid down.182*183* @return initialized arraylet spine with its arraylet pointers initialized.184*/185MMINLINE J9IndexableObject *186MM_IndexableObjectAllocationModel::layoutContiguousArraylet(MM_EnvironmentBase *env, J9IndexableObject *spine)187{188Assert_MM_true(_numberOfArraylets == _allocateDescription.getNumArraylets());189MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(env);190bool const compressed = env->compressObjectReferences();191192/* set arraylet pointers in the spine. these all point into the data part of the spine */193fj9object_t *arrayoidPtr = extensions->indexableObjectModel.getArrayoidPointer(spine);194uintptr_t leafOffset = (uintptr_t)GC_SlotObject::addToSlotAddress(arrayoidPtr, _numberOfArraylets, compressed);195if (_alignSpineDataSection) {196leafOffset = MM_Math::roundToCeiling(sizeof(uint64_t), leafOffset);197}198uintptr_t arrayletLeafSize = env->getOmrVM()->_arrayletLeafSize;199for (uintptr_t i = 0; i < _numberOfArraylets; i++) {200GC_SlotObject slotObject(env->getOmrVM(), arrayoidPtr);201slotObject.writeReferenceToSlot((omrobjectptr_t)leafOffset);202leafOffset += arrayletLeafSize;203arrayoidPtr = GC_SlotObject::addToSlotAddress(arrayoidPtr, 1, compressed);204}205206return spine;207}208209/**210* For discontiguous or hybrid arraylet spine is allocated first and leaves are sequentially211* allocated and attached to the spine. The allocation description saves and restores the212* spine pointer in case a GC occurs while allocating the leaves.213*214* If a leaf allocation fails the spine and preceding arraylets are abandoned as floating215* garbage and NULL is returned.216*217* @return initialized arraylet spine with attached arraylet leaves, or NULL218*/219MMINLINE J9IndexableObject *220MM_IndexableObjectAllocationModel::layoutDiscontiguousArraylet(MM_EnvironmentBase *env, J9IndexableObject *spine)221{222Assert_MM_true(_numberOfArraylets == _allocateDescription.getNumArraylets());223224MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(env);225GC_ArrayObjectModel *indexableObjectModel = &extensions->indexableObjectModel;226bool const compressed = env->compressObjectReferences();227228/* determine how many bytes to allocate outside of the spine (in arraylet leaves) */229const uintptr_t arrayletLeafSize = env->getOmrVM()->_arrayletLeafSize;230Assert_MM_true(_allocateDescription.getBytesRequested() >= _allocateDescription.getContiguousBytes());231uintptr_t bytesRemaining = _allocateDescription.getBytesRequested() - _allocateDescription.getContiguousBytes();232Assert_MM_true((0 == (bytesRemaining % arrayletLeafSize)) || (GC_ArrayletObjectModel::Hybrid != _layout));233/* hybrid arraylets store _dataSize % arrayletLeafSize bytes in the spine, remainder in _numberOfArraylets-1 leaves */234235/* allocate leaf for each arraylet and attach it to its leaf pointer in the spine */236uintptr_t arrayoidIndex = 0;237fj9object_t *arrayoidPtr = indexableObjectModel->getArrayoidPointer(spine);238while (0 < bytesRemaining) {239/* allocate the next arraylet leaf */240void *leaf = env->_objectAllocationInterface->allocateArrayletLeaf(env, &_allocateDescription,241_allocateDescription.getMemorySpace(), true);242243/* if leaf allocation failed set the result to NULL and return */244if (NULL == leaf) {245/* spine and preceding arraylets are now floating garbage */246Trc_MM_allocateAndConnectNonContiguousArraylet_leafFailure(env->getLanguageVMThread());247_allocateDescription.setSpine(NULL);248spine = NULL;249break;250}251252/* refresh the spine -- it might move if we GC while allocating the leaf */253spine = _allocateDescription.getSpine();254arrayoidPtr = indexableObjectModel->getArrayoidPointer(spine);255256/* set the arrayoid pointer in the spine to point to the new leaf */257GC_SlotObject slotObject(env->getOmrVM(), GC_SlotObject::addToSlotAddress(arrayoidPtr, arrayoidIndex, compressed));258slotObject.writeReferenceToSlot((omrobjectptr_t)leaf);259260bytesRemaining -= OMR_MIN(bytesRemaining, arrayletLeafSize);261arrayoidIndex += 1;262}263264if (NULL != spine) {265switch (_layout) {266case GC_ArrayletObjectModel::Discontiguous:267indexableObjectModel->AssertArrayletIsDiscontiguous(spine);268Assert_MM_true(arrayoidIndex == _numberOfArraylets);269#if defined(J9VM_GC_ENABLE_DOUBLE_MAP)270if (indexableObjectModel->isDoubleMappingEnabled()) {271/**272* There are some special cases where double mapping an arraylet is273* not necessary; isArrayletDataDiscontiguous() details those cases.274*/275if (indexableObjectModel->isArrayletDataDiscontiguous(spine)) {276doubleMapArraylets(env, (J9Object *)spine, NULL);277}278}279#endif /* J9VM_GC_ENABLE_DOUBLE_MAP */280break;281282case GC_ArrayletObjectModel::Hybrid:283#if defined(J9VM_GC_ENABLE_DOUBLE_MAP)284/* Unreachable if double map is enabled */285if (indexableObjectModel->isDoubleMappingEnabled()) {286Assert_MM_double_map_unreachable();287}288#endif /* J9VM_GC_ENABLE_DOUBLE_MAP */289/* last arrayoid points to end of arrayoid array in spine header (object-aligned if290* required). (data size % leaf size) bytes of data are stored here (may be empty).291*/292Assert_MM_true(arrayoidIndex == (_numberOfArraylets - 1));293{294uintptr_t leafOffset = (uintptr_t)GC_SlotObject::addToSlotAddress(arrayoidPtr, _numberOfArraylets, compressed);295if (_alignSpineDataSection) {296leafOffset = MM_Math::roundToCeiling(env->getObjectAlignmentInBytes(), leafOffset);297}298/* set the last arrayoid pointer to point to remainder data */299GC_SlotObject slotObject(env->getOmrVM(), GC_SlotObject::addToSlotAddress(arrayoidPtr, arrayoidIndex, compressed));300slotObject.writeReferenceToSlot((omrobjectptr_t)leafOffset);301}302break;303304default:305Assert_MM_unreachable();306break;307}308}309310return spine;311}312313#if defined(J9VM_GC_ENABLE_DOUBLE_MAP)314#if !((defined(LINUX) || defined(OSX)) && defined(J9VM_ENV_DATA64))315/* Double map is only supported on LINUX 64 bit Systems for now */316#error "Platform not supported by Double Map API"317#endif /* !((defined(LINUX) || defined(OSX)) && defined(J9VM_ENV_DATA64)) */318void *319MM_IndexableObjectAllocationModel::doubleMapArraylets(MM_EnvironmentBase *env, J9Object *objectPtr, void *preferredAddress)320{321MM_GCExtensions *extensions = MM_GCExtensions::getExtensions(env);322J9JavaVM *javaVM = extensions->getJavaVM();323324GC_ArrayletLeafIterator arrayletLeafIterator(javaVM, (J9IndexableObject *)objectPtr);325MM_Heap *heap = extensions->getHeap();326UDATA arrayletLeafSize = env->getOmrVM()->_arrayletLeafSize;327UDATA arrayletLeafCount = MM_Math::roundToCeiling(arrayletLeafSize, _dataSize) / arrayletLeafSize;328Trc_MM_double_map_Entry(env->getLanguageVMThread(), (void *)objectPtr, arrayletLeafSize, arrayletLeafCount);329330void *result = NULL;331332#define ARRAYLET_ALLOC_THRESHOLD 64333void *leaves[ARRAYLET_ALLOC_THRESHOLD];334void **arrayletLeaveAddrs = leaves;335if (arrayletLeafCount > ARRAYLET_ALLOC_THRESHOLD) {336arrayletLeaveAddrs = (void **)env->getForge()->allocate(arrayletLeafCount * sizeof(uintptr_t), MM_AllocationCategory::GC_HEAP, J9_GET_CALLSITE());337}338339if (NULL == arrayletLeaveAddrs) {340return NULL;341}342343GC_SlotObject *slotObject = NULL;344uintptr_t count = 0;345346while (NULL != (slotObject = arrayletLeafIterator.nextLeafPointer())) {347void *currentLeaf = slotObject->readReferenceFromSlot();348arrayletLeaveAddrs[count] = currentLeaf;349count++;350}351352/* Number of arraylet leaves in the iterator must match the number of leaves calculated */353Assert_MM_true(arrayletLeafCount == count);354355GC_SlotObject objectSlot(env->getOmrVM(), extensions->indexableObjectModel.getArrayoidPointer((J9IndexableObject *)objectPtr));356J9Object *firstLeafSlot = objectSlot.readReferenceFromSlot();357358MM_HeapRegionDescriptorVLHGC *firstLeafRegionDescriptor = (MM_HeapRegionDescriptorVLHGC *)heap->getHeapRegionManager()->tableDescriptorForAddress(firstLeafSlot);359360/* Retrieve actual page size */361UDATA pageSize = heap->getPageSize();362363/* For now we double map the entire region of all arraylet leaves. This might change in the future if hybrid regions are introduced. */364uintptr_t byteAmount = arrayletLeafSize * arrayletLeafCount;365366/* Get heap and from there call an OMR API that will doble map everything */367result = heap->doubleMapRegions(env, arrayletLeaveAddrs, count, arrayletLeafSize, byteAmount,368&firstLeafRegionDescriptor->_arrayletDoublemapID,369pageSize,370preferredAddress);371372if (arrayletLeafCount > ARRAYLET_ALLOC_THRESHOLD) {373env->getForge()->free((void *)arrayletLeaveAddrs);374}375376/*377* Double map failed.378* If doublemap fails the caller must handle it appropriately. The only case being379* JNI critical, where it will fall back to copying each element of the array to380* a temporary array (logic handled by JNI Critical). It might hurt performance381* but execution won't halt.382*/383if (NULL == firstLeafRegionDescriptor->_arrayletDoublemapID.address) {384Trc_MM_double_map_Failed(env->getLanguageVMThread());385result = NULL;386}387388Trc_MM_double_map_Exit(env->getLanguageVMThread(), result);389return result;390}391#endif /* J9VM_GC_ENABLE_DOUBLE_MAP */392393394395