8015391: NPG: With -XX:+UseCompressedKlassPointers OOME due to exhausted metadata space could occur when metaspace is almost empty
authorcoleenp
Tue, 02 Jul 2013 08:42:37 -0400
changeset 18491 dd63ba6b0ee4
parent 18433 bcb2e8b5f81e
child 18492 ab4fbcf75767
8015391: NPG: With -XX:+UseCompressedKlassPointers OOME due to exhausted metadata space could occur when metaspace is almost empty Summary: Allocate medium chunks for class metaspace when class loader has lots of classes Reviewed-by: mgerdin, jmasa
hotspot/src/share/vm/memory/metaspace.cpp
hotspot/src/share/vm/memory/universe.cpp
hotspot/src/share/vm/memory/universe.hpp
hotspot/src/share/vm/runtime/vmStructs.cpp
--- a/hotspot/src/share/vm/memory/metaspace.cpp	Thu Jun 20 11:53:51 2013 +0200
+++ b/hotspot/src/share/vm/memory/metaspace.cpp	Tue Jul 02 08:42:37 2013 -0400
@@ -70,7 +70,7 @@
   SpecializedChunk = 128,
   ClassSmallChunk = 256,
   SmallChunk = 512,
-  ClassMediumChunk = 1 * K,
+  ClassMediumChunk = 4 * K,
   MediumChunk = 8 * K,
   HumongousChunkGranularity = 8
 };
@@ -580,7 +580,6 @@
   // Number of small chunks to allocate to a manager
   // If class space manager, small chunks are unlimited
   static uint const _small_chunk_limit;
-  bool has_small_chunk_limit() { return !vs_list()->is_class(); }
 
   // Sum of all space in allocated chunks
   size_t _allocated_blocks_words;
