changeset 8858:15c04fe93c18

Merge
author mgerdin
date Wed, 03 Apr 2013 09:19:02 +0200
parents cc5b5976d72c (diff) d26674db4d91 (current diff)
children 0c039865ef2b
files make/windows/projectfiles/kernel/Makefile make/windows/projectfiles/kernel/vm.def make/windows/projectfiles/kernel/vm.dsw src/os/linux/vm/os_linux.cpp src/share/vm/runtime/arguments.cpp src/share/vm/runtime/globals.hpp test/runtime/8007736/TestStaticIF.java
diffstat 21 files changed, 316 insertions(+), 99 deletions(-) [+]
line wrap: on
line diff
--- a/src/os/bsd/vm/os_bsd.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/os/bsd/vm/os_bsd.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -167,20 +167,6 @@
   return Bsd::physical_memory();
 }
 
-julong os::allocatable_physical_memory(julong size) {
-#ifdef _LP64
-  return size;
-#else
-  julong result = MIN2(size, (julong)3800*M);
-   if (!is_allocatable(result)) {
-     // See comments under solaris for alignment considerations
-     julong reasonable_size = (julong)2*G - 2 * os::vm_page_size();
-     result =  MIN2(size, reasonable_size);
-   }
-   return result;
-#endif // _LP64
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // environment support
 
--- a/src/os/linux/vm/os_linux.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/os/linux/vm/os_linux.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -194,20 +194,6 @@
   return Linux::physical_memory();
 }
 
-julong os::allocatable_physical_memory(julong size) {
-#ifdef _LP64
-  return size;
-#else
-  julong result = MIN2(size, (julong)3800*M);
-   if (!is_allocatable(result)) {
-     // See comments under solaris for alignment considerations
-     julong reasonable_size = (julong)2*G - 2 * os::vm_page_size();
-     result =  MIN2(size, reasonable_size);
-   }
-   return result;
-#endif // _LP64
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // environment support
 
--- a/src/os/posix/vm/os_posix.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/os/posix/vm/os_posix.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -188,4 +188,66 @@
   st->cr();
 }
 
+bool os::has_allocatable_memory_limit(julong* limit) {
+  struct rlimit rlim;
+  int getrlimit_res = getrlimit(RLIMIT_AS, &rlim);
+  // if there was an error when calling getrlimit, assume that there is no limitation
+  // on virtual memory.
+  bool result;
+  if ((getrlimit_res != 0) || (rlim.rlim_cur == RLIM_INFINITY)) {
+    result = false;
+  } else {
+    *limit = (julong)rlim.rlim_cur;
+    result = true;
+  }
+#ifdef _LP64
+  return result;
+#else
+  // arbitrary virtual space limit for 32 bit Unices found by testing. If
+  // getrlimit above returned a limit, bound it with this limit. Otherwise
+  // directly use it.
+  const julong max_virtual_limit = (julong)3800*M;
+  if (result) {
+    *limit = MIN2(*limit, max_virtual_limit);
+  } else {
+    *limit = max_virtual_limit;
+  }
 
+  // bound by actually allocatable memory. The algorithm uses two bounds, an
+  // upper and a lower limit. The upper limit is the current highest amount of
+  // memory that could not be allocated, the lower limit is the current highest
+  // amount of memory that could be allocated.
+  // The algorithm iteratively refines the result by halving the difference
+  // between these limits, updating either the upper limit (if that value could
+  // not be allocated) or the lower limit (if the that value could be allocated)
+  // until the difference between these limits is "small".
+
+  // the minimum amount of memory we care about allocating.
+  const julong min_allocation_size = M;
+
+  julong upper_limit = *limit;
+
+  // first check a few trivial cases
+  if (is_allocatable(upper_limit) || (upper_limit <= min_allocation_size)) {
+    *limit = upper_limit;
+  } else if (!is_allocatable(min_allocation_size)) {
+    // we found that not even min_allocation_size is allocatable. Return it
+    // anyway. There is no point to search for a better value any more.
+    *limit = min_allocation_size;
+  } else {
+    // perform the binary search.
+    julong lower_limit = min_allocation_size;
+    while ((upper_limit - lower_limit) > min_allocation_size) {
+      julong temp_limit = ((upper_limit - lower_limit) / 2) + lower_limit;
+      temp_limit = align_size_down_(temp_limit, min_allocation_size);
+      if (is_allocatable(temp_limit)) {
+        lower_limit = temp_limit;
+      } else {
+        upper_limit = temp_limit;
+      }
+    }
+    *limit = lower_limit;
+  }
+  return true;
+#endif
+}
--- a/src/os/solaris/vm/os_solaris.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/os/solaris/vm/os_solaris.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -476,24 +476,6 @@
    return Solaris::physical_memory();
 }
 
