changeset 6815:9d38e660fc7a

8048269: Add flag to turn off class unloading after G1 concurrent mark Summary: Added -XX:+/-ClassUnloadingWithConcurrentMark Reviewed-by: jmasa, brutisso, mgerdin
author stefank
date Wed, 06 Aug 2014 09:55:16 +0200
parents 14e40cb29ca9
children c9142face067
files src/share/vm/gc_implementation/g1/concurrentMark.cpp src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp src/share/vm/gc_implementation/g1/heapRegion.inline.hpp src/share/vm/gc_implementation/shared/vmGCOperations.cpp src/share/vm/memory/sharedHeap.cpp src/share/vm/runtime/globals.hpp test/TEST.groups test/gc/class_unloading/AllocateBeyondMetaspaceSize.java test/gc/class_unloading/TestCMSClassUnloadingDisabledHWM.java test/gc/class_unloading/TestCMSClassUnloadingEnabledHWM.java test/gc/class_unloading/TestG1ClassUnloadingHWM.java
diffstat 11 files changed, 307 insertions(+), 143 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/gc_implementation/g1/concurrentMark.cpp	Mon Aug 04 15:04:45 2014 +0200
+++ b/src/share/vm/gc_implementation/g1/concurrentMark.cpp	Wed Aug 06 09:55:16 2014 +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());
-
-  bool purged_classes;
-
   {
-    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);
-  }
-
-  if (G1StringDedup::is_enabled()) {
-    G1RemarkGCTraceTime trace("String Deduplication Unlink", G1Log::finest());
-    G1StringDedup::unlink(&g1_is_alive);
+    G1RemarkGCTraceTime trace("Unloading", G1Log::finer());
+
+    if (ClassUnloadingWithConcurrentMark) {
+      bool purged_classes;
+
+      {
+        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);
+      }
+    }
+
+    if (G1StringDedup::is_enabled()) {
+      G1RemarkGCTraceTime trace("String Deduplication Unlink", G1Log::finest());
+      G1StringDedup::unlink(&g1_is_alive);
+    }
   }
 }
 
--- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Mon Aug 04 15:04:45 2014 +0200
+++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Wed Aug 06 09:55:16 2014 +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());
--- a/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp	Mon Aug 04 15:04:45 2014 +0200
+++ b/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp	Wed Aug 06 09:55:16 2014 +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());
+  HeapWord* next = g1h->concurrent_mark()->prevMarkBitMap()->
+      getNextMarkedWordAddress(addr, prev_top_at_mark_start());
 
-    assert(next > addr, "must get the next live object");
-
-    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) {
--- a/src/share/vm/gc_implementation/shared/vmGCOperations.cpp	Mon Aug 04 15:04:45 2014 +0200
+++ b/src/share/vm/gc_implementation/shared/vmGCOperations.cpp	Wed Aug 06 09:55:16 2014 +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();
 
--- a/src/share/vm/memory/sharedHeap.cpp	Mon Aug 04 15:04:45 2014 +0200
+++ b/src/share/vm/memory/sharedHeap.cpp	Wed Aug 06 09:55:16 2014 +0200
@@ -159,9 +159,9 @@
 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.
+  // 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.
@@ -172,6 +172,9 @@
 }
 
 void SharedHeap::StrongRootsScope::wait_until_all_workers_done_with_threads(uint n_workers) {
+  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);
--- a/src/share/vm/runtime/globals.hpp	Mon Aug 04 15:04:45 2014 +0200
+++ b/src/share/vm/runtime/globals.hpp	Wed Aug 06 09:55:16 2014 +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)")                                           \
--- a/test/TEST.groups	Mon Aug 04 15:04:45 2014 +0200
+++ b/test/TEST.groups	Wed Aug 06 09:55:16 2014 +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	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/class_unloading/AllocateBeyondMetaspaceSize.java	Wed Aug 06 09:55:16 2014 +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: <MetaspaceSize> <YoungGenSize>");
+    }
+
+    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];
+    }
+  }
+}
--- a/test/gc/class_unloading/TestCMSClassUnloadingDisabledHWM.java	Mon Aug 04 15:04:45 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * 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 TestCMSClassUnloadingDisabledHWM
- * @run main ClassFileInstaller sun.hotspot.WhiteBox
- * @run driver TestCMSClassUnloadingDisabledHWM
- * @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 sun.hotspot.WhiteBox;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-public class TestCMSClassUnloadingDisabledHWM {
-
-  private static OutputAnalyzer run(long metaspaceSize, long youngGenSize) throws Exception {
-      ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
-        "-Xbootclasspath/a:.",
-        "-XX:+WhiteBoxAPI",
-        "-XX:MetaspaceSize=" + metaspaceSize,
-        "-Xmn" + youngGenSize,
-        "-XX:+UseConcMarkSweepGC",
-        "-XX:-CMSClassUnloadingEnabled",
-        "-XX:+PrintHeapAtGC",
-        "-XX:+PrintGCDetails",
-        "AllocateBeyondMetaspaceSize",
-        "" + metaspaceSize,
-        "" + youngGenSize);
-    return new OutputAnalyzer(pb.start());
-  }
-
-  public static void main(String args[]) throws Exception {
-    long metaspaceSize = 32 * 1024 * 1024;
-    long youngGenSize = 32 * 1024 * 1024;
-
-    OutputAnalyzer out = run(metaspaceSize, youngGenSize);
-
-    // -XX:-CMSClassUnloadingEnabled is used, so we expect a full GC instead of a concurrent cycle.
-    out.shouldMatch(".*Full GC.*");
-    out.shouldNotMatch(".*CMS Initial Mark.*");
-  }
-}
-
-class AllocateBeyondMetaspaceSize {
-  public static Object dummy;
-
-  public static void main(String [] args) {
-    if (args.length != 2) {
-      throw new IllegalArgumentException("Usage: <MetaspaceSize> <YoungGenSize>");
-    }
-
-    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	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/class_unloading/TestCMSClassUnloadingEnabledHWM.java	Wed Aug 06 09:55:16 2014 +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	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/class_unloading/TestG1ClassUnloadingHWM.java	Wed Aug 06 09:55:16 2014 +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();
+  }
+}
+