@@ -1298,13 +1297,18 @@
 
 bool MetaspaceGC::should_expand(VirtualSpaceList* vsl, size_t word_size) {
 
-  size_t committed_capacity_bytes = MetaspaceAux::allocated_capacity_bytes();
   // If the user wants a limit, impose one.
-  size_t max_metaspace_size_bytes = MaxMetaspaceSize;
-  size_t metaspace_size_bytes = MetaspaceSize;
-  if (!FLAG_IS_DEFAULT(MaxMetaspaceSize) &&
-      MetaspaceAux::reserved_in_bytes() >= MaxMetaspaceSize) {
-    return false;
+  // The reason for someone using this flag is to limit reserved space.  So
+  // for non-class virtual space, compare against virtual spaces that are reserved.
+  // For class virtual space, we only compare against the committed space, not
+  // reserved space, because this is a larger space prereserved for compressed
+  // class pointers.
+  if (!FLAG_IS_DEFAULT(MaxMetaspaceSize)) {
+    size_t real_allocated = Metaspace::space_list()->virtual_space_total() +
+              MetaspaceAux::allocated_capacity_bytes(Metaspace::ClassType);
+    if (real_allocated >= MaxMetaspaceSize) {
+      return false;
+    }
   }
 
   // Class virtual space should always be expanded.  Call GC for the other
@@ -1318,11 +1322,12 @@
   }
 
 
-
   // If the capacity is below the minimum capacity, allow the
   // expansion.  Also set the high-water-mark (capacity_until_GC)
   // to that minimum capacity so that a GC will not be induced
   // until that minimum capacity is exceeded.
+  size_t committed_capacity_bytes = MetaspaceAux::allocated_capacity_bytes();
+  size_t metaspace_size_bytes = MetaspaceSize;
   if (committed_capacity_bytes < metaspace_size_bytes ||
       capacity_until_GC() == 0) {
     set_capacity_until_GC(metaspace_size_bytes);
@@ -1866,13 +1871,11 @@
   Metachunk* chunk = chunks_in_use(index);
   // Count the free space in all the chunk but not the
   // current chunk from which allocations are still being done.
-  if (chunk != NULL) {
-    Metachunk* prev = chunk;
-    while (chunk != NULL && chunk != current_chunk()) {
+  while (chunk != NULL) {
+    if (chunk != current_chunk()) {
       result += chunk->free_word_size();
-      prev = chunk;
-      chunk = chunk->next();
     }
+    chunk = chunk->next();
   }
   return result;
 }
@@ -1961,8 +1964,7 @@
   // chunks will be allocated.
   size_t chunk_word_size;
   if (chunks_in_use(MediumIndex) == NULL &&
-      (!has_small_chunk_limit() ||
-       sum_count_in_chunks_in_use(SmallIndex) < _small_chunk_limit)) {
+      sum_count_in_chunks_in_use(SmallIndex) < _small_chunk_limit) {
     chunk_word_size = (size_t) small_chunk_size();
     if (word_size + Metachunk::overhead() > small_chunk_size()) {
       chunk_word_size = medium_chunk_size();
@@ -2671,10 +2673,10 @@
 // Print total fragmentation for class and data metaspaces separately
 void MetaspaceAux::print_waste(outputStream* out) {
 
-  size_t specialized_waste = 0, small_waste = 0, medium_waste = 0, large_waste = 0;
-  size_t specialized_count = 0, small_count = 0, medium_count = 0, large_count = 0;
-  size_t cls_specialized_waste = 0, cls_small_waste = 0, cls_medium_waste = 0, cls_large_waste = 0;
-  size_t cls_specialized_count = 0, cls_small_count = 0, cls_medium_count = 0, cls_large_count = 0;
+  size_t specialized_waste = 0, small_waste = 0, medium_waste = 0;
+  size_t specialized_count = 0, small_count = 0, medium_count = 0, humongous_count = 0;
+  size_t cls_specialized_waste = 0, cls_small_waste = 0, cls_medium_waste = 0;
+  size_t cls_specialized_count = 0, cls_small_count = 0, cls_medium_count = 0, cls_humongous_count = 0;
 
   ClassLoaderDataGraphMetaspaceIterator iter;
   while (iter.repeat()) {
@@ -2686,8 +2688,7 @@
       small_count += msp->vsm()->sum_count_in_chunks_in_use(SmallIndex);
       medium_waste += msp->vsm()->sum_waste_in_chunks_in_use(MediumIndex);
       medium_count += msp->vsm()->sum_count_in_chunks_in_use(MediumIndex);
-      large_waste += msp->vsm()->sum_waste_in_chunks_in_use(HumongousIndex);
-      large_count += msp->vsm()->sum_count_in_chunks_in_use(HumongousIndex);
+      humongous_count += msp->vsm()->sum_count_in_chunks_in_use(HumongousIndex);
 
       cls_specialized_waste += msp->class_vsm()->sum_waste_in_chunks_in_use(SpecializedIndex);
       cls_specialized_count += msp->class_vsm()->sum_count_in_chunks_in_use(SpecializedIndex);
@@ -2695,20 +2696,23 @@
       cls_small_count += msp->class_vsm()->sum_count_in_chunks_in_use(SmallIndex);
       cls_medium_waste += msp->class_vsm()->sum_waste_in_chunks_in_use(MediumIndex);
       cls_medium_count += msp->class_vsm()->sum_count_in_chunks_in_use(MediumIndex);
-      cls_large_waste += msp->class_vsm()->sum_waste_in_chunks_in_use(HumongousIndex);
-      cls_large_count += msp->class_vsm()->sum_count_in_chunks_in_use(HumongousIndex);
+      cls_humongous_count += msp->class_vsm()->sum_count_in_chunks_in_use(HumongousIndex);
     }
   }
   out->print_cr("Total fragmentation waste (words) doesn't count free space");
   out->print_cr("  data: " SIZE_FORMAT " specialized(s) " SIZE_FORMAT ", "
                         SIZE_FORMAT " small(s) " SIZE_FORMAT ", "
-                        SIZE_FORMAT " medium(s) " SIZE_FORMAT,
+                        SIZE_FORMAT " medium(s) " SIZE_FORMAT ", "
+                        "large count " SIZE_FORMAT,
              specialized_count, specialized_waste, small_count,
-             small_waste, medium_count, medium_waste);
+             small_waste, medium_count, medium_waste, humongous_count);
   out->print_cr(" class: " SIZE_FORMAT " specialized(s) " SIZE_FORMAT ", "
-                           SIZE_FORMAT " small(s) " SIZE_FORMAT,
+                           SIZE_FORMAT " small(s) " SIZE_FORMAT ", "
+                           SIZE_FORMAT " medium(s) " SIZE_FORMAT ", "
+                           "large count " SIZE_FORMAT,
              cls_specialized_count, cls_specialized_waste,
-             cls_small_count, cls_small_waste);
+             cls_small_count, cls_small_waste,
+             cls_medium_count, cls_medium_waste, cls_humongous_count);
 }
 
 // Dump global metaspace things from the end of ClassLoaderDataGraph
@@ -3049,18 +3053,24 @@
       if (Verbose && TraceMetadataChunkAllocation) {
         gclog_or_tty->print_cr("Metaspace allocation failed for size "
           SIZE_FORMAT, word_size);
-        if (loader_data->metaspace_or_null() != NULL) loader_data->metaspace_or_null()->dump(gclog_or_tty);
+        if (loader_data->metaspace_or_null() != NULL) loader_data->dump(gclog_or_tty);
         MetaspaceAux::dump(gclog_or_tty);
       }
       // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support
-      report_java_out_of_memory("Metadata space");
+      const char* space_string = (mdtype == ClassType) ? "Class Metadata space" :
+                                                         "Metadata space";
+      report_java_out_of_memory(space_string);
 
       if (JvmtiExport::should_post_resource_exhausted()) {
         JvmtiExport::post_resource_exhausted(
             JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR,
-            "Metadata space");
+            space_string);
       }
-      THROW_OOP_0(Universe::out_of_memory_error_perm_gen());
+      if (mdtype == ClassType) {
+        THROW_OOP_0(Universe::out_of_memory_error_class_metaspace());
+      } else {
+        THROW_OOP_0(Universe::out_of_memory_error_metaspace());
+      }
     }
   }
   return Metablock::initialize(result, word_size);
