--- old/src/share/vm/gc_implementation/g1/concurrentMark.cpp 2014-08-05 10:05:54.140559777 +0200 +++ new/src/share/vm/gc_implementation/g1/concurrentMark.cpp 2014-08-05 10:05:54.028559782 +0200 @@ -2167,7 +2167,9 @@ g1h->increment_total_collections(); // Clean out dead classes and update Metaspace sizes. - ClassLoaderDataGraph::purge(); + if (ClassUnloadingWithConcurrentMark) { + ClassLoaderDataGraph::purge(); + } MetaspaceGC::compute_new_size(); // We reclaimed old regions so we should calculate the sizes to make @@ -2597,24 +2599,27 @@ assert(_markStack.isEmpty(), "Marking should have completed"); // Unload Klasses, String, Symbols, Code Cache, etc. + { + G1RemarkGCTraceTime trace("Unloading", G1Log::finer()); - G1RemarkGCTraceTime trace("Unloading", G1Log::finer()); - - bool purged_classes; + if (ClassUnloadingWithConcurrentMark) { + bool purged_classes; - { - G1RemarkGCTraceTime trace("System Dictionary Unloading", G1Log::finest()); - purged_classes = SystemDictionary::do_unloading(&g1_is_alive); - } + { + G1RemarkGCTraceTime trace("System Dictionary Unloading", G1Log::finest()); + purged_classes = SystemDictionary::do_unloading(&g1_is_alive); + } - { - G1RemarkGCTraceTime trace("Parallel Unloading", G1Log::finest()); - weakRefsWorkParallelPart(&g1_is_alive, purged_classes); - } + { + G1RemarkGCTraceTime trace("Parallel Unloading", G1Log::finest()); + weakRefsWorkParallelPart(&g1_is_alive, purged_classes); + } + } - if (G1StringDedup::is_enabled()) { - G1RemarkGCTraceTime trace("String Deduplication Unlink", G1Log::finest()); - G1StringDedup::unlink(&g1_is_alive); + if (G1StringDedup::is_enabled()) { + G1RemarkGCTraceTime trace("String Deduplication Unlink", G1Log::finest()); + G1StringDedup::unlink(&g1_is_alive); + } } } --- old/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp 2014-08-05 10:05:54.612559758 +0200 +++ new/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp 2014-08-05 10:05:54.492559763 +0200 @@ -4920,10 +4920,15 @@ if (_g1h->g1_policy()->during_initial_mark_pause()) { // We also need to mark copied objects. strong_root_cl = &scan_mark_root_cl; - weak_root_cl = &scan_mark_weak_root_cl; strong_cld_cl = &scan_mark_cld_cl; - weak_cld_cl = &scan_mark_weak_cld_cl; strong_code_cl = &scan_mark_code_cl; + if (ClassUnloadingWithConcurrentMark) { + weak_root_cl = &scan_mark_weak_root_cl; + weak_cld_cl = &scan_mark_weak_cld_cl; + } else { + weak_root_cl = &scan_mark_root_cl; + weak_cld_cl = &scan_mark_cld_cl; + } } else { strong_root_cl = &scan_only_root_cl; weak_root_cl = &scan_only_root_cl; @@ -4994,6 +4999,7 @@ double closure_app_time_sec = 0.0; bool during_im = _g1h->g1_policy()->during_initial_mark_pause(); + bool trace_metadata = during_im && ClassUnloadingWithConcurrentMark; BufferingOopClosure buf_scan_non_heap_roots(scan_non_heap_roots); BufferingOopClosure buf_scan_non_heap_weak_roots(scan_non_heap_weak_roots); @@ -5003,8 +5009,8 @@ &buf_scan_non_heap_roots, &buf_scan_non_heap_weak_roots, scan_strong_clds, - // Initial Mark handles the weak CLDs separately. - (during_im ? NULL : scan_weak_clds), + // Unloading Initial Marks handle the weak CLDs separately. + (trace_metadata ? NULL : scan_weak_clds), scan_strong_code); // Now the CM ref_processor roots. @@ -5016,7 +5022,7 @@ ref_processor_cm()->weak_oops_do(&buf_scan_non_heap_roots); } - if (during_im) { + if (trace_metadata) { // Barrier to make sure all workers passed // the strong CLD and strong nmethods phases. active_strong_roots_scope()->wait_until_all_workers_done_with_threads(n_par_threads()); @@ -6042,6 +6048,7 @@ { StrongRootsScope srs(this); // InitialMark needs claim bits to keep track of the marked-through CLDs. + // FIXME: Needed? if (g1_policy()->during_initial_mark_pause()) { ClassLoaderDataGraph::clear_claimed_marks(); } --- old/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp 2014-08-05 10:05:55.080559738 +0200 +++ new/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp 2014-08-05 10:05:54.968559743 +0200 @@ -94,26 +94,37 @@ inline bool HeapRegion::block_is_obj(const HeapWord* p) const { G1CollectedHeap* g1h = G1CollectedHeap::heap(); - return !g1h->is_obj_dead(oop(p), this); + if (ClassUnloadingWithConcurrentMark) { + return !g1h->is_obj_dead(oop(p), this); + } + return p < top(); } inline size_t HeapRegion::block_size(const HeapWord *addr) const { + if (addr == top()) { + return pointer_delta(end(), addr); + } + + if (block_is_obj(addr)) { + return oop(addr)->size(); + } + + assert(ClassUnloadingWithConcurrentMark, + err_msg("All blocks should be objects if G1 Class Unloading isn't used. " + "HR: ["PTR_FORMAT", "PTR_FORMAT", "PTR_FORMAT") " + "addr: " PTR_FORMAT, + p2i(bottom()), p2i(top()), p2i(end()), p2i(addr))); + // Old regions' dead objects may have dead classes // We need to find the next live object in some other // manner than getting the oop size G1CollectedHeap* g1h = G1CollectedHeap::heap(); - if (g1h->is_obj_dead(oop(addr), this)) { - HeapWord* next = g1h->concurrent_mark()->prevMarkBitMap()-> - getNextMarkedWordAddress(addr, prev_top_at_mark_start()); - - assert(next > addr, "must get the next live object"); + HeapWord* next = g1h->concurrent_mark()->prevMarkBitMap()-> + getNextMarkedWordAddress(addr, prev_top_at_mark_start()); - return pointer_delta(next, addr); - } else if (addr == top()) { - return pointer_delta(end(), addr); - } - return oop(addr)->size(); + assert(next > addr, "must get the next live object"); + return pointer_delta(next, addr); } inline HeapWord* HeapRegion::par_allocate_no_bot_updates(size_t word_size) { --- old/src/share/vm/gc_implementation/shared/vmGCOperations.cpp 2014-08-05 10:05:55.484559721 +0200 +++ new/src/share/vm/gc_implementation/shared/vmGCOperations.cpp 2014-08-05 10:05:55.372559726 +0200 @@ -195,6 +195,7 @@ gch->do_full_collection(gch->must_clear_all_soft_refs(), _max_level); } +// Returns true iff concurrent GCs unloads metadata. bool VM_CollectForMetadataAllocation::initiate_concurrent_GC() { #if INCLUDE_ALL_GCS if (UseConcMarkSweepGC && CMSClassUnloadingEnabled) { @@ -202,7 +203,7 @@ return true; } - if (UseG1GC) { + if (UseG1GC && ClassUnloadingWithConcurrentMark) { G1CollectedHeap* g1h = G1CollectedHeap::heap(); g1h->g1_policy()->set_initiate_conc_mark_if_possible(); --- old/src/share/vm/memory/sharedHeap.cpp 2014-08-05 10:05:55.900559704 +0200 +++ new/src/share/vm/memory/sharedHeap.cpp 2014-08-05 10:05:55.784559709 +0200 @@ -159,9 +159,10 @@ Monitor* SharedHeap::StrongRootsScope::_lock = new Monitor(Mutex::leaf, "StrongRootsScope lock", false); void SharedHeap::StrongRootsScope::mark_worker_done_with_threads(uint n_workers) { - // The Thread work barrier is only needed by G1. +#if INCLUDE_ALL_GCS + // The Thread work barrier is only needed by G1 Class Unloading. // No need to use the barrier if this is single-threaded code. - if (UseG1GC && n_workers > 0) { + if (UseG1GC && ClassUnloadingWithConcurrentMark && n_workers > 0) { uint new_value = (uint)Atomic::add(1, &_n_workers_done_with_threads); if (new_value == n_workers) { // This thread is last. Notify the others. @@ -169,9 +170,14 @@ _lock->notify_all(); } } +#endif } void SharedHeap::StrongRootsScope::wait_until_all_workers_done_with_threads(uint n_workers) { +#if INCLUDE_ALL_GCS + assert(UseG1GC, "Currently only used by G1"); + assert(ClassUnloadingWithConcurrentMark, "Currently only needed when doing G1 Class Unloading"); + // No need to use the barrier if this is single-threaded code. if (n_workers > 0 && (uint)_n_workers_done_with_threads != n_workers) { MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag); @@ -179,6 +185,7 @@ _lock->wait(Mutex::_no_safepoint_check_flag, 0, false); } } +#endif } void SharedHeap::process_roots(bool activate_scope, --- old/src/share/vm/runtime/globals.hpp 2014-08-05 10:05:56.320559686 +0200 +++ new/src/share/vm/runtime/globals.hpp 2014-08-05 10:05:56.200559691 +0200 @@ -1078,6 +1078,9 @@ product(bool, ClassUnloading, true, \ "Do unloading of classes") \ \ + product(bool, ClassUnloadingWithConcurrentMark, true, \ + "Do unloading of classes with a concurrent marking cycle") \ + \ develop(bool, DisableStartThread, false, \ "Disable starting of additional Java threads " \ "(for debugging only)") \ --- old/test/TEST.groups 2014-08-05 10:05:56.748559668 +0200 +++ new/test/TEST.groups 2014-08-05 10:05:56.636559673 +0200 @@ -217,6 +217,7 @@ gc/arguments/TestMaxHeapSizeTools.java \ gc/arguments/TestMaxNewSize.java \ gc/arguments/TestUseCompressedOopsErgo.java \ + gc/class_unloading/TestG1ClassUnloadingHWM.java \ gc/g1/ \ gc/metaspace/G1AddMetaspaceDependency.java \ gc/metaspace/TestMetaspacePerfCounters.java \ @@ -257,7 +258,7 @@ gc/arguments/TestCMSHeapSizeFlags.java \ gc/arguments/TestMaxNewSize.java \ gc/arguments/TestUseCompressedOopsErgo.java \ - gc/class_unloading/TestCMSClassUnloadingDisabledHWM.java \ + gc/class_unloading/TestCMSClassUnloadingEnabledHWM.java \ gc/concurrentMarkSweep/ \ gc/startup_warnings/TestCMS.java \ gc/startup_warnings/TestCMSIncrementalMode.java \ --- /dev/null 2014-08-04 09:47:04.384576507 +0200 +++ new/test/gc/class_unloading/AllocateBeyondMetaspaceSize.java 2014-08-05 10:05:57.156559651 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. 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. + * + * 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. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import sun.hotspot.WhiteBox; + +class AllocateBeyondMetaspaceSize { + public static Object dummy; + + public static void main(String [] args) { + if (args.length != 2) { + throw new IllegalArgumentException("Usage: "); + } + + long metaspaceSize = Long.parseLong(args[0]); + long youngGenSize = Long.parseLong(args[1]); + + run(metaspaceSize, youngGenSize); + } + + private static void run(long metaspaceSize, long youngGenSize) { + WhiteBox wb = WhiteBox.getWhiteBox(); + + long allocationBeyondMetaspaceSize = metaspaceSize * 2; + long metaspace = wb.allocateMetaspace(null, allocationBeyondMetaspaceSize); + + triggerYoungGC(youngGenSize); + + wb.freeMetaspace(null, metaspace, metaspace); + } + + private static void triggerYoungGC(long youngGenSize) { + long approxAllocSize = 32 * 1024; + long numAllocations = 2 * youngGenSize / approxAllocSize; + + for (long i = 0; i < numAllocations; i++) { + dummy = new byte[(int)approxAllocSize]; + } + } +} --- /dev/null 2014-08-04 09:47:04.384576507 +0200 +++ new/test/gc/class_unloading/TestCMSClassUnloadingEnabledHWM.java 2014-08-05 10:05:57.740559627 +0200 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. 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. + * + * 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. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @key gc + * @bug 8049831 + * @library /testlibrary /testlibrary/whitebox + * @build TestCMSClassUnloadingEnabledHWM AllocateBeyondMetaspaceSize + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run driver TestCMSClassUnloadingEnabledHWM + * @summary Test that -XX:-CMSClassUnloadingEnabled will trigger a Full GC when more than MetaspaceSize metadata is allocated. + */ + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; + +import java.util.ArrayList; +import java.util.Arrays; + +public class TestCMSClassUnloadingEnabledHWM { + private static long MetaspaceSize = 32 * 1024 * 1024; + private static long YoungGenSize = 32 * 1024 * 1024; + + private static OutputAnalyzer run(boolean enableUnloading) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-XX:+WhiteBoxAPI", + "-XX:MetaspaceSize=" + MetaspaceSize, + "-Xmn" + YoungGenSize, + "-XX:+UseConcMarkSweepGC", + "-XX:" + (enableUnloading ? "+" : "-") + "CMSClassUnloadingEnabled", + "-XX:+PrintHeapAtGC", + "-XX:+PrintGCDetails", + "AllocateBeyondMetaspaceSize", + "" + MetaspaceSize, + "" + YoungGenSize); + return new OutputAnalyzer(pb.start()); + } + + public static OutputAnalyzer runWithCMSClassUnloading() throws Exception { + return run(true); + } + + public static OutputAnalyzer runWithoutCMSClassUnloading() throws Exception { + return run(false); + } + + public static void testWithoutCMSClassUnloading() throws Exception { + // -XX:-CMSClassUnloadingEnabled is used, so we expect a full GC instead of a concurrent cycle. + OutputAnalyzer out = runWithoutCMSClassUnloading(); + + out.shouldMatch(".*Full GC.*"); + out.shouldNotMatch(".*CMS Initial Mark.*"); + } + + public static void testWithCMSClassUnloading() throws Exception { + // -XX:+CMSClassUnloadingEnabled is used, so we expect a concurrent cycle instead of a full GC. + OutputAnalyzer out = runWithCMSClassUnloading(); + + out.shouldMatch(".*CMS Initial Mark.*"); + out.shouldNotMatch(".*Full GC.*"); + } + + public static void main(String args[]) throws Exception { + testWithCMSClassUnloading(); + testWithoutCMSClassUnloading(); + } +} + --- /dev/null 2014-08-04 09:47:04.384576507 +0200 +++ new/test/gc/class_unloading/TestG1ClassUnloadingHWM.java 2014-08-05 10:05:58.224559607 +0200 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. 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. + * + * 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. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @key gc + * @bug 8049831 + * @library /testlibrary /testlibrary/whitebox + * @build TestG1ClassUnloadingHWM AllocateBeyondMetaspaceSize + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run driver TestG1ClassUnloadingHWM + * @summary Test that -XX:-ClassUnloadingWithConcurrentMark will trigger a Full GC when more than MetaspaceSize metadata is allocated. + */ + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; + +import java.util.ArrayList; +import java.util.Arrays; + +public class TestG1ClassUnloadingHWM { + private static long MetaspaceSize = 32 * 1024 * 1024; + private static long YoungGenSize = 32 * 1024 * 1024; + + private static OutputAnalyzer run(boolean enableUnloading) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-XX:+WhiteBoxAPI", + "-XX:MetaspaceSize=" + MetaspaceSize, + "-Xmn" + YoungGenSize, + "-XX:+UseG1GC", + "-XX:" + (enableUnloading ? "+" : "-") + "ClassUnloadingWithConcurrentMark", + "-XX:+PrintHeapAtGC", + "-XX:+PrintGCDetails", + "AllocateBeyondMetaspaceSize", + "" + MetaspaceSize, + "" + YoungGenSize); + return new OutputAnalyzer(pb.start()); + } + + public static OutputAnalyzer runWithG1ClassUnloading() throws Exception { + return run(true); + } + + public static OutputAnalyzer runWithoutG1ClassUnloading() throws Exception { + return run(false); + } + + public static void testWithoutG1ClassUnloading() throws Exception { + // -XX:-ClassUnloadingWithConcurrentMark is used, so we expect a full GC instead of a concurrent cycle. + OutputAnalyzer out = runWithoutG1ClassUnloading(); + + out.shouldMatch(".*Full GC.*"); + out.shouldNotMatch(".*initial-mark.*"); + } + + public static void testWithG1ClassUnloading() throws Exception { + // -XX:+ClassUnloadingWithConcurrentMark is used, so we expect a concurrent cycle instead of a full GC. + OutputAnalyzer out = runWithG1ClassUnloading(); + + out.shouldMatch(".*initial-mark.*"); + out.shouldNotMatch(".*Full GC.*"); + } + + public static void main(String args[]) throws Exception { + testWithG1ClassUnloading(); + testWithoutG1ClassUnloading(); + } +} +