diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index ca28cf7d42e8ee23ccd02f9940c7122264f14a87..07f881416c4ab17003c552dcc5edc825cfdc912d 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -1306,7 +1306,12 @@ bool G1CollectedHeap::do_collection(bool explicit_gc, TraceCPUTime tcpu(G1Log::finer(), true, gclog_or_tty); { - GCTraceTime t(GCCauseString("Full GC", gc_cause()), G1Log::fine(), true, NULL, gc_tracer->gc_id()); + GCCauseString cause_string("Full GC", gc_cause()); + if (TenantHeapIsolation && !gc_cause_context().is_system()) { + cause_string.append(FormatBuffer<>(" (tenant-%ld)", + com_alibaba_tenant_TenantContainer::get_tenant_id(gc_cause_context()->tenant_container()))); + } + GCTraceTime t(cause_string, G1Log::fine(), true, NULL, gc_tracer->gc_id()); TraceCollectorStats tcs(g1mm()->full_collection_counters()); TraceMemoryManagerStats tms(true /* fullGC */, gc_cause()); @@ -2613,6 +2618,9 @@ void G1CollectedHeap::collect(GCCause::Cause cause) { } else { // Schedule a Full GC. VM_G1CollectFull op(gc_count_before, full_gc_count_before, cause); + if (TenantHeapIsolation) { + op.set_allocation_context(AllocationContext::current()); + } VMThread::execute(&op); } } @@ -3992,6 +4000,10 @@ void G1CollectedHeap::log_gc_header() { GCCauseString gc_cause_str = GCCauseString("GC pause", gc_cause()) .append(g1_policy()->gcs_are_young() ? "(young)" : "(mixed)") .append(g1_policy()->during_initial_mark_pause() ? " (initial-mark)" : ""); + if (TenantHeapIsolation && !gc_cause_context().is_system()) { + gc_cause_str.append(FormatBuffer<>(" (tenant-%ld)", + com_alibaba_tenant_TenantContainer::get_tenant_id(gc_cause_context()->tenant_container()))); + } gclog_or_tty->print("[%s", (const char*)gc_cause_str); } diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index 1bca8daff4ec37ff0bc00db4530a4b001ac010b2..103879eab60b1042c7a5561b8702e30986d4e353 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -1664,6 +1664,31 @@ public: protected: size_t _max_heap_capacity; + +private: + // Context for on-going GC cause + AllocationContext_t _gc_cause_context; +public: + void set_gc_cause_context(AllocationContext_t context) { _gc_cause_context = context; } + AllocationContext_t gc_cause_context() { return _gc_cause_context; } +}; + +// +class G1ContextCauseSetter : public StackObj { +private: + G1CollectedHeap* _g1h; +public: + G1ContextCauseSetter(G1CollectedHeap* g1h, AllocationContext_t context) + : _g1h(g1h) { + if (TenantHeapIsolation) { + G1CollectedHeap::heap()->set_gc_cause_context(context); + } + } + ~G1ContextCauseSetter() { + if (TenantHeapIsolation) { + G1CollectedHeap::heap()->set_gc_cause_context(AllocationContext::system()); + } + } }; #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1COLLECTEDHEAP_HPP diff --git a/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp b/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp index a5b10b07b5df71d1a755d22b084ef18413998421..c196926cee56b7fec824ae31f130b536962818c7 100644 --- a/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp +++ b/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp @@ -44,6 +44,7 @@ VM_G1CollectForAllocation::VM_G1CollectForAllocation(uint gc_count_before, void VM_G1CollectForAllocation::doit() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); GCCauseSetter x(g1h, _gc_cause); + G1ContextCauseSetter set_ctxt(g1h, this->allocation_context()); _result = g1h->satisfy_failed_allocation(_word_size, allocation_context(), &_pause_succeeded); assert(_result == NULL || _pause_succeeded, @@ -53,6 +54,7 @@ void VM_G1CollectForAllocation::doit() { void VM_G1CollectFull::doit() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); GCCauseSetter x(g1h, _gc_cause); + G1ContextCauseSetter set_ctxt(g1h, this->allocation_context()); g1h->do_full_collection(false /* clear_all_soft_refs */); } @@ -108,6 +110,7 @@ void VM_G1IncCollectionPause::doit() { } GCCauseSetter x(g1h, _gc_cause); + G1ContextCauseSetter set_ctxt(g1h, this->allocation_context()); if (_should_initiate_conc_mark) { // It's safer to read old_marking_cycles_completed() here, given // that noone else will be updating it concurrently. Since we'll diff --git a/src/share/vm/gc_implementation/g1/vm_operations_g1.hpp b/src/share/vm/gc_implementation/g1/vm_operations_g1.hpp index 265eb37d36f6e5320aa0bc790c00afc796d677f0..362f52a4613342f06bdd3a6b7f506abaeceec624 100644 --- a/src/share/vm/gc_implementation/g1/vm_operations_g1.hpp +++ b/src/share/vm/gc_implementation/g1/vm_operations_g1.hpp @@ -53,6 +53,8 @@ public: }; class VM_G1CollectFull: public VM_GC_Operation { +private: + AllocationContext_t _allocation_context; public: VM_G1CollectFull(uint gc_count_before, uint full_gc_count_before, @@ -63,6 +65,8 @@ public: virtual const char* name() const { return "full garbage-first collection"; } + void set_allocation_context(AllocationContext_t context) { _allocation_context = context; } + AllocationContext_t allocation_context() { return _allocation_context; } }; class VM_G1CollectForAllocation: public VM_G1OperationWithAllocRequest { diff --git a/test/multi-tenant/TestTenantGCCause.java b/test/multi-tenant/TestTenantGCCause.java new file mode 100644 index 0000000000000000000000000000000000000000..8fa0c1adbf462cf277ffbe61b5bc2c36b5b1e02d --- /dev/null +++ b/test/multi-tenant/TestTenantGCCause.java @@ -0,0 +1,165 @@ + +/* + * 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 + * @requires os.family == "Linux" + * @requires os.arch == "amd64" + * @summary Test GC cause in GC log for tenant container + * @library /testlibrary + * @build TestTenantGCCause + * @run main/othervm/timeout=120 TestTenantGCCause + */ + +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.ProcessTools; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import static com.oracle.java.testlibrary.Asserts.*; + +public class TestTenantGCCause { + + private static final int K = 1024; + + // used by child process to create a new TenantContainer with given heap limit, + // and do allocation of given amount of bytes inside the TenantContainer; + static class AllocWorker { + // limit of TenantContainer, parsed from args[0]; + private static long tenantLimit; + // total amount of bytes to be allocated, parsed from args[1]; + private static long bytesToAlloc; + + public static void main(String[] args) { + parseArgs(args); + TenantConfiguration config = new TenantConfiguration(); + config.limitHeap(tenantLimit); + TenantContainer tenant = TenantContainer.create(config); + try { + tenant.run(()->{ + List refs = new LinkedList<>(); // keep objects alive + long totalAllocated = 0; + while (totalAllocated < bytesToAlloc) { + byte[] obj = new byte[4 * K]; + refs.add(obj); + totalAllocated += (obj.length + 16); + } + }); + } catch (TenantException e) { + e.printStackTrace(); + fail(); + } + } + + // parse size, can be '1234', '1K', '4M' + private static void parseArgs(String[] args) { + assertEquals(args.length, 2); + tenantLimit = parseSize(args[0]); + assertGreaterThan(tenantLimit, 0L); + bytesToAlloc = parseSize(args[1]); + } + + private static final Pattern UNIT_PATTERN = Pattern.compile("(\\d+)([MmKkGg]{0,1})"); + + private static long parseSize(String s) { + Matcher m = UNIT_PATTERN.matcher(s); + long size = 0; + if (m.matches()) { + size = Long.parseLong(m.group(1)); + if (m.groupCount() == 2) { + switch (m.group(2).toLowerCase()) { + case "k": + return (size * K); + case "m": + return (size * K * K); + case "g": + return (size * K * K * K); + default: + fail(); + } + } + } + throw new IllegalArgumentException("Bad size format: " + s); + } + } + + private void testYoungGCCause() { + try { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+MultiTenant", + "-XX:+TenantHeapThrottling", + "-XX:+UseG1GC", + "-Xmx1g", + "-Xms1g", + "-XX:G1HeapRegionSize=2m", + "-XX:+PrintGCDetails", + AllocWorker.class.getName(), + "4m", + "8m"); + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + assertTrue(out.asLines().stream() + .sequential() + .peek(System.out::println) + .filter(l -> l.contains("[GC pause")) + .anyMatch(l -> l.contains("(tenant-0)"))); + assertNotEquals(out.getExitValue(), 0); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + + private void testFullGCCause() { + try { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+MultiTenant", + "-XX:+TenantHeapThrottling", + "-XX:+UseG1GC", + "-Xmx1g", + "-Xms1g", + "-XX:G1HeapRegionSize=2m", + "-XX:+PrintGCDetails", + AllocWorker.class.getName(), + "4m", + "8m"); + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + assertTrue(out.asLines().stream() + .sequential() + .peek(System.out::println) + .filter(l -> l.contains("[Full GC")) + .anyMatch(l -> l.contains("(tenant-0)"))); + assertNotEquals(out.getExitValue(), 0); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + + public static void main(String[] args) { + TestTenantGCCause test = new TestTenantGCCause(); + test.testYoungGCCause(); + test.testFullGCCause(); + } +}