--- a/hotspot/src/share/vm/memory/universe.cpp	Thu Jun 20 11:53:51 2013 +0200
+++ b/hotspot/src/share/vm/memory/universe.cpp	Tue Jul 02 08:42:37 2013 -0400
@@ -110,7 +110,8 @@
 LatestMethodOopCache* Universe::_loader_addClass_cache    = NULL;
 ActiveMethodOopsCache* Universe::_reflect_invoke_cache    = NULL;
 oop Universe::_out_of_memory_error_java_heap          = NULL;
-oop Universe::_out_of_memory_error_perm_gen           = NULL;
+oop Universe::_out_of_memory_error_metaspace          = NULL;
+oop Universe::_out_of_memory_error_class_metaspace    = NULL;
 oop Universe::_out_of_memory_error_array_size         = NULL;
 oop Universe::_out_of_memory_error_gc_overhead_limit  = NULL;
 objArrayOop Universe::_preallocated_out_of_memory_error_array = NULL;
@@ -179,7 +180,8 @@
   f->do_oop((oop*)&_the_null_string);
   f->do_oop((oop*)&_the_min_jint_string);
   f->do_oop((oop*)&_out_of_memory_error_java_heap);
-  f->do_oop((oop*)&_out_of_memory_error_perm_gen);
+  f->do_oop((oop*)&_out_of_memory_error_metaspace);
+  f->do_oop((oop*)&_out_of_memory_error_class_metaspace);
   f->do_oop((oop*)&_out_of_memory_error_array_size);
   f->do_oop((oop*)&_out_of_memory_error_gc_overhead_limit);
     f->do_oop((oop*)&_preallocated_out_of_memory_error_array);
@@ -561,7 +563,8 @@
   // a potential loop which could happen if an out of memory occurs when attempting
   // to allocate the backtrace.
   return ((throwable() != Universe::_out_of_memory_error_java_heap) &&
-          (throwable() != Universe::_out_of_memory_error_perm_gen)  &&
+          (throwable() != Universe::_out_of_memory_error_metaspace)  &&
+          (throwable() != Universe::_out_of_memory_error_class_metaspace)  &&
           (throwable() != Universe::_out_of_memory_error_array_size) &&
           (throwable() != Universe::_out_of_memory_error_gc_overhead_limit));
 }
@@ -1011,7 +1014,8 @@
     k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_OutOfMemoryError(), true, CHECK_false);
     k_h = instanceKlassHandle(THREAD, k);
     Universe::_out_of_memory_error_java_heap = k_h->allocate_instance(CHECK_false);
-    Universe::_out_of_memory_error_perm_gen = k_h->allocate_instance(CHECK_false);
+    Universe::_out_of_memory_error_metaspace = k_h->allocate_instance(CHECK_false);
+    Universe::_out_of_memory_error_class_metaspace = k_h->allocate_instance(CHECK_false);
     Universe::_out_of_memory_error_array_size = k_h->allocate_instance(CHECK_false);
     Universe::_out_of_memory_error_gc_overhead_limit =
       k_h->allocate_instance(CHECK_false);
