changeset 96:2acabb781f53

Merge
author apetrusenko
date Mon, 07 Apr 2008 09:32:49 -0700
parents d3cd40645d0d d05ebaf00ed0
children f38a25e2458a 7747916a0945
files
diffstat 4 files changed, 219 insertions(+), 130 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp	Tue Apr 01 16:14:18 2008 -0700
+++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp	Mon Apr 07 09:32:49 2008 -0700
@@ -225,6 +225,34 @@
   assert(_dilatation_factor >= 1.0, "from previous assert");
 }
 
+
+// The field "_initiating_occupancy" represents the occupancy percentage
+// at which we trigger a new collection cycle.  Unless explicitly specified
+// via CMSInitiating[Perm]OccupancyFraction (argument "io" below), it
+// is calculated by:
+//
+//   Let "f" be MinHeapFreeRatio in
+//
+//    _intiating_occupancy = 100-f +
+//                           f * (CMSTrigger[Perm]Ratio/100)
+//   where CMSTrigger[Perm]Ratio is the argument "tr" below.
+//
+// That is, if we assume the heap is at its desired maximum occupancy at the
+// end of a collection, we let CMSTrigger[Perm]Ratio of the (purported) free
+// space be allocated before initiating a new collection cycle.
+//
+void ConcurrentMarkSweepGeneration::init_initiating_occupancy(intx io, intx tr) {
+  assert(io <= 100 && tr >= 0 && tr <= 100, "Check the arguments");
+  if (io >= 0) {
+    _initiating_occupancy = (double)io / 100.0;
+  } else {
+    _initiating_occupancy = ((100 - MinHeapFreeRatio) +
+                             (double)(tr * MinHeapFreeRatio) / 100.0)
+                            / 100.0;
+  }
+}
+
+
 void ConcurrentMarkSweepGeneration::ref_processor_init() {
   assert(collector() != NULL, "no collector");
   collector()->ref_processor_init();
@@ -520,8 +548,8 @@
   _verification_mark_bm(0, Mutex::leaf + 1, "CMS_verification_mark_bm_lock"),
   _completed_initialization(false),
   _collector_policy(cp),
-  _unload_classes(false),
-  _unloaded_classes_last_cycle(false),
+  _should_unload_classes(false),
+  _concurrent_cycles_since_last_unload(0),
   _sweep_estimate(CMS_SweepWeight, CMS_SweepPadding)
 {
   if (ExplicitGCInvokesConcurrentAndUnloadsClasses) {
@@ -642,26 +670,11 @@
     }
   }
 
-  // "initiatingOccupancy" is the occupancy ratio at which we trigger
-  // a new collection cycle.  Unless explicitly specified via
-  // CMSTriggerRatio, it is calculated by:
-  //   Let "f" be MinHeapFreeRatio in
-  //
-  //    intiatingOccupancy = 100-f +
-  //                         f * (CMSTriggerRatio/100)
-  // That is, if we assume the heap is at its desired maximum occupancy at the
-  // end of a collection, we let CMSTriggerRatio of the (purported) free
-  // space be allocated before initiating a new collection cycle.
-  if (CMSInitiatingOccupancyFraction > 0) {
-    _initiatingOccupancy = (double)CMSInitiatingOccupancyFraction / 100.0;
-  } else {
-    _initiatingOccupancy = ((100 - MinHeapFreeRatio) +
-                           (double)(CMSTriggerRatio *
-                                    MinHeapFreeRatio) / 100.0)
-                           / 100.0;
-  }
+  _cmsGen ->init_initiating_occupancy(CMSInitiatingOccupancyFraction, CMSTriggerRatio);
+  _permGen->init_initiating_occupancy(CMSInitiatingPermOccupancyFraction, CMSTriggerPermRatio);
+
   // Clip CMSBootstrapOccupancy between 0 and 100.
-  _bootstrap_occupancy = ((double)MIN2((intx)100, MAX2((intx)0, CMSBootstrapOccupancy)))
+  _bootstrap_occupancy = ((double)MIN2((uintx)100, MAX2((uintx)0, CMSBootstrapOccupancy)))
                          /(double)100;
 
   _full_gcs_since_conc_gc = 0;
@@ -1413,7 +1426,8 @@
     gclog_or_tty->print_cr("promotion_rate=%g", stats().promotion_rate());
     gclog_or_tty->print_cr("cms_allocation_rate=%g", stats().cms_allocation_rate());
     gclog_or_tty->print_cr("occupancy=%3.7f", _cmsGen->occupancy());
-    gclog_or_tty->print_cr("initiatingOccupancy=%3.7f", initiatingOccupancy());
+    gclog_or_tty->print_cr("initiatingOccupancy=%3.7f", _cmsGen->initiating_occupancy());
+    gclog_or_tty->print_cr("initiatingPermOccupancy=%3.7f", _permGen->initiating_occupancy());
   }
   // ------------------------------------------------------------------
 
