Skip to content

Commit

Permalink
[MultiTenant] Support TenantHeapIsolation
Browse files Browse the repository at this point in the history
Summary: ported heap isolation feature of MultiTenant to Dragonwell8

Test Plan: hotspot/test/multi-tenant/{TestHeapIsolation.java,TestMultiTenantOptionDeps.sh,TestPerTenantTLAB.java}

Reviewed-by: luchsh, mmyxym

Issue: dragonwell-project/dragonwell8#90
  • Loading branch information
Hao Tang committed Apr 3, 2020
1 parent f09d14b commit d301d8a
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 57 deletions.
3 changes: 1 addition & 2 deletions src/share/classes/com/alibaba/rcm/Constraint.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
81 changes: 29 additions & 52 deletions src/share/classes/com/alibaba/rcm/ResourceType.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@

package com.alibaba.rcm;

import java.util.Arrays;
import java.util.stream.IntStream;

/**
* Enumeration of {@link Constraint}'s type.
* <p>
Expand Down Expand Up @@ -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.
* <p>
* 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;
}

/**
Expand All @@ -85,11 +93,8 @@ protected ResourceType(String name, ParameterChecker... checkers) {
* @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);
}

Expand All @@ -102,49 +107,21 @@ public final Constraint newConstraint(long... values) {
* <pre>
* public final static ResourceType MY_RESOURCE =
* new ResourceType() {
* protected boolean validate(long[] values) {
* protected void validate(long[] values) throws IllegalArgumentException {
* // the check logic
* }
* };
* </pre>
*
* @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;
}
}
15 changes: 14 additions & 1 deletion src/share/classes/com/alibaba/tenant/NativeDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>obj</code>, or null if ROOT tenant container
native TenantContainer containerOf(Object obj);

private static native void registerNatives0();

static {
Expand All @@ -47,4 +60,4 @@ public Void run() {
});
registerNatives0();
}
}
}
21 changes: 21 additions & 0 deletions src/share/classes/com/alibaba/tenant/TenantConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Collection;
import com.alibaba.rcm.ResourceType;
import com.alibaba.rcm.Constraint;
import static com.alibaba.rcm.ResourceType.*;

/**
*
Expand Down Expand Up @@ -70,4 +71,24 @@ Collection<Constraint> getAllConstraints() {
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();
}
}
40 changes: 40 additions & 0 deletions src/share/classes/com/alibaba/tenant/TenantContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ public class TenantContainer {
*/
private long tenantId;

/*
* address of native tenant allocation context
*/
private long allocationContext = 0L;

/*
* tenant name
*/
Expand Down Expand Up @@ -253,6 +258,10 @@ public void destroy() {
*
*/
private void cleanUp() {
if (TenantGlobals.isHeapIsolationEnabled()) {
nd.destroyTenantAllocationContext(allocationContext);
}

// clear references
spawnedThreads.clear();
attachedThreads.clear();
Expand All @@ -274,6 +283,11 @@ private TenantContainer(TenantContainer parent, String name, TenantConfiguration
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() {
Expand Down Expand Up @@ -376,6 +390,7 @@ public static TenantContainer create(TenantContainer parent, String name, Tenant
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;
Expand Down Expand Up @@ -424,6 +439,18 @@ public long getProcessCpuTime() {
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
Expand Down Expand Up @@ -564,6 +591,19 @@ private static void initializeTenantContainerClass() {
}
}

/**
* Retrieve the tenant container where <code>obj</code> is allocated in
* @param obj object to be searched
* @return TenantContainer object whose memory space contains <code>obj</code>,
* 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
Expand Down
14 changes: 13 additions & 1 deletion src/share/javavm/export/jvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 5 additions & 1 deletion src/share/native/com/alibaba/tenant/NativeDispatcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit d301d8a

Please sign in to comment.