diff --git a/src/share/classes/com/alibaba/rcm/Constraint.java b/src/share/classes/com/alibaba/rcm/Constraint.java index 85dd7a0d14cd1d6faf7904ae1939e9a4bc480f48..94531a6fb153c7814941b538749e93800a938652 100644 --- a/src/share/classes/com/alibaba/rcm/Constraint.java +++ b/src/share/classes/com/alibaba/rcm/Constraint.java @@ -47,8 +47,7 @@ public class Constraint { /** * Constraint should be instantiated by {@link ResourceType#newConstraint(long...)} */ - Constraint(ResourceType type, long[] values) { - assert type.validate(values); + protected Constraint(ResourceType type, long[] values) { this.type = type; this.values = values; } diff --git a/src/share/classes/com/alibaba/rcm/ResourceType.java b/src/share/classes/com/alibaba/rcm/ResourceType.java index e0dc45e7281c0574f8f98fa6dd128e9e1b49f07c..c18239bd4e0709c308fbc5736c6067596adf942c 100644 --- a/src/share/classes/com/alibaba/rcm/ResourceType.java +++ b/src/share/classes/com/alibaba/rcm/ResourceType.java @@ -22,9 +22,6 @@ package com.alibaba.rcm; -import java.util.Arrays; -import java.util.stream.IntStream; - /** * Enumeration of {@link Constraint}'s type. *

@@ -54,27 +51,38 @@ public class ResourceType { * The value ranges from 0 to CPU_COUNT * 100. For example, {@code 150} * means that ResourceContainer can use up to 1.5 CPU cores. */ - public final static ResourceType CPU_PERCENT = - new ResourceType("CPU_PERCENT", newChecker(0, Runtime.getRuntime().availableProcessors() * 100)); + public final static ResourceType CPU_PERCENT = new ResourceType("CPU_PERCENT") { + @Override + protected void validate(long... values) throws IllegalArgumentException { + if (values == null || values.length != 1 + || values[0] < 1 + || values[0] > Runtime.getRuntime().availableProcessors() * 100) { + throw new IllegalArgumentException("Bad CPU_PERCENT constraint: " + values[0]); + } + } + }; + /** * Throttling the max heap usage. *

* param #1: maximum heap size in bytes */ - public final static ResourceType HEAP_RETAINED = - new ResourceType("HEAP_RETAINED", newChecker(0, Runtime.getRuntime().maxMemory())); + public final static ResourceType HEAP_RETAINED = new ResourceType("HEAP_RETAINED") { + @Override + protected void validate(long... values) throws IllegalArgumentException { + if (values == null || values.length != 1 + || values[0] <= 0 + || values[0] > Runtime.getRuntime().maxMemory()) { + throw new IllegalArgumentException("Bad HEAP_RETAINED constraint: " + values[0]); + } + } + }; + // name of this ResourceType private final String name; - private final ParameterChecker[] checkers; protected ResourceType(String name) { this.name = name; - this.checkers = null; - } - - protected ResourceType(String name, ParameterChecker... checkers) { - this.name = name; - this.checkers = checkers; } /** @@ -85,11 +93,8 @@ public class ResourceType { * @return newly-created Constraint * @throws IllegalArgumentException when parameter check fails */ - public final Constraint newConstraint(long... values) { - if (!validate(values)) { - throw new IllegalArgumentException(this + - " values: " + Arrays.toString(values)); - } + public Constraint newConstraint(long... values) { + validate(values); return new Constraint(this, values); } @@ -102,49 +107,21 @@ public class ResourceType { *

      * public final static ResourceType MY_RESOURCE =
      *     new ResourceType() {
-     *         protected boolean validate(long[] values) {
+     *         protected void validate(long[] values) throws IllegalArgumentException {
      *              // the check logic
      *         }
      *     };
      * 
* * @param values parameter value - * @return if parameter is validated + * @throws IllegalArgumentException if validation failed */ - protected boolean validate(long[] values) { - if (checkers == null) { - return true; - } - if (checkers.length != values.length) { - return false; - } - return IntStream.range(0, values.length) - .allMatch(i -> checkers[i].check(values[i])); + protected void validate(long... values) throws IllegalArgumentException { + // No check at all! } @Override public String toString() { - return name; - } - - protected static ParameterChecker newChecker(long from, long to) { - return new ParameterChecker(from, to); - } - - /** - * Helper class for parameter range check. - */ - protected static class ParameterChecker { - private final long from; - private final long to; - - protected ParameterChecker(long from, long to) { - this.from = from; - this.to = to; - } - - protected boolean check(long value) { - return value >= from && value < to; - } + return "ResourceType-" + name; } } diff --git a/src/share/classes/com/alibaba/tenant/NativeDispatcher.java b/src/share/classes/com/alibaba/tenant/NativeDispatcher.java index e09f4bcb6b32c78456a5b458a2bef425cc8a76f7..5fa50373eb398d5c3435f0985442785f25fa9fa5 100644 --- a/src/share/classes/com/alibaba/tenant/NativeDispatcher.java +++ b/src/share/classes/com/alibaba/tenant/NativeDispatcher.java @@ -32,9 +32,22 @@ class NativeDispatcher { // Attach the current thread to given {@code tenant} native void attach(TenantContainer tenant); + // Create and initialize native allocation context + native void createTenantAllocationContext(TenantContainer tenant, long heapLimit); + + // Destroy native allocation context + // NOTE: cannot be called directly in finalize() otherwise will lead to hang issue + native void destroyTenantAllocationContext(long allocationContext); + + // Get memory size currently occupied by this allocation context + native long getTenantOccupiedMemory(long allocationContext); + // Gets an array containing the amount of memory allocated on the Java heap for a set of threads (in bytes) native void getThreadsAllocatedMemory(long[] ids, long[] memSizes); + // Gets the TenantContainer object whose memory space contains obj, or null if ROOT tenant container + native TenantContainer containerOf(Object obj); + private static native void registerNatives0(); static { @@ -47,4 +60,4 @@ class NativeDispatcher { }); registerNatives0(); } -} \ No newline at end of file +} diff --git a/src/share/classes/com/alibaba/tenant/TenantConfiguration.java b/src/share/classes/com/alibaba/tenant/TenantConfiguration.java index 71179370729e0c04128573ccc92257ad0730ae54..5992f1b8941dabc712b4f0ea8a2bee5f99d65542 100644 --- a/src/share/classes/com/alibaba/tenant/TenantConfiguration.java +++ b/src/share/classes/com/alibaba/tenant/TenantConfiguration.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.Collection; import com.alibaba.rcm.ResourceType; import com.alibaba.rcm.Constraint; +import static com.alibaba.rcm.ResourceType.*; /** * @@ -70,4 +71,24 @@ public class TenantConfiguration { void setConstraint(Constraint constraint) { constraints.put(constraint.getResourceType(), constraint); } + + /** + * Limit total heap size of new {@code TenantContainer} created from this configuration + * @param maxJavaHeapBytes maximum heap size in byte + * @return current {@code TenantConfiguration} + */ + public TenantConfiguration limitHeap(long maxJavaHeapBytes) { + constraints.put(HEAP_RETAINED, HEAP_RETAINED.newConstraint(maxJavaHeapBytes)); + return this; + } + + /** + * @return the max amount of heap the tenant is allowed to consume. + */ + public long getMaxHeap() { + if (constraints.containsKey(HEAP_RETAINED)) { + return constraints.get(HEAP_RETAINED).getValues()[0]; + } + return Runtime.getRuntime().maxMemory(); + } } diff --git a/src/share/classes/com/alibaba/tenant/TenantContainer.java b/src/share/classes/com/alibaba/tenant/TenantContainer.java index a1309eb0c481ccd20def352aa1710b5a3c4e3f7e..4481b0f78d2f5f512c2316fbe75987633ba1fa85 100644 --- a/src/share/classes/com/alibaba/tenant/TenantContainer.java +++ b/src/share/classes/com/alibaba/tenant/TenantContainer.java @@ -93,6 +93,11 @@ public class TenantContainer { */ private long tenantId; + /* + * address of native tenant allocation context + */ + private long allocationContext = 0L; + /* * tenant name */ @@ -253,6 +258,10 @@ public class TenantContainer { * */ private void cleanUp() { + if (TenantGlobals.isHeapIsolationEnabled()) { + nd.destroyTenantAllocationContext(allocationContext); + } + // clear references spawnedThreads.clear(); attachedThreads.clear(); @@ -274,6 +283,11 @@ public class TenantContainer { props = new Properties(); props.putAll(System.getProperties()); tenantContainerMap.put(this.tenantId, this); + + // Create allocation context if heap isolation enabled + if (TenantGlobals.isHeapIsolationEnabled()) { + nd.createTenantAllocationContext(this, configuration.getMaxHeap()); + } } TenantConfiguration getConfiguration() { @@ -376,6 +390,7 @@ public class TenantContainer { if (null == configuration) { throw new IllegalArgumentException("Failed to create tenant, illegal arguments: configuration is null"); } + TenantContainer tc = new TenantContainer(parent, name, configuration); tenantContainerMap.put(tc.getTenantId(), tc); return tc; @@ -424,6 +439,18 @@ public class TenantContainer { return cpuTime; } + /** + * Gets the heap space occupied by this tenant + * @return heap space occupied by this tenant, 0 if tenant heap isolation is disabled. + * @throws IllegalStateException if -XX:+TenantHeapIsolation is not enabled. + */ + public long getOccupiedMemory() { + if (!TenantGlobals.isHeapIsolationEnabled()) { + throw new IllegalStateException("-XX:+TenantHeapIsolation is not enabled"); + } + return nd.getTenantOccupiedMemory(allocationContext); + } + /** * Runs the code in the target tenant container * @param task the code to run @@ -564,6 +591,19 @@ public class TenantContainer { } } + /** + * Retrieve the tenant container where obj is allocated in + * @param obj object to be searched + * @return TenantContainer object whose memory space contains obj, + * or null if ROOT tenant container + */ + public static TenantContainer containerOf(Object obj) { + if (!TenantGlobals.isHeapIsolationEnabled()) { + throw new UnsupportedOperationException("containerOf() only works with -XX:+TenantHeapIsolation"); + } + return obj != null ? nd.containerOf(obj) : null; + } + /** * Runs {@code Supplier.get} in the root tenant. * @param supplier target used to call diff --git a/src/share/javavm/export/jvm.h b/src/share/javavm/export/jvm.h index 18997baeb1acaff59701a42d17b4c1547bdeb42f..f1a64d966ed7e142352db6191e7a26760eb903e5 100644 --- a/src/share/javavm/export/jvm.h +++ b/src/share/javavm/export/jvm.h @@ -365,7 +365,19 @@ JVM_ElasticHeapGetTotalUncommittedBytes(JNIEnv *env, jclass clazz); * com.alibaba.tenant.TenantContainer */ JNIEXPORT void JNICALL -JVM_AttachToTenant(JNIEnv *env, jobject tenant); +JVM_AttachToTenant(JNIEnv *env, jobject ignored, jobject tenant); + +JNIEXPORT void JNICALL +JVM_CreateTenantAllocationContext(JNIEnv *env, jobject ignored, jobject tenant, jlong heapLimit); + +JNIEXPORT void JNICALL +JVM_DestroyTenantAllocationContext(JNIEnv *env, jobject ignored, jlong context); + +JNIEXPORT jobject JNICALL +JVM_TenantContainerOf(JNIEnv* env, jclass tenantContainerClass, jobject obj); + +JNIEXPORT jlong JNICALL +JVM_GetTenantOccupiedMemory(JNIEnv *env, jobject ignored, jlong context); /* * java.lang.reflect.Array diff --git a/src/share/native/com/alibaba/tenant/NativeDispatcher.c b/src/share/native/com/alibaba/tenant/NativeDispatcher.c index ab5208efce7ca151c93274101d4dfd9c277c05d7..c8b79239bc4ab1299b6e287763196fb702f02cb8 100644 --- a/src/share/native/com/alibaba/tenant/NativeDispatcher.c +++ b/src/share/native/com/alibaba/tenant/NativeDispatcher.c @@ -12,7 +12,11 @@ static const JmmInterface* jmm_interface = NULL; static JNINativeMethod methods[] = { - {"attach", "(" TENANT ")V", (void *)&JVM_AttachToTenant}, + {"attach", "(" TENANT ")V", (void *)&JVM_AttachToTenant}, + {"createTenantAllocationContext", "(" TENANT "J)V", (void *)&JVM_CreateTenantAllocationContext}, + {"destroyTenantAllocationContext", "(J)V", (void *)&JVM_DestroyTenantAllocationContext}, + {"getTenantOccupiedMemory", "(J)J", (void *)&JVM_GetTenantOccupiedMemory}, + {"containerOf", "(Ljava/lang/Object;)"TENANT, (void *)&JVM_TenantContainerOf }, }; JNIEXPORT void JNICALL