Skip to content

Commit

Permalink
Adjust heap size for CRIU restore
Browse files Browse the repository at this point in the history
When we snapshot on a machine with higher physical memory and restore on one
with lower physical memory, the InstantOn JVM heap size will be larger than
a usual JVM run on the restore machine. And the momory usage will be abnormally
higher.

Update the extension with the correct physical memory limit and utilize the
existing softmax mechanism to solve the problem. Note we only update the softmax
if it is larger than the minHeap size, smaller than the maxHeap size and
previously set softmax size (if any).

Related: #17596

Signed-off-by: Frank Kang <[email protected]>
  • Loading branch information
kangyining committed Sep 25, 2023
1 parent 749f58c commit 36112eb
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 56 deletions.
52 changes: 26 additions & 26 deletions runtime/gc_base/GCExtensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,44 +189,44 @@ MM_GCExtensions::tearDown(MM_EnvironmentBase *env)
}

void
MM_GCExtensions::identityHashDataAddRange(MM_EnvironmentBase *env, MM_MemorySubSpace* subspace, UDATA size, void* lowAddress, void* highAddress)
MM_GCExtensions::identityHashDataAddRange(MM_EnvironmentBase *env, MM_MemorySubSpace* subspace, uintptr_t size, void* lowAddress, void* highAddress)
{
J9IdentityHashData* hashData = getJavaVM()->identityHashData;
if (J9_IDENTITY_HASH_SALT_POLICY_STANDARD == hashData->hashSaltPolicy) {
if (MEMORY_TYPE_NEW == (subspace->getTypeFlags() & MEMORY_TYPE_NEW)) {
if (hashData->hashData1 == (UDATA)highAddress) {
if (hashData->hashData1 == (uintptr_t)highAddress) {
/* Expanding low bound */
hashData->hashData1 = (UDATA)lowAddress;
} else if (hashData->hashData2 == (UDATA)lowAddress) {
hashData->hashData1 = (uintptr_t)lowAddress;
} else if (hashData->hashData2 == (uintptr_t)lowAddress) {
/* Expanding high bound */
hashData->hashData2 = (UDATA)highAddress;
hashData->hashData2 = (uintptr_t)highAddress;
} else {
/* First expand */
Assert_MM_true(UDATA_MAX == hashData->hashData1);
Assert_MM_true(0 == hashData->hashData2);
hashData->hashData1 = (UDATA)lowAddress;
hashData->hashData2 = (UDATA)highAddress;
hashData->hashData1 = (uintptr_t)lowAddress;
hashData->hashData2 = (uintptr_t)highAddress;
}
}
}
}

void
MM_GCExtensions::identityHashDataRemoveRange(MM_EnvironmentBase *env, MM_MemorySubSpace* subspace, UDATA size, void* lowAddress, void* highAddress)
MM_GCExtensions::identityHashDataRemoveRange(MM_EnvironmentBase *env, MM_MemorySubSpace* subspace, uintptr_t size, void* lowAddress, void* highAddress)
{
J9IdentityHashData* hashData = getJavaVM()->identityHashData;
if (J9_IDENTITY_HASH_SALT_POLICY_STANDARD == hashData->hashSaltPolicy) {
if (MEMORY_TYPE_NEW == (subspace->getTypeFlags() & MEMORY_TYPE_NEW)) {
if (hashData->hashData1 == (UDATA)lowAddress) {
if (hashData->hashData1 == (uintptr_t)lowAddress) {
/* Contracting low bound */
Assert_MM_true(hashData->hashData1 <= (UDATA)highAddress);
Assert_MM_true((UDATA)highAddress <= hashData->hashData2);
hashData->hashData1 = (UDATA)highAddress;
} else if (hashData->hashData2 == (UDATA)highAddress) {
Assert_MM_true(hashData->hashData1 <= (uintptr_t)highAddress);
Assert_MM_true((uintptr_t)highAddress <= hashData->hashData2);
hashData->hashData1 = (uintptr_t)highAddress;
} else if (hashData->hashData2 == (uintptr_t)highAddress) {
/* Contracting high bound */
Assert_MM_true(hashData->hashData1 <= (UDATA)lowAddress);
Assert_MM_true((UDATA)lowAddress <= hashData->hashData2);
hashData->hashData2 = (UDATA)lowAddress;
Assert_MM_true(hashData->hashData1 <= (uintptr_t)lowAddress);
Assert_MM_true((uintptr_t)lowAddress <= hashData->hashData2);
hashData->hashData2 = (uintptr_t)lowAddress;
} else {
Assert_MM_unreachable();
}
Expand All @@ -235,7 +235,7 @@ MM_GCExtensions::identityHashDataRemoveRange(MM_EnvironmentBase *env, MM_MemoryS
}

void
MM_GCExtensions::updateIdentityHashDataForSaltIndex(UDATA index)
MM_GCExtensions::updateIdentityHashDataForSaltIndex(uintptr_t index)
{
getJavaVM()->identityHashData->hashSaltTable[index] = (U_32)convertValueToHash(getJavaVM(), getJavaVM()->identityHashData->hashSaltTable[index]);
}
Expand All @@ -244,37 +244,37 @@ MM_GCExtensions::updateIdentityHashDataForSaltIndex(UDATA index)
* computeDefaultMaxHeapForJava is for Java only, it will be called during gcParseCommandLineAndInitializeWithValues(),
* computeDefaultMaxHeap() will still be called during MM_GCExtensionsBase::initialize(), computeDefaultMaxHeapForJava() can overwrite value of memoryMax.
*/
void
uintptr_t
MM_GCExtensions::computeDefaultMaxHeapForJava(bool enableOriginalJDK8HeapSizeCompatibilityOption)
{
OMRPORT_ACCESS_FROM_OMRVM(_omrVM);

uintptr_t maxMemoryValue = memoryMax;
if (OMR_CGROUP_SUBSYSTEM_MEMORY == omrsysinfo_cgroup_are_subsystems_enabled(OMR_CGROUP_SUBSYSTEM_MEMORY)) {
if (omrsysinfo_cgroup_is_memlimit_set()) {
/* If running in a cgroup with memory limit > 1G, reserve at-least 512M for JVM's internal requirements
* like JIT compilation etc, and extend default max heap memory to at-most 75% of cgroup limit.
* The value reserved for JVM's internal requirements excludes heap. This value is a conservative
* estimate of the JVM's internal requirements, given that one compilation thread can use up to 256M.
*/
#define OPENJ9_IN_CGROUP_NATIVE_FOOTPRINT_EXCLUDING_HEAP ((U_64)512 * 1024 * 1024)
memoryMax = (uintptr_t)OMR_MAX((int64_t)(usablePhysicalMemory / 2), (int64_t)(usablePhysicalMemory - OPENJ9_IN_CGROUP_NATIVE_FOOTPRINT_EXCLUDING_HEAP));
memoryMax = (uintptr_t)OMR_MIN(memoryMax, (usablePhysicalMemory / 4) * 3);
#define OPENJ9_IN_CGROUP_NATIVE_FOOTPRINT_EXCLUDING_HEAP ((uint64_t)512 * 1024 * 1024)
maxMemoryValue = (uintptr_t)OMR_MAX((int64_t)(usablePhysicalMemory / 2), (int64_t)(usablePhysicalMemory - OPENJ9_IN_CGROUP_NATIVE_FOOTPRINT_EXCLUDING_HEAP));
maxMemoryValue = (uintptr_t)OMR_MIN(maxMemoryValue, (usablePhysicalMemory / 4) * 3);
#undef OPENJ9_IN_CGROUP_NATIVE_FOOTPRINT_EXCLUDING_HEAP
}
}

#if defined(OMR_ENV_DATA64)
if (!enableOriginalJDK8HeapSizeCompatibilityOption) {
/* extend java default max memory to 25% of usable RAM */
memoryMax = OMR_MAX(memoryMax, usablePhysicalMemory / 4);
maxMemoryValue = OMR_MAX(maxMemoryValue, usablePhysicalMemory / 4);
}

/* limit maxheapsize up to MAXIMUM_HEAP_SIZE_RECOMMENDED_FOR_3BIT_SHIFT_COMPRESSEDREFS, then can set 3bit compressedrefs as the default */
memoryMax = OMR_MIN(memoryMax, MAXIMUM_HEAP_SIZE_RECOMMENDED_FOR_3BIT_SHIFT_COMPRESSEDREFS);
maxMemoryValue = OMR_MIN(maxMemoryValue, MAXIMUM_HEAP_SIZE_RECOMMENDED_FOR_3BIT_SHIFT_COMPRESSEDREFS);
#endif /* OMR_ENV_DATA64 */

memoryMax = MM_Math::roundToFloor(heapAlignment, memoryMax);
maxSizeDefaultMemorySpace = memoryMax;
maxMemoryValue = MM_Math::roundToFloor(heapAlignment, maxMemoryValue);
return maxMemoryValue;
}

MM_OwnableSynchronizerObjectList *
Expand Down
58 changes: 33 additions & 25 deletions runtime/gc_base/GCExtensions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ class MM_GCExtensions : public MM_GCExtensionsBase {
J9MemoryManagerVerboseInterface verboseFunctionTable;

#if defined(J9VM_GC_FINALIZATION)
IDATA finalizeCycleInterval;
IDATA finalizeCycleLimit;
intptr_t finalizeCycleInterval;
intptr_t finalizeCycleLimit;
#endif /* J9VM_GC_FINALIZATION */

MM_HookInterface hookInterface;
Expand All @@ -121,11 +121,11 @@ class MM_GCExtensions : public MM_GCExtensionsBase {

bool dynamicClassUnloadingSet; /**< is true if value for dynamicClassUnloading was specified in command line */

UDATA runtimeCheckDynamicClassUnloading;
uintptr_t runtimeCheckDynamicClassUnloading;
bool dynamicClassUnloadingKickoffThresholdForced; /**< true if classUnloadingKickoffThreshold is specified in java options. */
bool dynamicClassUnloadingThresholdForced; /**< true if classUnloadingThresholdForced is specified in java options. */
UDATA dynamicClassUnloadingKickoffThreshold; /**< the threshold to kickoff a concurrent global GC from a scavenge */
UDATA dynamicClassUnloadingThreshold; /**< the threshold to trigger class unloading during a global GC */
uintptr_t dynamicClassUnloadingKickoffThreshold; /**< the threshold to kickoff a concurrent global GC from a scavenge */
uintptr_t dynamicClassUnloadingThreshold; /**< the threshold to trigger class unloading during a global GC */
double classUnloadingAnonymousClassWeight; /**< The weight factor to apply to anonymous classes for threshold comparisons */
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */

Expand All @@ -135,8 +135,8 @@ class MM_GCExtensions : public MM_GCExtensionsBase {
bool fvtest_forceFinalizeClassLoaders;
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */

UDATA maxSoftReferenceAge; /**< The fixed age specified as the soft reference threshold which acts as our baseline for the dynamicMaxSoftReferenceAge */
UDATA dynamicMaxSoftReferenceAge; /**< The age which represents the clearing age of soft references for a globalGC cycle. At the end of a GC cycle, it will be updated for the following cycle by taking the percentage of free heap in the oldest generation as a fraction of the maxSoftReferenceAge */
uintptr_t maxSoftReferenceAge; /**< The fixed age specified as the soft reference threshold which acts as our baseline for the dynamicMaxSoftReferenceAge */
uintptr_t dynamicMaxSoftReferenceAge; /**< The age which represents the clearing age of soft references for a globalGC cycle. At the end of a GC cycle, it will be updated for the following cycle by taking the percentage of free heap in the oldest generation as a fraction of the maxSoftReferenceAge */
#if defined(J9VM_GC_FINALIZATION)
GC_FinalizeListManager* finalizeListManager;
#endif /* J9VM_GC_FINALIZATION */
Expand All @@ -149,18 +149,18 @@ class MM_GCExtensions : public MM_GCExtensionsBase {
MM_ObjectAccessBarrier* accessBarrier;

#if defined(J9VM_GC_FINALIZATION)
UDATA finalizeMainPriority; /**< cmd line option to set finalize main thread priority */
UDATA finalizeWorkerPriority; /**< cmd line option to set finalize worker thread priority */
uintptr_t finalizeMainPriority; /**< cmd line option to set finalize main thread priority */
uintptr_t finalizeWorkerPriority; /**< cmd line option to set finalize worker thread priority */
#endif /* J9VM_GC_FINALIZATION */

MM_ClassLoaderManager* classLoaderManager; /**< Pointer to the gc's classloader manager to process classloaders/classes */
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
UDATA deadClassLoaderCacheSize;
uintptr_t deadClassLoaderCacheSize;
#endif /*defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING) */

MM_UnfinalizedObjectList* unfinalizedObjectLists; /**< The global linked list of unfinalized object lists. */

UDATA objectListFragmentCount; /**< the size of Local Object Buffer(per gc thread), used by referenceObjectBuffer, UnfinalizedObjectBuffer and OwnableSynchronizerObjectBuffer */
uintptr_t objectListFragmentCount; /**< the size of Local Object Buffer(per gc thread), used by referenceObjectBuffer, UnfinalizedObjectBuffer and OwnableSynchronizerObjectBuffer */

MM_Wildcard* numaCommonThreadClassNamePatterns; /**< A linked list of thread class names which should be associated with the common context */

Expand All @@ -182,8 +182,8 @@ class MM_GCExtensions : public MM_GCExtensionsBase {
};
JitStringDeDupPolicy stringDedupPolicy;

IDATA _asyncCallbackKey;
IDATA _TLHAsyncCallbackKey;
intptr_t _asyncCallbackKey;
intptr_t _TLHAsyncCallbackKey;

bool _HeapManagementMXBeanBackCompatibilityEnabled;

Expand All @@ -193,8 +193,8 @@ class MM_GCExtensions : public MM_GCExtensionsBase {

double maxRAMPercent; /**< Value of -XX:MaxRAMPercentage specified by the user */
double initialRAMPercent; /**< Value of -XX:InitialRAMPercentage specified by the user */
UDATA minimumFreeSizeForSurvivor; /**< minimum free size can be reused by collector as survivor, for balanced GC only */
UDATA freeSizeThresholdForSurvivor; /**< if average freeSize(freeSize/freeCount) of the region is smaller than the Threshold, the region would not be reused by collector as survivor, for balanced GC only */
uintptr_t minimumFreeSizeForSurvivor; /**< minimum free size can be reused by collector as survivor, for balanced GC only */
uintptr_t freeSizeThresholdForSurvivor; /**< if average freeSize(freeSize/freeCount) of the region is smaller than the Threshold, the region would not be reused by collector as survivor, for balanced GC only */
bool recycleRemainders; /**< true if need to recycle TLHRemainders at the end of PGC, for balanced GC only */

enum ContinuationListOption {
Expand All @@ -218,8 +218,16 @@ class MM_GCExtensions : public MM_GCExtensionsBase {
public:
static MM_GCExtensions* newInstance(MM_EnvironmentBase* env);
virtual void kill(MM_EnvironmentBase* env);

void computeDefaultMaxHeapForJava(bool enableOriginalJDK8HeapSizeCompatibilityOption);
/**
* compute the default max heap size for java based on physical memory and other metrics
* Note this function is used to adjust the max heap size during gc initialization
* and to adjust the soft max value during the gc restore initialization in case the
* available memory in the environment reduces
* @param[in] enableOriginalJDK8HeapSizeCompatibilityOption boolean value indicates if
* we enable original heap size compatibility option for JDK under JDK8
* @return the computed max heap size
*/
uintptr_t computeDefaultMaxHeapForJava(bool enableOriginalJDK8HeapSizeCompatibilityOption);

MMINLINE J9HookInterface** getHookInterface() { return J9_HOOK_INTERFACE(hookInterface); };

Expand All @@ -229,27 +237,27 @@ class MM_GCExtensions : public MM_GCExtensionsBase {
*/
MMINLINE MM_StringTable* getStringTable() { return stringTable; }

MMINLINE UDATA getDynamicMaxSoftReferenceAge()
MMINLINE uintptr_t getDynamicMaxSoftReferenceAge()
{
return dynamicMaxSoftReferenceAge;
}

MMINLINE UDATA getMaxSoftReferenceAge()
MMINLINE uintptr_t getMaxSoftReferenceAge()
{
return maxSoftReferenceAge;
}

virtual void identityHashDataAddRange(MM_EnvironmentBase* env, MM_MemorySubSpace* subspace, UDATA size, void* lowAddress, void* highAddress);
virtual void identityHashDataRemoveRange(MM_EnvironmentBase* env, MM_MemorySubSpace* subspace, UDATA size, void* lowAddress, void* highAddress);
virtual void identityHashDataAddRange(MM_EnvironmentBase* env, MM_MemorySubSpace* subspace, uintptr_t size, void* lowAddress, void* highAddress);
virtual void identityHashDataRemoveRange(MM_EnvironmentBase* env, MM_MemorySubSpace* subspace, uintptr_t size, void* lowAddress, void* highAddress);

void updateIdentityHashDataForSaltIndex(UDATA index);
void updateIdentityHashDataForSaltIndex(uintptr_t index);

/**
* Set Tenure address range
* @param base low address of Old subspace range
* @param size size of Old subspace in bytes
*/
virtual void setTenureAddressRange(void* base, UDATA size)
virtual void setTenureAddressRange(void* base, uintptr_t size)
{
_tenureBase = base;
_tenureSize = size;
Expand All @@ -262,15 +270,15 @@ class MM_GCExtensions : public MM_GCExtensionsBase {
GC_OMRVMThreadListIterator omrVMThreadListIterator(_omrVM);
while (OMR_VMThread* walkThread = omrVMThreadListIterator.nextOMRVMThread()) {
walkThread->lowTenureAddress = heapBaseForBarrierRange0;
walkThread->highTenureAddress = (void*)((UDATA)heapBaseForBarrierRange0 + heapSizeForBarrierRange0);
walkThread->highTenureAddress = (void*)((uintptr_t)heapBaseForBarrierRange0 + heapSizeForBarrierRange0);
walkThread->heapBaseForBarrierRange0 = heapBaseForBarrierRange0;
walkThread->heapSizeForBarrierRange0 = heapSizeForBarrierRange0;
}

GC_VMThreadListIterator vmThreadListIterator((J9JavaVM*)_omrVM->_language_vm);
while (J9VMThread* walkThread = vmThreadListIterator.nextVMThread()) {
walkThread->lowTenureAddress = heapBaseForBarrierRange0;
walkThread->highTenureAddress = (void*)((UDATA)heapBaseForBarrierRange0 + heapSizeForBarrierRange0);
walkThread->highTenureAddress = (void*)((uintptr_t)heapBaseForBarrierRange0 + heapSizeForBarrierRange0);
walkThread->heapBaseForBarrierRange0 = heapBaseForBarrierRange0;
walkThread->heapSizeForBarrierRange0 = heapSizeForBarrierRange0;
}
Expand Down
38 changes: 34 additions & 4 deletions runtime/gc_modron_startup/mminit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3178,15 +3178,43 @@ gcReinitializeDefaultsForRestore(J9VMThread* vmThread)
{
MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(vmThread);
J9JavaVM *vm = vmThread->javaVM;
bool result = true;

PORT_ACCESS_FROM_JAVAVM(vm);

OMRPORT_ACCESS_FROM_J9PORT(PORTLIB);
/* Note here we update this parameter which represents the machine physical memory,
* but not the original heap geometry from the snapshot run. We just force the heap
* not to go beyond a certain thereshold calculated based on the new (restore) memory
* availalibity through setting SoftMx.
*/
extensions->usablePhysicalMemory = omrsysinfo_get_addressable_physical_memory();
/* We use false here for computeDefaultMaxHeapForJava(), since the restore
* path is only active for releases after Java 8
*/
uintptr_t candidateSoftMx = extensions->computeDefaultMaxHeapForJava(false);
/* we will set softMx value only if maxHeap calculation returned us a smaller
* value than existing maxHeap or softMx values and a larger value than existing
* minHeap value as inherited from/established at snapshot run, this max/minHeap
* is checked by j9gc_set_softmx()
*/
if (extensions->memoryMax > candidateSoftMx) {
if ((0 == extensions->softMx) || (extensions->softMx > candidateSoftMx)) {
if (extensions->initialMemorySize > candidateSoftMx) {
uintptr_t minimumSizeValue = extensions->initialMemorySize;
const char *qualifier = NULL;
qualifiedSize(&minimumSizeValue, &qualifier);
j9nls_printf(PORTLIB,J9NLS_ERROR,J9NLS_GC_SUBSPACE_TOO_SMALL_FOR_VALUE, "-Xsoftmx", minimumSizeValue, qualifier);
goto _error;
}
else{
extensions->softMx = candidateSoftMx;
}
}
}
extensions->gcThreadCountForced = false;
extensions->parSweepChunkSize = 0;

if (!gcParseReconfigurableArguments(vm, vm->checkpointState.restoreArgsList)) {
result = false;
goto _error;
}

/* If the thread count is being forced, check its validity and display a warning message if it is invalid, then mark it as invalid. */
Expand All @@ -3195,7 +3223,9 @@ gcReinitializeDefaultsForRestore(J9VMThread* vmThread)
extensions->gcThreadCountForced = false;
}

return result;
return true;
_error:
return false;
}
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */

Expand Down
3 changes: 2 additions & 1 deletion runtime/gc_modron_startup/mmparse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1554,7 +1554,8 @@ gcParseCommandLineAndInitializeWithValues(J9JavaVM *vm, IDATA *memoryParameters)
}
}
/* set default max heap for Java */
extensions->computeDefaultMaxHeapForJava(enableOriginalJDK8HeapSizeCompatibilityOption);
extensions->memoryMax = extensions->computeDefaultMaxHeapForJava(enableOriginalJDK8HeapSizeCompatibilityOption);
extensions->maxSizeDefaultMemorySpace = extensions->memoryMax;
}
result = option_set_to_opt(vm, OPT_XMCA, &index, EXACT_MEMORY_MATCH, &vm->ramClassAllocationIncrement);
if (OPTION_OK != result) {
Expand Down

0 comments on commit 36112eb

Please sign in to comment.