@@ -1446,84 +1460,91 @@
   // old gen want a collection cycle started. Each may use
   // an appropriate criterion for making this decision.
   // XXX We need to make sure that the gen expansion
-  // criterion dovetails well with this.
-  if (_cmsGen->shouldConcurrentCollect(initiatingOccupancy())) {
+  // criterion dovetails well with this. XXX NEED TO FIX THIS
+  if (_cmsGen->should_concurrent_collect()) {
     if (Verbose && PrintGCDetails) {
       gclog_or_tty->print_cr("CMS old gen initiated");
     }
     return true;
   }
 
-  if (cms_should_unload_classes() &&
-      _permGen->shouldConcurrentCollect(initiatingOccupancy())) {
-    if (Verbose && PrintGCDetails) {
-     gclog_or_tty->print_cr("CMS perm gen initiated");
-    }
-    return true;
-  }
-
-  return false;
-}
-
-// Clear _expansion_cause fields of constituent generations
-void CMSCollector::clear_expansion_cause() {
-  _cmsGen->clear_expansion_cause();
-  _permGen->clear_expansion_cause();
-}
-
-bool ConcurrentMarkSweepGeneration::shouldConcurrentCollect(
-  double initiatingOccupancy) {
-  // We should be conservative in starting a collection cycle.  To
-  // start too eagerly runs the risk of collecting too often in the
-  // extreme.  To collect too rarely falls back on full collections,
-  // which works, even if not optimum in terms of concurrent work.
-  // As a work around for too eagerly collecting, use the flag
-  // UseCMSInitiatingOccupancyOnly.  This also has the advantage of
-  // giving the user an easily understandable way of controlling the
-  // collections.
-  // We want to start a new collection cycle if any of the following
-  // conditions hold:
-  // . our current occupancy exceeds the initiating occupancy, or
-  // . we recently needed to expand and have not since that expansion,
-  //   collected, or
-  // . we are not using adaptive free lists and linear allocation is
-  //   going to fail, or
-  // . (for old gen) incremental collection has already failed or
-  //   may soon fail in the near future as we may not be able to absorb
-  //   promotions.
-  assert_lock_strong(freelistLock());
-
-  if (occupancy() > initiatingOccupancy) {
-    if (PrintGCDetails && Verbose) {
-      gclog_or_tty->print(" %s: collect because of occupancy %f / %f  ",
-        short_name(), occupancy(), initiatingOccupancy);
-    }
-    return true;
-  }
-  if (UseCMSInitiatingOccupancyOnly) {
-    return false;
-  }
-  if (expansion_cause() == CMSExpansionCause::_satisfy_allocation) {
-    if (PrintGCDetails && Verbose) {
-      gclog_or_tty->print(" %s: collect because expanded for allocation ",
-        short_name());
-    }
-    return true;
-  }
+  // We start a collection if we believe an incremental collection may fail;
+  // this is not likely to be productive in practice because it's probably too
+  // late anyway.
   GenCollectedHeap* gch = GenCollectedHeap::heap();
   assert(gch->collector_policy()->is_two_generation_policy(),
          "You may want to check the correctness of the following");
   if (gch->incremental_collection_will_fail()) {
     if (PrintGCDetails && Verbose) {
-      gclog_or_tty->print(" %s: collect because incremental collection will fail ",
+      gclog_or_tty->print("CMSCollector: collect because incremental collection will fail ");
+    }
+    return true;
+  }
+
+  if (CMSClassUnloadingEnabled && _permGen->should_concurrent_collect()) {
+    bool res = update_should_unload_classes();
+    if (res) {
+      if (Verbose && PrintGCDetails) {
+        gclog_or_tty->print_cr("CMS perm gen initiated");
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+// Clear _expansion_cause fields of constituent generations
+void CMSCollector::clear_expansion_cause() {
+  _cmsGen->clear_expansion_cause();
+  _permGen->clear_expansion_cause();
+}
+
+// We should be conservative in starting a collection cycle.  To
+// start too eagerly runs the risk of collecting too often in the
+// extreme.  To collect too rarely falls back on full collections,
+// which works, even if not optimum in terms of concurrent work.
+// As a work around for too eagerly collecting, use the flag
+// UseCMSInitiatingOccupancyOnly.  This also has the advantage of
+// giving the user an easily understandable way of controlling the
+// collections.
+// We want to start a new collection cycle if any of the following
+// conditions hold:
+// . our current occupancy exceeds the configured initiating occupancy
+//   for this generation, or
+// . we recently needed to expand this space and have not, since that
+//   expansion, done a collection of this generation, or
+// . the underlying space believes that it may be a good idea to initiate
+//   a concurrent collection (this may be based on criteria such as the
+//   following: the space uses linear allocation and linear allocation is
+//   going to fail, or there is believed to be excessive fragmentation in
+//   the generation, etc... or ...
+// [.(currently done by CMSCollector::shouldConcurrentCollect() only for
+//   the case of the old generation, not the perm generation; see CR 6543076):
+//   we may be approaching a point at which allocation requests may fail because
+//   we will be out of sufficient free space given allocation rate estimates.]
+bool ConcurrentMarkSweepGeneration::should_concurrent_collect() const {
+
+  assert_lock_strong(freelistLock());
+  if (occupancy() > initiating_occupancy()) {
+    if (PrintGCDetails && Verbose) {
+      gclog_or_tty->print(" %s: collect because of occupancy %f / %f  ",
+        short_name(), occupancy(), initiating_occupancy());
+    }
+    return true;
+  }
+  if (UseCMSInitiatingOccupancyOnly) {
+    return false;
+  }
+  if (expansion_cause() == CMSExpansionCause::_satisfy_allocation) {
+    if (PrintGCDetails && Verbose) {
+      gclog_or_tty->print(" %s: collect because expanded for allocation ",
         short_name());
     }
     return true;
   }
-  if (!_cmsSpace->adaptive_freelists() &&
-      _cmsSpace->linearAllocationWouldFail()) {
+  if (_cmsSpace->should_concurrent_collect()) {
     if (PrintGCDetails && Verbose) {
-      gclog_or_tty->print(" %s: collect because of linAB ",
+      gclog_or_tty->print(" %s: collect because cmsSpace says so ",
         short_name());
     }
     return true;
@@ -1970,8 +1991,9 @@
          "Should have been NULL'd before baton was passed");
   reset(false /* == !asynch */);
   _cmsGen->reset_after_compaction();
-
-  if (verifying() && !cms_should_unload_classes()) {
+  _concurrent_cycles_since_last_unload = 0;
+
+  if (verifying() && !should_unload_classes()) {
     perm_gen_verify_bit_map()->clear_all();
   }
 
@@ -2098,6 +2120,7 @@
   {
     bool safepoint_check = Mutex::_no_safepoint_check_flag;
     MutexLockerEx hl(Heap_lock, safepoint_check);
+    FreelistLocker fll(this);
     MutexLockerEx x(CGC_lock, safepoint_check);
     if (_foregroundGCIsActive || !UseAsyncConcMarkSweepGC) {
       // The foreground collector is active or we're
@@ -2112,13 +2135,9 @@
       // a new cycle.
       clear_expansion_cause();
     }
-    _unloaded_classes_last_cycle = cms_should_unload_classes(); // ... from last cycle
-    // This controls class unloading in response to an explicit gc request.
-    // If ExplicitGCInvokesConcurrentAndUnloadsClasses is set, then
-    // we will unload classes even if CMSClassUnloadingEnabled is not set.
-    // See CR 6541037 and related CRs.
-    _unload_classes = _full_gc_requested                      // ... for this cycle
-                      && ExplicitGCInvokesConcurrentAndUnloadsClasses;
+    // Decide if we want to enable class unloading as part of the
+    // ensuing concurrent GC cycle.
+    update_should_unload_classes();
     _full_gc_requested = false;           // acks all outstanding full gc requests
     // Signal that we are about to start a collection
     gch->increment_total_full_collections();  // ... starting a collection cycle
@@ -3047,21 +3066,62 @@
 }
 #endif // PRODUCT
 
+// Decide if we want to enable class unloading as part of the
+// ensuing concurrent GC cycle. We will collect the perm gen and
+// unload classes if it's the case that:
+// (1) an explicit gc request has been made and the flag
+//     ExplicitGCInvokesConcurrentAndUnloadsClasses is set, OR
+// (2) (a) class unloading is enabled at the command line, and
+//     (b) (i)   perm gen threshold has been crossed, or
+//         (ii)  old gen is getting really full, or
+//         (iii) the previous N CMS collections did not collect the
+//               perm gen
+// NOTE: Provided there is no change in the state of the heap between
+// calls to this method, it should have idempotent results. Moreover,
+// its results should be monotonically increasing (i.e. going from 0 to 1,
+// but not 1 to 0) between successive calls between which the heap was
+// not collected. For the implementation below, it must thus rely on
+// the property that concurrent_cycles_since_last_unload()
+// will not decrease unless a collection cycle happened and that
+// _permGen->should_concurrent_collect() and _cmsGen->is_too_full() are
+// themselves also monotonic in that sense. See check_monotonicity()
+// below.
+bool CMSCollector::update_should_unload_classes() {
+  _should_unload_classes = false;
+  // Condition 1 above
+  if (_full_gc_requested && ExplicitGCInvokesConcurrentAndUnloadsClasses) {
+    _should_unload_classes = true;
+  } else if (CMSClassUnloadingEnabled) { // Condition 2.a above
+    // Disjuncts 2.b.(i,ii,iii) above
+    _should_unload_classes = (concurrent_cycles_since_last_unload() >=
+                              CMSClassUnloadingMaxInterval)
+                           || _permGen->should_concurrent_collect()
+                           || _cmsGen->is_too_full();
+  }
+  return _should_unload_classes;
+}
+
+bool ConcurrentMarkSweepGeneration::is_too_full() const {
+  bool res = should_concurrent_collect();
+  res = res && (occupancy() > (double)CMSIsTooFullPercentage/100.0);
+  return res;
+}
+
 void CMSCollector::setup_cms_unloading_and_verification_state() {
   const  bool should_verify =    VerifyBeforeGC || VerifyAfterGC || VerifyDuringGC
                              || VerifyBeforeExit;
   const  int  rso           =    SharedHeap::SO_Symbols | SharedHeap::SO_Strings
                              |   SharedHeap::SO_CodeCache;
 
-  if (cms_should_unload_classes()) {   // Should unload classes this cycle
+  if (should_unload_classes()) {   // Should unload classes this cycle
     remove_root_scanning_option(rso);  // Shrink the root set appropriately
     set_verifying(should_verify);    // Set verification state for this cycle
     return;                            // Nothing else needs to be done at this time
   }
 
   // Not unloading classes this cycle
-  assert(!cms_should_unload_classes(), "Inconsitency!");
-  if ((!verifying() || cms_unloaded_classes_last_cycle()) && should_verify) {
+  assert(!should_unload_classes(), "Inconsitency!");
+  if ((!verifying() || unloaded_classes_last_cycle()) && should_verify) {
     // We were not verifying, or we _were_ unloading classes in the last cycle,
     // AND some verification options are enabled this cycle; in this case,
     // we must make sure that the deadness map is allocated if not already so,
@@ -4693,7 +4753,7 @@
 
   GenCollectedHeap* gch = GenCollectedHeap::heap();
 
-  if (cms_should_unload_classes()) {
+  if (should_unload_classes()) {
     CodeCache::gc_prologue();
   }
   assert(haveFreelistLocks(), "must have free list locks");
@@ -4753,7 +4813,7 @@
   verify_work_stacks_empty();
   verify_overflow_empty();
 
-  if (cms_should_unload_classes()) {
+  if (should_unload_classes()) {
     CodeCache::gc_epilogue();
   }
 
@@ -5623,7 +5683,7 @@
     verify_work_stacks_empty();
   }
 
-  if (cms_should_unload_classes()) {
+  if (should_unload_classes()) {
     {
       TraceTime t("class unloading", PrintGCDetails, false, gclog_or_tty);
 
@@ -5726,7 +5786,7 @@
   // this cycle, we preserve the perm gen object "deadness" information
   // in the perm_gen_verify_bit_map. In order to do that we traverse
   // all blocks in perm gen and mark all dead objects.
-  if (verifying() && !cms_should_unload_classes()) {
+  if (verifying() && !should_unload_classes()) {
     assert(perm_gen_verify_bit_map()->sizeInBits() != 0,
            "Should have already been allocated");
     MarkDeadObjectsClosure mdo(this, _permGen->cmsSpace(),
@@ -5753,7 +5813,7 @@
     }
 
     // Now repeat for perm gen
-    if (cms_should_unload_classes()) {
+    if (should_unload_classes()) {
       CMSTokenSyncWithLocks ts(true, _permGen->freelistLock(),
                              bitMapLock());
       sweepWork(_permGen, asynch);
@@ -5775,7 +5835,7 @@
     // already have needed locks
     sweepWork(_cmsGen,  asynch);
 
-    if (cms_should_unload_classes()) {
+    if (should_unload_classes()) {
       sweepWork(_permGen, asynch);
     }
     // Update heap occupancy information which is used as
@@ -5937,6 +5997,11 @@
   }
   gen->cmsSpace()->sweep_completed();
   gen->cmsSpace()->endSweepFLCensus(sweepCount());
+  if (should_unload_classes()) {                // unloaded classes this cycle,
+    _concurrent_cycles_since_last_unload = 0;   // ... reset count
+  } else {                                      // did not unload classes,
+    _concurrent_cycles_since_last_unload++;     // ... increment count
+  }
 }
 
 // Reset CMS data structures (for now just the marking bit map)
@@ -7194,7 +7259,7 @@
   _revisitStack(revisitStack),
   _finger(finger),
   _parent(parent),
-  _should_remember_klasses(collector->cms_should_unload_classes())
+  _should_remember_klasses(collector->should_unload_classes())
 { }
 
 Par_PushOrMarkClosure::Par_PushOrMarkClosure(CMSCollector* collector,
@@ -7217,7 +7282,7 @@
   _finger(finger),
   _global_finger_addr(global_finger_addr),
   _parent(parent),
-  _should_remember_klasses(collector->cms_should_unload_classes())
+  _should_remember_klasses(collector->should_unload_classes())
 { }
 
 
@@ -7360,7 +7425,7 @@
   _mark_stack(mark_stack),
   _revisit_stack(revisit_stack),
   _concurrent_precleaning(concurrent_precleaning),
-  _should_remember_klasses(collector->cms_should_unload_classes())
+  _should_remember_klasses(collector->should_unload_classes())
 {
   assert(_ref_processor != NULL, "_ref_processor shouldn't be NULL");
 }
@@ -7422,7 +7487,7 @@
   _bit_map(bit_map),
   _work_queue(work_queue),
   _revisit_stack(revisit_stack),
-  _should_remember_klasses(collector->cms_should_unload_classes())
+  _should_remember_klasses(collector->should_unload_classes())
 {
   assert(_ref_processor != NULL, "_ref_processor shouldn't be NULL");
 }
@@ -7944,7 +8009,7 @@
 
     #ifdef DEBUG
       if (oop(addr)->klass() != NULL &&
-          (   !_collector->cms_should_unload_classes()
+          (   !_collector->should_unload_classes()
            || oop(addr)->is_parsable())) {
         // Ignore mark word because we are running concurrent with mutators
         assert(oop(addr)->is_oop(true), "live block should be an oop");
@@ -7957,7 +8022,7 @@
   } else {
     // This should be an initialized object that's alive.
     assert(oop(addr)->klass() != NULL &&
-           (!_collector->cms_should_unload_classes()
+           (!_collector->should_unload_classes()
             || oop(addr)->is_parsable()),
            "Should be an initialized object");
     // Ignore mark word because we are running concurrent with mutators
--- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp	Tue Apr 01 16:14:18 2008 -0700
+++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp	Mon Apr 07 09:32:49 2008 -0700
@@ -535,13 +535,16 @@
   // In support of ExplicitGCInvokesConcurrent
   static   bool _full_gc_requested;
   unsigned int  _collection_count_start;
+
   // Should we unload classes this concurrent cycle?
-  // Set in response to a concurrent full gc request.
-  bool _unload_classes;
-  bool _unloaded_classes_last_cycle;
+  bool _should_unload_classes;
+  unsigned int  _concurrent_cycles_since_last_unload;
+  unsigned int concurrent_cycles_since_last_unload() const {
+    return _concurrent_cycles_since_last_unload;
+  }
   // Did we (allow) unload classes in the previous concurrent cycle?
-  bool cms_unloaded_classes_last_cycle() const {
-    return _unloaded_classes_last_cycle || CMSClassUnloadingEnabled;
+  bool unloaded_classes_last_cycle() const {
+    return concurrent_cycles_since_last_unload() == 0;
   }
 
   // Verification support
@@ -651,8 +654,6 @@
   // number of full gc's since the last concurrent gc.
   uint   _full_gcs_since_conc_gc;
 
-  // if occupancy exceeds this, start a new gc cycle
-  double _initiatingOccupancy;
   // occupancy used for bootstrapping stats
   double _bootstrap_occupancy;
 
@@ -825,7 +826,6 @@
 
   Mutex* bitMapLock()        const { return _markBitMap.lock();    }
   static CollectorState abstract_state() { return _collectorState;  }
-  double initiatingOccupancy() const { return _initiatingOccupancy; }
 
   bool should_abort_preclean() const; // Whether preclean should be aborted.
   size_t get_eden_used() const;
@@ -849,11 +849,10 @@
   // In support of ExplicitGCInvokesConcurrent
   static void request_full_gc(unsigned int full_gc_count);
   // Should we unload classes in a particular concurrent cycle?
-  bool cms_should_unload_classes() const {
-    assert(!_unload_classes ||  ExplicitGCInvokesConcurrentAndUnloadsClasses,
-           "Inconsistency; see CR 6541037");
-    return _unload_classes || CMSClassUnloadingEnabled;
+  bool should_unload_classes() const {
+    return _should_unload_classes;
   }
+  bool update_should_unload_classes();
 
   void direct_allocated(HeapWord* start, size_t size);
 
@@ -1022,6 +1021,10 @@
     _incremental_collection_failed = false;
   }
 
+  // accessors
+  void set_expansion_cause(CMSExpansionCause::Cause v) { _expansion_cause = v;}
+  CMSExpansionCause::Cause expansion_cause() const { return _expansion_cause; }
+
  private:
   // For parallel young-gen GC support.
   CMSParGCThreadState** _par_gc_thread_states;
@@ -1029,10 +1032,6 @@
   // Reason generation was expanded
   CMSExpansionCause::Cause _expansion_cause;
 
-  // accessors
-  void set_expansion_cause(CMSExpansionCause::Cause v) { _expansion_cause = v;}
-  CMSExpansionCause::Cause expansion_cause() { return _expansion_cause; }
-
   // In support of MinChunkSize being larger than min object size
   const double _dilatation_factor;
 
@@ -1045,6 +1044,10 @@
 
   CollectionTypes _debug_collection_type;
 
+  // Fraction of current occupancy at which to start a CMS collection which
+  // will collect this generation (at least).
+  double _initiating_occupancy;
+
  protected:
   // Grow generation by specified size (returns false if unable to grow)
   bool grow_by(size_t bytes);
@@ -1060,6 +1063,10 @@
   // space.
   size_t max_available() const;
 
+  // getter and initializer for _initiating_occupancy field.
+  double initiating_occupancy() const { return _initiating_occupancy; }
+  void   init_initiating_occupancy(intx io, intx tr);
+
  public:
   ConcurrentMarkSweepGeneration(ReservedSpace rs, size_t initial_byte_size,
                                 int level, CardTableRS* ct,
@@ -1103,7 +1110,7 @@
   size_t capacity() const;
   size_t used() const;
   size_t free() const;
-  double occupancy()      { return ((double)used())/((double)capacity()); }
+  double occupancy() const { return ((double)used())/((double)capacity()); }
   size_t contiguous_available() const;
   size_t unsafe_max_alloc_nogc() const;
 
@@ -1158,8 +1165,8 @@
     bool younger_handles_promotion_failure) const;
 
   bool should_collect(bool full, size_t size, bool tlab);
-    // XXXPERM
-  bool shouldConcurrentCollect(double initiatingOccupancy); // XXXPERM
+  virtual bool should_concurrent_collect() const;
+  virtual bool is_too_full() const;
   void collect(bool   full,
                bool   clear_all_soft_refs,
                size_t size,
--- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp	Tue Apr 01 16:14:18 2008 -0700
+++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp	Mon Apr 07 09:32:49 2008 -0700
@@ -267,7 +267,7 @@
          (_permGen->cmsSpace()->is_in_reserved(addr)
           && _permGen->cmsSpace()->block_is_obj(addr)),
          "must be object");
-  return  cms_should_unload_classes() &&
+  return  should_unload_classes() &&
           _collectorState == Sweeping &&
          !_markBitMap.isMarked(addr);
 }
--- a/src/share/vm/runtime/globals.hpp	Tue Apr 01 16:14:18 2008 -0700
+++ b/src/share/vm/runtime/globals.hpp	Mon Apr 07 09:32:49 2008 -0700
@@ -1319,6 +1319,10 @@
   product(bool, CMSClassUnloadingEnabled, false,                            \
           "Whether class unloading enabled when using CMS GC")              \
                                                                             \
+  product(uintx, CMSClassUnloadingMaxInterval, 0,                           \
+          "When CMS class unloading is enabled, the maximum CMS cycle count"\
+          " for which classes may not be unloaded")                         \
+                                                                            \
   product(bool, CMSCompactWhenClearAllSoftRefs, true,                       \
           "Compact when asked to collect CMS gen with clear_all_soft_refs") \
                                                                             \
@@ -1504,17 +1508,30 @@
           "Percentage of MinHeapFreeRatio in CMS generation that is "       \
           "  allocated before a CMS collection cycle commences")            \
                                                                             \
-  product(intx, CMSBootstrapOccupancy, 50,                                  \
+  product(intx, CMSTriggerPermRatio, 80,                                    \
+          "Percentage of MinHeapFreeRatio in the CMS perm generation that"  \
+          "  is allocated before a CMS collection cycle commences, that  "  \
+          "  also collects the perm generation")                            \
+                                                                            \
+  product(uintx, CMSBootstrapOccupancy, 50,                                 \
           "Percentage CMS generation occupancy at which to "                \
           " initiate CMS collection for bootstrapping collection stats")    \
                                                                             \
   product(intx, CMSInitiatingOccupancyFraction, -1,                         \
           "Percentage CMS generation occupancy to start a CMS collection "  \
-          " cycle (A negative value means that CMSTirggerRatio is used)")   \
+          " cycle (A negative value means that CMSTriggerRatio is used)")   \
+                                                                            \
+  product(intx, CMSInitiatingPermOccupancyFraction, -1,                     \
+          "Percentage CMS perm generation occupancy to start a CMScollection"\
+          " cycle (A negative value means that CMSTriggerPermRatio is used)")\
                                                                             \
   product(bool, UseCMSInitiatingOccupancyOnly, false,                       \
           "Only use occupancy as a crierion for starting a CMS collection") \
                                                                             \
+  product(intx, CMSIsTooFullPercentage, 98,                                 \
+          "An absolute ceiling above which CMS will always consider the"    \
+          " perm gen ripe for collection")                                  \
+                                                                            \
   develop(bool, CMSTestInFreeList, false,                                   \
           "Check if the coalesced range is already in the "                 \
           "free lists as claimed.")                                         \