-julong os::allocatable_physical_memory(julong size) {
-#ifdef _LP64
-   return size;
-#else
-   julong result = MIN2(size, (julong)3835*M);
-   if (!is_allocatable(result)) {
-     // Memory allocations will be aligned but the alignment
-     // is not known at this point.  Alignments will
-     // be at most to LargePageSizeInBytes.  Protect
-     // allocations from alignments up to illegal
-     // values. If at this point 2G is illegal.
-     julong reasonable_size = (julong)2*G - 2 * LargePageSizeInBytes;
-     result =  MIN2(size, reasonable_size);
-   }
-   return result;
-#endif
-}
-
 static hrtime_t first_hrtime = 0;
 static const hrtime_t hrtime_hz = 1000*1000*1000;
 const int LOCK_BUSY = 1;
--- a/src/os/windows/vm/os_windows.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/os/windows/vm/os_windows.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -686,12 +686,17 @@
   return win32::physical_memory();
 }
 
-julong os::allocatable_physical_memory(julong size) {
+bool os::has_allocatable_memory_limit(julong* limit) {
+  MEMORYSTATUSEX ms;
+  ms.dwLength = sizeof(ms);
+  GlobalMemoryStatusEx(&ms);
 #ifdef _LP64
-  return size;
+  *limit = (julong)ms.ullAvailVirtual;
+  return true;
 #else
   // Limit to 1400m because of the 2gb address space wall
-  return MIN2(size, (julong)1400*M);
+  *limit = MIN2((julong)1400*M, (julong)ms.ullAvailVirtual);
+  return true;
 #endif
 }
 
--- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -854,7 +854,8 @@
   assert(!isHumongous(word_size), "we do not allow humongous TLABs");
 
   unsigned int dummy_gc_count_before;
-  return attempt_allocation(word_size, &dummy_gc_count_before);
+  int dummy_gclocker_retry_count = 0;
+  return attempt_allocation(word_size, &dummy_gc_count_before, &dummy_gclocker_retry_count);
 }
 
 HeapWord*
@@ -863,14 +864,14 @@
   assert_heap_not_locked_and_not_at_safepoint();
 
   // Loop until the allocation is satisified, or unsatisfied after GC.
-  for (int try_count = 1; /* we'll return */; try_count += 1) {
+  for (int try_count = 1, gclocker_retry_count = 0; /* we'll return */; try_count += 1) {
     unsigned int gc_count_before;
 
     HeapWord* result = NULL;
     if (!isHumongous(word_size)) {
-      result = attempt_allocation(word_size, &gc_count_before);
+      result = attempt_allocation(word_size, &gc_count_before, &gclocker_retry_count);
     } else {
-      result = attempt_allocation_humongous(word_size, &gc_count_before);
+      result = attempt_allocation_humongous(word_size, &gc_count_before, &gclocker_retry_count);
     }
     if (result != NULL) {
       return result;
@@ -894,6 +895,9 @@
       }
       return result;
     } else {
+      if (gclocker_retry_count > GCLockerRetryAllocationCount) {
+        return NULL;
+      }
       assert(op.result() == NULL,
              "the result should be NULL if the VM op did not succeed");
     }
@@ -910,7 +914,8 @@
 }
 
 HeapWord* G1CollectedHeap::attempt_allocation_slow(size_t word_size,
-                                           unsigned int *gc_count_before_ret) {
+                                           unsigned int *gc_count_before_ret,
+                                           int* gclocker_retry_count_ret) {
   // Make sure you read the note in attempt_allocation_humongous().
 
   assert_heap_not_locked_and_not_at_safepoint();
@@ -986,10 +991,16 @@
         return NULL;
       }
     } else {
+      if (*gclocker_retry_count_ret > GCLockerRetryAllocationCount) {
+        MutexLockerEx x(Heap_lock);
+        *gc_count_before_ret = total_collections();
+        return NULL;
+      }
       // The GCLocker is either active or the GCLocker initiated
       // GC has not yet been performed. Stall until it is and
       // then retry the allocation.
       GC_locker::stall_until_clear();
+      (*gclocker_retry_count_ret) += 1;
     }
 
     // We can reach here if we were unsuccessul in scheduling a