@@ -1044,7 +1048,9 @@
     java_lang_Throwable::set_message(Universe::_out_of_memory_error_java_heap, msg());
 
     msg = java_lang_String::create_from_str("Metadata space", CHECK_false);
-    java_lang_Throwable::set_message(Universe::_out_of_memory_error_perm_gen, msg());
+    java_lang_Throwable::set_message(Universe::_out_of_memory_error_metaspace, msg());
+    msg = java_lang_String::create_from_str("Class Metadata space", CHECK_false);
+    java_lang_Throwable::set_message(Universe::_out_of_memory_error_class_metaspace, msg());
 
     msg = java_lang_String::create_from_str("Requested array size exceeds VM limit", CHECK_false);
     java_lang_Throwable::set_message(Universe::_out_of_memory_error_array_size, msg());
--- a/hotspot/src/share/vm/memory/universe.hpp	Thu Jun 20 11:53:51 2013 +0200
+++ b/hotspot/src/share/vm/memory/universe.hpp	Tue Jul 02 08:42:37 2013 -0400
@@ -177,10 +177,12 @@
   static LatestMethodOopCache* _finalizer_register_cache; // static method for registering finalizable objects
   static LatestMethodOopCache* _loader_addClass_cache;    // method for registering loaded classes in class loader vector
   static ActiveMethodOopsCache* _reflect_invoke_cache;    // method for security checks
-  static oop          _out_of_memory_error_java_heap; // preallocated error object (no backtrace)
-  static oop          _out_of_memory_error_perm_gen;  // preallocated error object (no backtrace)
-  static oop          _out_of_memory_error_array_size;// preallocated error object (no backtrace)
-  static oop          _out_of_memory_error_gc_overhead_limit; // preallocated error object (no backtrace)
+  // preallocated error objects (no backtrace)
+  static oop          _out_of_memory_error_java_heap;
+  static oop          _out_of_memory_error_metaspace;
+  static oop          _out_of_memory_error_class_metaspace;
+  static oop          _out_of_memory_error_array_size;
+  static oop          _out_of_memory_error_gc_overhead_limit;
 
   static Array<int>*       _the_empty_int_array;    // Canonicalized int array
   static Array<u2>*        _the_empty_short_array;  // Canonicalized short array
@@ -348,7 +350,8 @@
   // may or may not have a backtrace. If error has a backtrace then the stack trace is already
   // filled in.
   static oop out_of_memory_error_java_heap()          { return gen_out_of_memory_error(_out_of_memory_error_java_heap);  }
-  static oop out_of_memory_error_perm_gen()           { return gen_out_of_memory_error(_out_of_memory_error_perm_gen);   }
+  static oop out_of_memory_error_metaspace()          { return gen_out_of_memory_error(_out_of_memory_error_metaspace);   }
+  static oop out_of_memory_error_class_metaspace()    { return gen_out_of_memory_error(_out_of_memory_error_class_metaspace);   }
   static oop out_of_memory_error_array_size()         { return gen_out_of_memory_error(_out_of_memory_error_array_size); }
   static oop out_of_memory_error_gc_overhead_limit()  { return gen_out_of_memory_error(_out_of_memory_error_gc_overhead_limit);  }
 
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp	Thu Jun 20 11:53:51 2013 +0200
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp	Tue Jul 02 08:42:37 2013 -0400
@@ -437,10 +437,6 @@
      static_field(Universe,                    _main_thread_group,                            oop)                                   \
      static_field(Universe,                    _system_thread_group,                          oop)                                   \
      static_field(Universe,                    _the_empty_class_klass_array,                  objArrayOop)                           \
-     static_field(Universe,                    _out_of_memory_error_java_heap,                oop)                                   \
-     static_field(Universe,                    _out_of_memory_error_perm_gen,                 oop)                                   \
-     static_field(Universe,                    _out_of_memory_error_array_size,               oop)                                   \
-     static_field(Universe,                    _out_of_memory_error_gc_overhead_limit,        oop)                                   \
      static_field(Universe,                    _null_ptr_exception_instance,                  oop)                                   \
      static_field(Universe,                    _arithmetic_exception_instance,                oop)                                   \
      static_field(Universe,                    _vm_exception,                                 oop)                                   \