8042933: assert(capacity_until_gc >= committed_bytes) failed
authorehelin
Thu, 29 May 2014 14:31:28 +0200
changeset 24847 37c354b113fe
parent 24846 269aeb9131ba
child 24848 34d57b35331b
8042933: assert(capacity_until_gc >= committed_bytes) failed Reviewed-by: stefank, jmasa
hotspot/src/share/vm/memory/metaspace.cpp
hotspot/src/share/vm/memory/metaspace.hpp
hotspot/src/share/vm/runtime/thread.cpp
hotspot/test/gc/metaspace/TestMetaspaceInitialization.java
--- a/hotspot/src/share/vm/memory/metaspace.cpp	Tue Jun 03 09:34:04 2014 +0000
+++ b/hotspot/src/share/vm/memory/metaspace.cpp	Thu May 29 14:31:28 2014 +0200
@@ -1423,6 +1423,17 @@
   return (size_t)Atomic::add_ptr(-(intptr_t)v, &_capacity_until_GC);
 }
 
+void MetaspaceGC::initialize() {
+  // Set the high-water mark to MaxMetapaceSize during VM initializaton since
+  // we can't do a GC during initialization.
+  _capacity_until_GC = MaxMetaspaceSize;
+}
+
+void MetaspaceGC::post_initialize() {
+  // Reset the high-water mark once the VM initialization is done.
+  _capacity_until_GC = MAX2(MetaspaceAux::committed_bytes(), MetaspaceSize);
+}
+
 bool MetaspaceGC::can_expand(size_t word_size, bool is_class) {
   // Check if the compressed class space is full.
   if (is_class && Metaspace::using_class_space()) {
@@ -1443,21 +1454,13 @@
 
 size_t MetaspaceGC::allowed_expansion() {
   size_t committed_bytes = MetaspaceAux::committed_bytes();
+  size_t capacity_until_gc = capacity_until_GC();
+
+  assert(capacity_until_gc >= committed_bytes,
+        err_msg("capacity_until_gc: " SIZE_FORMAT " < committed_bytes: " SIZE_FORMAT,
+                capacity_until_gc, committed_bytes));
 
   size_t left_until_max  = MaxMetaspaceSize - committed_bytes;
-
-  // Always grant expansion if we are initiating the JVM,
-  // or if the GC_locker is preventing GCs.
-  if (!is_init_completed() || GC_locker::is_active_and_needs_gc()) {
-    return left_until_max / BytesPerWord;
-  }
-
-  size_t capacity_until_gc = capacity_until_GC();
-
-  if (capacity_until_gc <= committed_bytes) {
-    return 0;
-  }
-
   size_t left_until_GC = capacity_until_gc - committed_bytes;
   size_t left_to_commit = MIN2(left_until_GC, left_until_max);
 
@@ -1469,7 +1472,15 @@
   uint current_shrink_factor = _shrink_factor;
   _shrink_factor = 0;
 
-  const size_t used_after_gc = MetaspaceAux::capacity_bytes();
+  // Using committed_bytes() for used_after_gc is an overestimation, since the
+  // chunk free lists are included in committed_bytes() and the memory in an
+  // un-fragmented chunk free list is available for future allocations.
+  // However, if the chunk free lists becomes fragmented, then the memory may
+  // not be available for future allocations and the memory is therefore "in use".
+  // Including the chunk free lists in the definition of "in use" is therefore
+  // necessary. Not including the chunk free lists can cause capacity_until_GC to
+  // shrink below committed_bytes() and this has caused serious bugs in the past.
+  const size_t used_after_gc = MetaspaceAux::committed_bytes();
   const size_t capacity_until_GC = MetaspaceGC::capacity_until_GC();
 
   const double minimum_free_percentage = MinMetaspaceFreeRatio / 100.0;
@@ -3094,6 +3105,8 @@
 }
 
 void Metaspace::global_initialize() {
+  MetaspaceGC::initialize();
+
   // Initialize the alignment for shared spaces.
   int max_alignment = os::vm_allocation_granularity();
   size_t cds_total = 0;
@@ -3201,10 +3214,13 @@
     }
   }
 
-  MetaspaceGC::initialize();
   _tracer = new MetaspaceTracer();
 }
 
+void Metaspace::post_initialize() {
+  MetaspaceGC::post_initialize();
+}
+
 Metachunk* Metaspace::get_initialization_chunk(MetadataType mdtype,
                                                size_t chunk_word_size,
                                                size_t chunk_bunch) {
--- a/hotspot/src/share/vm/memory/metaspace.hpp	Tue Jun 03 09:34:04 2014 +0000
+++ b/hotspot/src/share/vm/memory/metaspace.hpp	Thu May 29 14:31:28 2014 +0200
@@ -208,6 +208,7 @@
 
   static void ergo_initialize();
   static void global_initialize();
+  static void post_initialize();
 
   static size_t first_chunk_word_size() { return _first_chunk_word_size; }
   static size_t first_class_chunk_word_size() { return _first_class_chunk_word_size; }
@@ -398,7 +399,8 @@
 
  public:
 
-  static void initialize() { _capacity_until_GC = MetaspaceSize; }
+  static void initialize();
+  static void post_initialize();
 
   static size_t capacity_until_GC();
   static size_t inc_capacity_until_GC(size_t v);
--- a/hotspot/src/share/vm/runtime/thread.cpp	Tue Jun 03 09:34:04 2014 +0000
+++ b/hotspot/src/share/vm/runtime/thread.cpp	Thu May 29 14:31:28 2014 +0200
@@ -3543,6 +3543,8 @@
   // debug stuff, that does not work until all basic classes have been initialized.
   set_init_completed();
 
+  Metaspace::post_initialize();
+
   HOTSPOT_VM_INIT_END();
 
   // record VM initialization completion time
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/metaspace/TestMetaspaceInitialization.java	Thu May 29 14:31:28 2014 +0200
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013, 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 java.util.ArrayList;
+
+/* @test TestMetaspaceInitialization
+ * @bug 8042933
+ * @summary Tests to initialize metaspace with a very low MetaspaceSize
+ * @library /testlibrary
+ * @run main/othervm -XX:MetaspaceSize=2m TestMetaspaceInitialization
+ */
+public class TestMetaspaceInitialization {
+    private class Internal {
+        public int x;
+        public Internal(int x) {
+            this.x = x;
+        }
+    }
+
+    private void test() {
+        ArrayList<Internal> l = new ArrayList<>();
+        l.add(new Internal(17));
+    }
+
+    public static void main(String[] args) {
+        new TestMetaspaceInitialization().test();
+    }
+}