@@ -1019,7 +1030,8 @@
 }
 
 HeapWord* G1CollectedHeap::attempt_allocation_humongous(size_t word_size,
-                                          unsigned int * gc_count_before_ret) {
+                                          unsigned int * gc_count_before_ret,
+                                          int* gclocker_retry_count_ret) {
   // The structure of this method has a lot of similarities to
   // attempt_allocation_slow(). The reason these two were not merged
   // into a single one is that such a method would require several "if
@@ -1104,10 +1116,16 @@
         return NULL;
       }
     } else {
+      if (*gclocker_retry_count_ret > GCLockerRetryAllocationCount) {
+        MutexLockerEx x(Heap_lock);
+        *gc_count_before_ret = total_collections();
+        return NULL;
+      }
       // The GCLocker is either active or the GCLocker initiated
       // GC has not yet been performed. Stall until it is and
       // then retry the allocation.
       GC_locker::stall_until_clear();
+      (*gclocker_retry_count_ret) += 1;
     }
 
     // We can reach here if we were unsuccessul in scheduling a
@@ -3270,12 +3288,12 @@
 
 void G1CollectedHeap::verify(bool silent,
                              VerifyOption vo) {
-  if (SafepointSynchronize::is_at_safepoint() || ! UseTLAB) {
+  if (SafepointSynchronize::is_at_safepoint()) {
     if (!silent) { gclog_or_tty->print("Roots "); }
     VerifyRootsClosure rootsCl(vo);
 
     assert(Thread::current()->is_VM_thread(),
-      "Expected to be executed serially by the VM thread at this point");
+           "Expected to be executed serially by the VM thread at this point");
 
     CodeBlobToOopClosure blobsCl(&rootsCl, /*do_marking=*/ false);
     VerifyKlassClosure klassCl(this, &rootsCl);
@@ -3360,7 +3378,8 @@
     }
     guarantee(!failures, "there should not have been any failures");
   } else {
-    if (!silent) gclog_or_tty->print("(SKIPPING roots, heapRegions, remset) ");
+    if (!silent)
+      gclog_or_tty->print("(SKIPPING roots, heapRegionSets, heapRegions, remset) ");
   }
 }
 
--- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp	Wed Apr 03 09:19:02 2013 +0200
@@ -559,18 +559,21 @@
   // the mutator alloc region without taking the Heap_lock. This
   // should only be used for non-humongous allocations.
   inline HeapWord* attempt_allocation(size_t word_size,
-                                      unsigned int* gc_count_before_ret);
+                                      unsigned int* gc_count_before_ret,
+                                      int* gclocker_retry_count_ret);
 
   // Second-level mutator allocation attempt: take the Heap_lock and
   // retry the allocation attempt, potentially scheduling a GC
   // pause. This should only be used for non-humongous allocations.
   HeapWord* attempt_allocation_slow(size_t word_size,
-                                    unsigned int* gc_count_before_ret);
+                                    unsigned int* gc_count_before_ret,
+                                    int* gclocker_retry_count_ret);
 
   // Takes the Heap_lock and attempts a humongous allocation. It can
   // potentially schedule a GC pause.
   HeapWord* attempt_allocation_humongous(size_t word_size,
-                                         unsigned int* gc_count_before_ret);
+                                         unsigned int* gc_count_before_ret,
+                                         int* gclocker_retry_count_ret);
 
   // Allocation attempt that should be called during safepoints (e.g.,
   // at the end of a successful GC). expect_null_mutator_alloc_region
--- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp	Wed Apr 03 09:19:02 2013 +0200
@@ -60,7 +60,8 @@
 
 inline HeapWord*
 G1CollectedHeap::attempt_allocation(size_t word_size,
-                                    unsigned int* gc_count_before_ret) {
+                                    unsigned int* gc_count_before_ret,
+                                    int* gclocker_retry_count_ret) {
   assert_heap_not_locked_and_not_at_safepoint();
   assert(!isHumongous(word_size), "attempt_allocation() should not "
          "be called for humongous allocation requests");
@@ -68,7 +69,9 @@
   HeapWord* result = _mutator_alloc_region.attempt_allocation(word_size,
                                                       false /* bot_updates */);
   if (result == NULL) {
-    result = attempt_allocation_slow(word_size, gc_count_before_ret);
+    result = attempt_allocation_slow(word_size,
+                                     gc_count_before_ret,
+                                     gclocker_retry_count_ret);
   }
   assert_heap_not_locked();
   if (result != NULL) {
--- a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -326,6 +326,7 @@
 
   uint loop_count = 0;
   uint gc_count = 0;
+  int gclocker_stalled_count = 0;
 
   while (result == NULL) {
     // We don't want to have multiple collections for a single filled generation.
@@ -354,6 +355,10 @@
         return result;
       }
 
+      if (gclocker_stalled_count > GCLockerRetryAllocationCount) {
+        return NULL;
+      }
+
       // Failed to allocate without a gc.
       if (GC_locker::is_active_and_needs_gc()) {
         // If this thread is not in a jni critical section, we stall
@@ -366,6 +371,7 @@
         if (!jthr->in_critical()) {
           MutexUnlocker mul(Heap_lock);
           GC_locker::stall_until_clear();
+          gclocker_stalled_count += 1;
           continue;
         } else {
           if (CheckJNICalls) {
--- a/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -256,7 +256,7 @@
   }
 
   if (PrintGC && Verbose) {
-    if (success && GC_locker::is_active()) {
+    if (success && GC_locker::is_active_and_needs_gc()) {
       gclog_or_tty->print_cr("Garbage collection disabled, expanded heap instead");
     }
   }
--- a/src/share/vm/memory/collectorPolicy.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/memory/collectorPolicy.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -532,7 +532,7 @@
 
   // Loop until the allocation is satisified,
   // or unsatisfied after GC.
-  for (int try_count = 1; /* return or throw */; try_count += 1) {
+  for (int try_count = 1, gclocker_stalled_count = 0; /* return or throw */; try_count += 1) {
     HandleMark hm; // discard any handles allocated in each iteration
 
     // First allocation attempt is lock-free.
@@ -576,6 +576,10 @@
           }
         }
 
+        if (gclocker_stalled_count > GCLockerRetryAllocationCount) {
+          return NULL; // we didn't get to do a GC and we didn't get any memory
+        }
+
         // If this thread is not in a jni critical section, we stall
         // the requestor until the critical section has cleared and
         // GC allowed. When the critical section clears, a GC is
@@ -587,6 +591,7 @@
           MutexUnlocker mul(Heap_lock);
           // Wait for JNI critical section to be exited
           GC_locker::stall_until_clear();
+          gclocker_stalled_count += 1;
           continue;
         } else {
           if (CheckJNICalls) {
--- a/src/share/vm/memory/universe.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/memory/universe.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -953,15 +953,6 @@
 void universe2_init() {
   EXCEPTION_MARK;
   Universe::genesis(CATCH);
-  // Although we'd like to verify here that the state of the heap
-  // is good, we can't because the main thread has not yet added
-  // itself to the threads list (so, using current interfaces
-  // we can't "fill" its TLAB), unless TLABs are disabled.
-  if (VerifyBeforeGC && !UseTLAB &&
-      Universe::heap()->total_collections() >= VerifyGCStartAt) {
-     Universe::heap()->prepare_for_verify();
-     Universe::verify();   // make sure we're starting with a clean slate
-  }
 }
 
 
--- a/src/share/vm/runtime/arguments.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/runtime/arguments.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -1553,6 +1553,15 @@
   }
 }
 
+julong Arguments::limit_by_allocatable_memory(julong limit) {
+  julong max_allocatable;
+  julong result = limit;
+  if (os::has_allocatable_memory_limit(&max_allocatable)) {
+    result = MIN2(result, max_allocatable / MaxVirtMemFraction);
+  }
+  return result;
+}
+
 void Arguments::set_heap_size() {
   if (!FLAG_IS_DEFAULT(DefaultMaxRAMFraction)) {
     // Deprecated flag
@@ -1591,12 +1600,12 @@
       }
       reasonable_max = MIN2(reasonable_max, max_coop_heap);
     }
-    reasonable_max = os::allocatable_physical_memory(reasonable_max);
+    reasonable_max = limit_by_allocatable_memory(reasonable_max);
 
     if (!FLAG_IS_DEFAULT(InitialHeapSize)) {
       // An initial heap size was specified on the command line,
       // so be sure that the maximum size is consistent.  Done
-      // after call to allocatable_physical_memory because that
+      // after call to limit_by_allocatable_memory because that
       // method might reduce the allocation size.
       reasonable_max = MAX2(reasonable_max, (julong)InitialHeapSize);
     }
@@ -1616,14 +1625,14 @@
 
     reasonable_minimum = MIN2(reasonable_minimum, (julong)MaxHeapSize);
 
-    reasonable_minimum = os::allocatable_physical_memory(reasonable_minimum);
+    reasonable_minimum = limit_by_allocatable_memory(reasonable_minimum);
 
     julong reasonable_initial = phys_mem / InitialRAMFraction;
 
     reasonable_initial = MAX2(reasonable_initial, reasonable_minimum);
     reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize);
 
-    reasonable_initial = os::allocatable_physical_memory(reasonable_initial);
+    reasonable_initial = limit_by_allocatable_memory(reasonable_initial);
 
     if (PrintGCDetails && Verbose) {
       // Cannot use gclog_or_tty yet.
@@ -2609,9 +2618,7 @@
       initHeapSize = MIN2(total_memory / (julong)2,
                           total_memory - (julong)160*M);
 
-      // Make sure that if we have a lot of memory we cap the 32 bit
-      // process space.  The 64bit VM version of this function is a nop.
-      initHeapSize = os::allocatable_physical_memory(initHeapSize);
+      initHeapSize = limit_by_allocatable_memory(initHeapSize);
 
       if (FLAG_IS_DEFAULT(MaxHeapSize)) {
          FLAG_SET_CMDLINE(uintx, MaxHeapSize, initHeapSize);
--- a/src/share/vm/runtime/arguments.hpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/runtime/arguments.hpp	Wed Apr 03 09:19:02 2013 +0200
@@ -312,6 +312,9 @@
   static void set_use_compressed_oops();
   static void set_ergonomics_flags();
   static void set_shared_spaces_flags();
+  // limits the given memory size by the maximum amount of memory this process is
+  // currently allowed to allocate or reserve.
+  static julong limit_by_allocatable_memory(julong size);
   // Setup heap size
   static void set_heap_size();
   // Based on automatic selection criteria, should the
--- a/src/share/vm/runtime/globals.hpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/runtime/globals.hpp	Wed Apr 03 09:19:02 2013 +0200
@@ -1404,6 +1404,10 @@
           "How much the GC can expand the eden by while the GC locker  "    \
           "is active (as a percentage)")                                    \
                                                                             \
+  diagnostic(intx, GCLockerRetryAllocationCount, 2,                         \
+          "Number of times to retry allocations when"                       \
+          " blocked by the GC locker")                                      \
+                                                                            \
   develop(bool, UseCMSAdaptiveFreeLists, true,                              \
           "Use Adaptive Free Lists in the CMS generation")                  \
                                                                             \
@@ -1960,6 +1964,10 @@
   product(uintx, InitialRAMFraction, 64,                                    \
           "Fraction (1/n) of real memory used for initial heap size")       \
                                                                             \
+  develop(uintx, MaxVirtMemFraction, 2,                                     \
+          "Maximum fraction (1/n) of virtual memory used for ergonomically" \
+          "determining maximum heap size")                                  \
+                                                                            \
   product(bool, UseAutoGCSelectPolicy, false,                               \
           "Use automatic collection selection policy")                      \
                                                                             \
--- a/src/share/vm/runtime/init.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/runtime/init.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -132,15 +132,6 @@
   javaClasses_init();   // must happen after vtable initialization
   stubRoutines_init2(); // note: StubRoutines need 2-phase init
 
-  // Although we'd like to, we can't easily do a heap verify
-  // here because the main thread isn't yet a JavaThread, so
-  // its TLAB may not be made parseable from the usual interfaces.
-  if (VerifyBeforeGC && !UseTLAB &&
-      Universe::heap()->total_collections() >= VerifyGCStartAt) {
-    Universe::heap()->prepare_for_verify();
-    Universe::verify();   // make sure we're starting with a clean slate
-  }
-
   // All the flags that get adjusted by VM_Version_init and os::init_2
   // have been set so dump the flags now.
   if (PrintFlagsFinal) {
--- a/src/share/vm/runtime/os.hpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/runtime/os.hpp	Wed Apr 03 09:19:02 2013 +0200
@@ -184,7 +184,7 @@
   }
   static julong available_memory();
   static julong physical_memory();
-  static julong allocatable_physical_memory(julong size);
+  static bool has_allocatable_memory_limit(julong* limit);
   static bool is_server_class_machine();
 
   // number of CPUs
--- a/src/share/vm/runtime/thread.cpp	Thu Mar 28 19:13:22 2013 -0700
+++ b/src/share/vm/runtime/thread.cpp	Wed Apr 03 09:19:02 2013 +0200
@@ -3423,12 +3423,6 @@
   // real raw monitor. VM is setup enough here for raw monitor enter.
   JvmtiExport::transition_pending_onload_raw_monitors();
 
-  if (VerifyBeforeGC &&
-      Universe::heap()->total_collections() >= VerifyGCStartAt) {
-    Universe::heap()->prepare_for_verify();
-    Universe::verify();   // make sure we're starting with a clean slate
-  }
-
   // Fully start NMT
   MemTracker::start();
 
@@ -3452,6 +3446,11 @@
   }
 
   assert (Universe::is_fully_initialized(), "not initialized");
+  if (VerifyBeforeGC && VerifyGCStartAt == 0) {
+    Universe::heap()->prepare_for_verify();
+    Universe::verify();   // make sure we're starting with a clean slate
+  }
+
   EXCEPTION_MARK;
 
   // At this point, the Universe is initialized, but we have not executed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/TestVerifyBeforeGCDuringStartup.java	Wed Apr 03 09:19:02 2013 +0200
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/* @test TestVerifyBeforeGCDuringStartup.java
+ * @key gc
+ * @bug 8010463
+ * @summary Simple test run with -XX:+VerifyBeforeGC -XX:-UseTLAB to verify 8010463
+ * @library /testlibrary
+ */
+
+import com.oracle.java.testlibrary.OutputAnalyzer;
+import com.oracle.java.testlibrary.ProcessTools;
+
+public class TestVerifyBeforeGCDuringStartup {
+  public static void main(String args[]) throws Exception {
+    ProcessBuilder pb =
+      ProcessTools.createJavaProcessBuilder(System.getProperty("test.vm.opts"),
+                                            "-XX:-UseTLAB",
+                                            "-XX:+UnlockDiagnosticVMOptions",
+                                            "-XX:+VerifyBeforeGC", "-version");
+    OutputAnalyzer output = new OutputAnalyzer(pb.start());
+    output.shouldContain("[Verifying");
+    output.shouldHaveExitValue(0);
+  }
+}
--- a/test/gc/metaspace/ClassMetaspaceSizeInJmapHeap.java	Thu Mar 28 19:13:22 2013 -0700
+++ b/test/gc/metaspace/ClassMetaspaceSizeInJmapHeap.java	Wed Apr 03 09:19:02 2013 +0200
@@ -39,8 +39,10 @@
     public static void main(String[] args) throws Exception {
         String pid = Integer.toString(ProcessTools.getProcessId());
 
-        ProcessBuilder pb = new ProcessBuilder();
-        pb.command(JDKToolFinder.getJDKTool("jmap"), "-heap",  pid);
+        JDKToolLauncher jmap = JDKToolLauncher.create("jmap")
+                                              .addToolArg("-heap")
+                                              .addToolArg(pid);
+        ProcessBuilder pb = new ProcessBuilder(jmap.getCommand());
 
         File out = new File("ClassMetaspaceSizeInJmapHeap.stdout.txt");
         pb.redirectOutput(out);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/JDKToolLauncher.java	Wed Apr 03 09:19:02 2013 +0200
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+package com.oracle.java.testlibrary;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import com.oracle.java.testlibrary.JDKToolFinder;
+import com.oracle.java.testlibrary.ProcessTools;
+
+/**
+ * A utility for constructing command lines for starting JDK tool processes.
+ *
+ * The JDKToolLauncher can in particular be combined with a
+ * java.lang.ProcessBuilder to easily run a JDK tool. For example, the
+ * following code run {@code jmap -heap} against a process with GC logging
+ * turned on for the {@code jmap} process:
+ *
+ * <pre>
+ * {@code
+ * JDKToolLauncher jmap = JDKToolLauncher.create("jmap")
+ *                                       .addVMArg("-XX:+PrintGC");
+ *                                       .addVMArg("-XX:+PrintGCDetails")
+ *                                       .addToolArg("-heap")
+ *                                       .addToolArg(pid);
+ * ProcessBuilder pb = new ProcessBuilder(jmap.getCommand());
+ * Process p = pb.start();
+ * }
+ * </pre>
+ */
+public class JDKToolLauncher {
+    private final String executable;
+    private final List<String> vmArgs = new ArrayList<String>();
+    private final List<String> toolArgs = new ArrayList<String>();
+
+    private JDKToolLauncher(String tool) {
+        executable = JDKToolFinder.getJDKTool(tool);
+        vmArgs.addAll(Arrays.asList(ProcessTools.getPlatformSpecificVMArgs()));
+    }
+
+    /**
+     * Creates a new JDKToolLauncher for the specified tool.
+     *
+     * @param tool The name of the tool
+     * @return A new JDKToolLauncher
+     */
+    public static JDKToolLauncher create(String tool) {
+        return new JDKToolLauncher(tool);
+    }
+
+    /**
+     * Adds an argument to the JVM running the tool.
+     *
+     * The JVM arguments are passed to the underlying JVM running the tool.
+     * Arguments will automatically be prepended with "-J".
+     *
+     * Any platform specific arguments required for running the tool are
+     * automatically added.
+     *
+     *
+     * @param arg The argument to VM running the tool
+     * @return The JDKToolLauncher instance
+     */
+    public JDKToolLauncher addVMArg(String arg) {
+        vmArgs.add("-J" + arg);
+        return this;
+    }
+
+    /**
+     * Adds an argument to the tool.
+     *
+     * @param arg The argument to the tool
+     * @return The JDKToolLauncher instance
+     */
+    public JDKToolLauncher addToolArg(String arg) {
+        toolArgs.add(arg);
+        return this;
+    }
+
+    /**
+     * Returns the command that can be used for running the tool.
+     *
+     * @return An array whose elements are the arguments of the command.
+     */
+    public String[] getCommand() {
+        List<String> command = new ArrayList<String>();
+        command.add(executable);
+        command.addAll(vmArgs);
+        command.addAll(toolArgs);
+        return command.toArray(new String[command.size()]);
+    }
+}