提交 70a7d2cc 编写于 作者: H Hao Tang 提交者: Jonathan Lu

[MultiTenant] Support TenantHeapThrottling

Summary: ported heap throttling feature of MultiTenant to Dragonwell8

Test Plan: hotspot/test/multi-tenant/

Reviewed-by: luchsh, mmyxym

Issue: https://github.com/alibaba/dragonwell8/issues/91
上级 8e20eb99
......@@ -178,8 +178,8 @@ void G1AllocRegion::update_alloc_region(HeapRegion* alloc_region) {
assert(alloc_region != NULL && !alloc_region->is_empty(),
ar_ext_msg(this, "pre-condition"));
alloc_region->set_allocation_context(allocation_context());
_alloc_region = alloc_region;
_alloc_region->set_allocation_context(allocation_context());
_count += 1;
trace("updated");
}
......@@ -243,6 +243,12 @@ G1AllocRegion::G1AllocRegion(const char* name,
HeapRegion* MutatorAllocRegion::allocate_new_region(size_t word_size,
bool force) {
// if it is about to exceed tenant heap limit, fail current request directly
if (TenantHeapThrottling && !allocation_context().is_system()
&& !allocation_context()->can_allocate(word_size)) {
return NULL;
}
return _g1h->new_mutator_alloc_region(word_size, force);
}
......
......@@ -746,6 +746,12 @@ HeapWord* G1CollectedHeap::humongous_obj_allocate(size_t word_size, AllocationCo
verify_region_sets_optional();
// early return if exceeds tenant limit
if (TenantHeapThrottling && !context.is_system()
&& !context->can_allocate(word_size)) {
return NULL;
}
uint first = G1_NO_HRM_INDEX;
uint obj_regions = (uint)(align_size_up_(word_size, HeapRegion::GrainWords) / HeapRegion::GrainWords);
......
......@@ -141,6 +141,26 @@ G1TenantAllocationContext::~G1TenantAllocationContext() {
VMThread::execute(&vm_op);
}
bool G1TenantAllocationContext::can_allocate(size_t attempt_word_size) {
assert(TenantHeapThrottling, "pre-condition");
assert_heap_locked_or_at_safepoint(true /* should_be_vm_thread */);
// always allow allocation
if (heap_size_limit() == TENANT_HEAP_NO_LIMIT) {
return true;
}
size_t occupied_regions = occupied_heap_region_count();
if (_g1h->isHumongous(attempt_word_size)) {
// reach here only from G1CollectedHeap::humongous_obj_allocate
occupied_regions += heap_words_to_region_num(attempt_word_size);
} else {
// reach here only from MutatorAllocRegion::allocate_new_region
occupied_regions++;
}
return occupied_regions <= heap_region_limit();
}
void G1TenantAllocationContext::inc_occupied_heap_region_count() {
assert(TenantHeapIsolation && occupied_heap_region_count() >= 0, "pre-condition");
assert(Heap_lock->owned_by_self() || SafepointSynchronize::is_at_safepoint(), "not locked");
......@@ -163,6 +183,14 @@ G1TenantAllocationContext* G1TenantAllocationContext::current() {
return thrd->allocation_context().tenant_allocation_context();
}
void G1TenantAllocationContext::set_heap_size_limit(size_t new_size) {
assert(TenantHeapThrottling, "pre-condition");
assert(new_size != TENANT_HEAP_NO_LIMIT, "sanity");
_heap_size_limit = new_size;
_heap_region_limit = heap_bytes_to_region_num(_heap_size_limit);
}
size_t G1TenantAllocationContext::heap_bytes_to_region_num(size_t size_in_bytes) {
return heap_words_to_region_num(size_in_bytes >> LogBytesPerWord);
}
......
......@@ -103,6 +103,25 @@ public:
void inc_occupied_heap_region_count();
void dec_occupied_heap_region_count();
size_t occupied_heap_region_count() { return _occupied_heap_region_count; }
//
// Check if next allocation request can be satisfied
// Called only before trying to allocate new regions, including:
// 1, allocate one new eden region for non-humongous object
// 2, allocate continuous regions for humongous oject
//
// NOTE: must be called at safepoint or after grabbing Heap_lock
//
// can_allocate() works safely with inc/dec_occupied_heap_region_count() because they are
// protected by locks depending on different scenarios.
// 1, Outside safepoint, Heap_lock is used to provide exclusive access.
// 2, Inside safepoint, there may be three stages:
// 2.1, before CGC operation starts, the only entrance is G1CollectedHeap::attempt_allocation_at_safepoint()
// which is single-threaded (VMThread), no concurrency issue;
// 2.2, During CGC operation, inc/dec_occupied_heap_region_count() methods are guarded
// by FreeList_lock among different GC worker threads;
// 2.3, after CGC operation but before safepoint ends, same as 2.1;
//
bool can_allocate(size_t attempt_word_size);
// record the compact dest
const CachedCompactPoint& cached_compact_point() const { return _ccp; }
......
......@@ -3901,6 +3901,14 @@ JVM_ENTRY(void, JVM_CreateTenantAllocationContext(JNIEnv *env, jobject ignored,
oop tenant_obj = JNIHandles::resolve_non_null(tenant);
assert(tenant_obj != NULL, "Cannot create allocation context for a null tenant container");
G1CollectedHeap::heap()->create_tenant_allocation_context(tenant_obj);
// set up heap limit if TenantHeapThrottling enabled
if (TenantHeapThrottling) {
assert(heapLimit > 0, "Bad heap limit");
G1TenantAllocationContext* context = com_alibaba_tenant_TenantContainer::get_tenant_allocation_context(tenant_obj);
assert(context != NULL && context->heap_size_limit() == TENANT_HEAP_NO_LIMIT, "sanity");
context->set_heap_size_limit((size_t)heapLimit);
}
JVM_END
// This method should be called before reclaiming of Java TenantContainer object
......
......@@ -28,6 +28,7 @@
*/
#define TENANT_FLAG_MULTI_TENANT_ENABLED (0x1) // bit 0 to indicate if the tenant feature is enabled.
#define TENANT_FLAG_HEAP_THROTTLING_ENABLED (0x2) // bit 1 to indicate if heap throttling feature is enabled.
#define TENANT_FLAG_HEAP_ISOLATION_ENABLED (0x80) // bit 7 to indicate if heap isolation feature is enabled.
static jint tenant_GetTenantFlags(TenantEnv *env, jclass cls);
......@@ -50,6 +51,10 @@ tenant_GetTenantFlags(TenantEnv *env, jclass cls)
result |= TENANT_FLAG_MULTI_TENANT_ENABLED;
}
if (TenantHeapThrottling) {
result |= TENANT_FLAG_HEAP_THROTTLING_ENABLED;
}
if (TenantHeapIsolation) {
result |= TENANT_FLAG_HEAP_ISOLATION_ENABLED;
}
......
......@@ -24,6 +24,18 @@
#include "runtime/java.hpp"
void ArgumentsExt::set_tenant_flags() {
// order is critical here, please be careful
// TenantHeapThrottling directly depends on TenantHeapIsolation
if (TenantHeapThrottling) {
if (FLAG_IS_DEFAULT(TenantHeapIsolation)) {
FLAG_SET_ERGO(bool, TenantHeapIsolation, true);
}
if (!TenantHeapIsolation) {
vm_exit_during_initialization("Invalid combination of -XX:+TenantHeapThrottling and -XX:-TenantHeapIsolation");
}
}
// TenantHeapIsolation directly depends on MultiTenant, UseG1GC
if (TenantHeapIsolation) {
if (FLAG_IS_DEFAULT(MultiTenant)) {
......
......@@ -102,6 +102,9 @@
\
product(bool, UsePerTenantTLAB, false, \
"Mutator may maintain multiple TLABs for each of the tenants") \
\
product(bool, TenantHeapThrottling, false, \
"Enable heap throttling per tenant") \
//add new AJVM specific flags here
......
......@@ -40,6 +40,7 @@ const char* NMTUtil::_memory_type_names[] = {
"Arena Chunk",
"Test",
"Tracing",
"Tenant",
"Unknown"
};
......
......@@ -25,6 +25,8 @@
* @summary Test TenantContainer.containOf() to retrieve tenant container of a Java object
* @library /testlibrary
* @build TestContainerOf
* @run main/othervm -XX:+MultiTenant -XX:+TenantHeapThrottling -XX:+UseG1GC -Xmx1024M -Xms512M
* -XX:G1HeapRegionSize=1M TestContainerOf
* @run main/othervm -XX:+MultiTenant -XX:+TenantHeapIsolation -XX:+UseG1GC -Xmx1024M -Xms512M
* -XX:G1HeapRegionSize=1M TestContainerOf
*/
......
/*
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Alibaba designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*
* @test
* @summary Test heap limit on tenant
* @library /testlibrary /testlibrary/whitebox
* @build TestTenantHeapLimit
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+MultiTenant -XX:+TenantHeapThrottling -XX:+WhiteBoxAPI -XX:+UseG1GC -Xmx1024M -Xms512M -XX:G1HeapRegionSize=1M TestTenantHeapLimit
*/
import static com.oracle.java.testlibrary.Asserts.*;
import com.alibaba.tenant.TenantConfiguration;
import com.alibaba.tenant.TenantContainer;
import com.alibaba.tenant.TenantException;
import com.oracle.java.testlibrary.OutputAnalyzer;
import com.oracle.java.testlibrary.Platform;
import com.oracle.java.testlibrary.ProcessTools;
import sun.hotspot.WhiteBox;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class TestTenantHeapLimit {
private static final WhiteBox wb = WhiteBox.getWhiteBox();
private static final int HEAP_REGION_SIZE = wb.g1RegionSize();
// convenient sizing constants
private static final int K = 1024;
private static final int M = K * 1024;
private static final int G = M * 1024;
// object header size
private static final int objHeaderSize = Platform.is64bit() ? 16 : 8;
public static void main(String[] args) throws Exception {
testExpectedOOM(new TenantConfiguration().limitHeap(HEAP_REGION_SIZE << 9));
testExpectedOOM(new TenantConfiguration().limitHeap(HEAP_REGION_SIZE << 7));
testExpectedOOM(new TenantConfiguration().limitHeap(HEAP_REGION_SIZE << 4));
testExpectedOOM(new TenantConfiguration().limitHeap(HEAP_REGION_SIZE << 1));
testThrowAwayGarbage(new TenantConfiguration().limitHeap(HEAP_REGION_SIZE << 9), 5000L /* ms */);
testThrowAwayGarbage(new TenantConfiguration().limitHeap(HEAP_REGION_SIZE << 7), 5000L /* ms */);
testThrowAwayGarbage(new TenantConfiguration().limitHeap(HEAP_REGION_SIZE << 4), 5000L /* ms */);
testThrowAwayGarbage(new TenantConfiguration().limitHeap(HEAP_REGION_SIZE << 1), 5000L /* ms */);
testSmallTenantHeapLimit();
testHumongousLimit();
}
private static void testHumongousLimit() {
TenantConfiguration config = new TenantConfiguration().limitHeap(HEAP_REGION_SIZE * 100);
TenantContainer tenant = TenantContainer.create(config);
System.gc();
try {
tenant.run(()->{
try {
// create a humongous object whose size > tenant heap limit
Object obj = new byte[(int) (config.getMaxHeap() * 2)];
// should throw OOM
fail();
} catch (OutOfMemoryError oom) {
// expected!
} catch (Throwable e) {
System.err.println("Unexpected exception!");
e.printStackTrace();
fail();
}
});
} catch (TenantException e) {
fail();
}
}
//
// create a tenant which has heap limit far less than current young gen size,
// after allocating object across tenant limit, YGC should be triggered.
//
private static void testSmallTenantHeapLimit() {
OutputAnalyzer output = null;
try {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-XX:+UseG1GC",
"-Xmx1024m",
"-Xms1024m",
"-XX:G1HeapRegionSize=1M",
"-XX:+PrintGCDetails",
"-Xbootclasspath/a:.",
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"-XX:+PrintGC",
"-XX:+MultiTenant",
"-XX:+TenantHeapThrottling",
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+UnlockExperimentalVMOptions",
"-XX:G1NewSizePercent=50", /* young gen: 512MB ~= 1024MB * 50% */
HeapConsumer.class.getName());
output = ProcessTools.executeProcess(pb);
// expect normal exit, no OOM error
assertEquals(output.getExitValue(), 0);
// should not have full GC
assertFalse(output.getStdout().contains("[Full GC (Allocation Failure)"));
// must have triggerred YGC
assertTrue(output.getStdout().contains("[GC pause (G1 Evacuation Pause) (young)"));
} catch (Exception e) {
e.printStackTrace();
fail();
} finally {
if (output != null) {
System.out.println("STDOUT of child process:");
System.out.println(output.getStdout());
System.out.println("STDOUT END");
System.err.println("STDERR of child process:");
System.err.println(output.getStderr());
System.err.println("STDERR END");
}
}
}
public static class HeapConsumer {
public static void main(String[] args) {
// tenant heap limit is 40M, much smaller than young gen capacity 512M.
// so GC should be triggered right after reaching tenant heap limit.
TenantConfiguration config = new TenantConfiguration().limitHeap(40 * M);
TenantContainer tenant = TenantContainer.create(config);
try {
tenant.run(()-> {
int allocationGrain = 64; /*bytes per allocation*/
long unitSize = allocationGrain + objHeaderSize;
// to keep track of total allocated object size
long totalAllocatedBytes = 0;
// total limit is far more large that tenant limit
long allocateLimit = 200 * M;
// keep liveReferencePercent of tenant limit as strongly reachable
float liveReferencePercent = 0.5f;
int slots = (int) (config.getMaxHeap() / unitSize * liveReferencePercent);
// strong references to prevent some of the object from being GCed
List<Object> holder = new ArrayList<>(slots);
int nextIndex = 0;
// allocate objects
while (totalAllocatedBytes < allocateLimit) {
// allocate object
byte[] arr = new byte[allocationGrain];
// build necessary references
totalAllocatedBytes += (arr.length);
if (holder.size() < slots) {
holder.add(arr);
} else {
holder.set((nextIndex++) % slots, arr);
}
}
System.out.println("finished allocation! total " + totalAllocatedBytes + " bytes");
});
} catch (TenantException e) {
e.printStackTrace();
fail();
} finally {
tenant.destroy();
}
}
}
private static void testThrowAwayGarbage(TenantConfiguration config, long millis) {
System.out.println("Start testThrowAwayGarbage with heapLimit=" + config.getMaxHeap() + "bytes");
final AtomicBoolean shouldEnd = new AtomicBoolean(false);
// below well-behaved task never holds memory exceeds rebel heap limit
Thread conformist = new Thread() {
@Override
public void run() {
TenantContainer slave = TenantContainer.create(config);
try {
slave.run(() -> {
while (!shouldEnd.get()) {
// allocate and throw away
Object o = new byte[4];
o = new byte[HEAP_REGION_SIZE >> 2];
}
});
} catch (TenantException e) {
fail();
} finally {
slave.destroy();
}
}
};
conformist.start();
// wait for the conformist to end
try {
Thread.sleep(millis);
shouldEnd.set(true);
conformist.join();
} catch (InterruptedException e) {
fail();
}
System.out.println("testThrowAwayGarbage finished normally");
}
private static void testExpectedOOM(TenantConfiguration config) {
// below is a bad tenant task, which will hold memory more that limit,
// thus should throw OOM
System.out.println("Start testExpectedOOM with heapLimit=" + config.getMaxHeap() + "bytes");
TenantContainer rebel = TenantContainer.create(config);
try {
rebel.run(() -> {
long total = 0, limit = (config.getMaxHeap() << 1);
int unit = (HEAP_REGION_SIZE >> 3);
List<Object> refs = new ArrayList();
while (total < limit) {
refs.add(new byte[unit]);
total += unit;
}
});
fail();
} catch (TenantException e) {
fail();
} catch (OutOfMemoryError oom) {
// expected!
System.out.println("Expected OOM!");
} catch (Throwable t) {
// for other exception, just error
fail();
} finally {
rebel.destroy();
}
System.out.println("testExpectedOOM finished normally");
}
private static void fail() {
assertTrue(false, "Failed!");
}
}
......@@ -21,9 +21,9 @@
#
#
# @test TestMultiTenantOptionDeps
# @summary Test the dependencies of multi-tenant options
# @run shell TestMultiTenantOptionDeps.sh
# @test TestTenantJVMOptions
# @summary Test the dependencies of multi-tenant JVM options
# @run shell TestTenantJVMOptions.sh
#
if [ "${TESTSRC}" = "" ]
......@@ -58,6 +58,11 @@ function check_dependency_bool_bool_false() {
fi
}
check_dependency_bool_bool '-XX:+UseG1GC -XX:+TenantHeapIsolation' '-XX:+MultiTenant'
check_dependency_bool_bool '-XX:+UseG1GC -XX:+TenantHeapThrottling' '-XX:+MultiTenant'
check_dependency_bool_bool '-XX:+UseG1GC -XX:+TenantHeapThrottling' '-XX:+TenantHeapIsolation'
check_dependency_bool_bool '-XX:+UseG1GC -XX:+TenantHeapIsolation -XX:+UseTLAB' '-XX:+UsePerTenantTLAB'
# check if provided jvm arguments is invalid
function assert_invalid_jvm_options() {
JVM_ARGS=$1
......@@ -69,7 +74,10 @@ function assert_invalid_jvm_options() {
fi
}
check_dependency_bool_bool '-XX:+UseG1GC -XX:+TenantHeapIsolation' '-XX:+MultiTenant'
assert_invalid_jvm_options '-XX:+TenantHeapIsolation'
assert_invalid_jvm_options '-XX:+TenantHeapIsolation -XX:+UseConcMarkSweepGC'
assert_invalid_jvm_options '-XX:+TenantHeapThrottling'
assert_invalid_jvm_options '-XX:+UseG1GC -XX:+TenantHeapIsolation -XX:-MultiTenant'
assert_invalid_jvm_options '-XX:+UseG1GC -XX:+TenantHeapThrottling -XX:-TenantHeapIsolation'
assert_invalid_jvm_options '-XX:+UseG1GC -XX:+UsePerTenantTLAB -XX:+TenantHeapThrottling -XX:-UseTLAB'
assert_invalid_jvm_options '-XX:+UseG1GC -XX:+UsePerTenantTLAB -XX:+UseTLAB -XX:-TenantHeapThrottling'
/*
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Alibaba designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*
* @test
* @summary TestTenantMemoryMTSafe
* @library /testlibrary /testlibrary/whitebox
* @build TestTenantMemoryMTSafe sun.hotspot.WhiteBox
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
* -XX:+MultiTenant -XX:+TenantHeapThrottling -XX:+WhiteBoxAPI -XX:+UseG1GC -Xmx1g -Xms1g
* -XX:G1HeapRegionSize=1M -XX:+PrintGCDetails -XX:+PrintGCDateStamps TestTenantMemoryMTSafe
*/
import static com.oracle.java.testlibrary.Asserts.*;
import com.alibaba.tenant.TenantConfiguration;
import com.alibaba.tenant.TenantContainer;
import com.alibaba.tenant.TenantException;
import com.oracle.java.testlibrary.OutputAnalyzer;
import com.oracle.java.testlibrary.Platform;
import com.oracle.java.testlibrary.ProcessTools;
import sun.hotspot.WhiteBox;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class TestTenantMemoryMTSafe {
private static final WhiteBox WB = WhiteBox.getWhiteBox();
private static final int HEAP_REGION_SIZE = WB.g1RegionSize();
public static void main(String[] args) throws Exception {
TenantConfiguration config = new TenantConfiguration().limitHeap(HEAP_REGION_SIZE * 1024);
TenantContainer tenant = TenantContainer.create(config);
System.gc();
try {
tenant.run(()->{
try {
Object[] array = new Object[3000];
for (int i = 0; i < 3000; i++) {
array[i] = new byte[100*1024];
}
WB.fullGC();
for (int i = 0; i < 3000; i++) {
array[i] = null;
}
WB.g1StartConcMarkCycle();
while (WB.g1InConcurrentMark()) {
Thread.sleep(100);
}
WB.youngGC();
WB.youngGC();
WB.youngGC();
WB.youngGC();
WB.youngGC();
assertTrue(tenant.getOccupiedMemory() < 1024*1024*5,"Must be less than 5m");
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
fail();
}
});
} catch (TenantException e) {
fail();
}
}
private static void fail() {
assertTrue(false, "Failed!");
}
}
/*
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Alibaba designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/*
* @test
* @summary Verify PrintNMTStatistics with TenantHeapThrottling enabled
* @library /testlibrary
*/
import com.alibaba.tenant.TenantConfiguration;
import com.alibaba.tenant.TenantContainer;
import com.oracle.java.testlibrary.*;
public class TestTenantNMTSupport {
public static void main(String args[]) throws Exception {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+IgnoreUnrecognizedVMOptions",
"-XX:+MultiTenant",
"-XX:+TenantHeapThrottling",
"-XX:+UseG1GC",
"-XX:+PrintNMTStatistics",
"-XX:NativeMemoryTracking=detail",
TestingTenant.class.getName());
OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
output_detail.shouldHaveExitValue(0);
output_detail.shouldContain("Virtual memory map:");
output_detail.shouldContain("Details:");
output_detail.shouldContain("Tenant (reserved=");
output_detail.shouldContain("G1TenantAllocationContexts::initialize");
output_detail.shouldNotContain("error");
}
static class TestingTenant {
public static void main(String[] args) throws Exception{
TenantConfiguration config = new TenantConfiguration().limitHeap(32 * 1024 * 1024);
TenantContainer tenant = TenantContainer.create(config);
tenant.run(() -> System.out.println("Hello"));
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册