提交 d786fdbf 编写于 作者: H Hao Tang 提交者: Hao Tang

[MultiTenant] Support TenantHeapIsolation

Summary: ported heap isolation feature of MultiTenant to Dragonwell8

Test Plan: hotspot/test/multi-tenant/

Reviewed-by: luchsh, mmyxym

Issue: https://github.com/alibaba/dragonwell8/issues/90
上级 f09d14b6
......@@ -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;
}
......
......@@ -22,9 +22,6 @@
package com.alibaba.rcm;
import java.util.Arrays;
import java.util.stream.IntStream;
/**
* Enumeration of {@link Constraint}'s type.
* <p>
......@@ -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;
}
/**
......@@ -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 {
* <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;
}
}
......@@ -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 {
......
......@@ -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();
}
}
......@@ -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 <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
......
......@@ -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
......
......@@ -13,6 +13,10 @@ static const JmmInterface* jmm_interface = NULL;
static JNINativeMethod methods[] = {
{"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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册