changeset 9038:4b8584c24ff4

8215934: G1 Old Gen MemoryPool CollectionUsage.used values don't reflect mixed GC results Summary: Memory pools can now be optional collection participants, e.g., G1 Old Gen in an incremental collection. Reviewed-by: tschatzl, jcbeyler, andrew
author phh
date Mon, 28 Jan 2019 17:51:10 +0000
parents a9ab35a0f5cb
children 28f68e5c6fb3
files src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp src/share/vm/services/memoryManager.cpp src/share/vm/services/memoryManager.hpp src/share/vm/services/memoryService.cpp src/share/vm/services/memoryService.hpp test/gc/TestMemoryMXBeansAndPoolsPresence.java test/gc/g1/mixedgc/TestOldGenCollectionUsage.java
diffstat 8 files changed, 420 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp	Tue Jan 08 04:55:25 2019 +0000
+++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp	Mon Jan 28 17:51:10 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -9541,6 +9541,7 @@
     case CMSCollector::InitialMarking:
       initialize(true  /* fullGC */ ,
                  cause /* cause of the GC */,
+                 true  /* allMemoryPoolsAffected */,
                  true  /* recordGCBeginTime */,
                  true  /* recordPreGCUsage */,
                  false /* recordPeakUsage */,
@@ -9553,6 +9554,7 @@
     case CMSCollector::FinalMarking:
       initialize(true  /* fullGC */ ,
                  cause /* cause of the GC */,
+                 true  /* allMemoryPoolsAffected */,
                  false /* recordGCBeginTime */,
                  false /* recordPreGCUsage */,
                  false /* recordPeakUsage */,
@@ -9565,6 +9567,7 @@
     case CMSCollector::Sweeping:
       initialize(true  /* fullGC */ ,
                  cause /* cause of the GC */,
+                 true  /* allMemoryPoolsAffected */,
                  false /* recordGCBeginTime */,
                  false /* recordPreGCUsage */,
                  true  /* recordPeakUsage */,
--- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Tue Jan 08 04:55:25 2019 +0000
+++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Mon Jan 28 17:51:10 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -4008,7 +4008,8 @@
     log_gc_header();
 
     TraceCollectorStats tcs(g1mm()->incremental_collection_counters());
-    TraceMemoryManagerStats tms(false /* fullGC */, gc_cause());
+    TraceMemoryManagerStats tms(false /* fullGC */, gc_cause(),
+                                yc_type() == Mixed /* allMemoryPoolsAffected */);
 
     // If the secondary_free_list is not empty, append it to the
     // free_list. No need to wait for the cleanup operation to finish;
--- a/src/share/vm/services/memoryManager.cpp	Tue Jan 08 04:55:25 2019 +0000
+++ b/src/share/vm/services/memoryManager.cpp	Mon Jan 28 17:51:10 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -49,13 +49,15 @@
   (void)const_cast<instanceOop&>(_memory_mgr_obj = instanceOop(NULL));
 }
 
-void MemoryManager::add_pool(MemoryPool* pool) {
-  assert(_num_pools < MemoryManager::max_num_pools, "_num_pools exceeds the max");
-  if (_num_pools < MemoryManager::max_num_pools) {
-    _pools[_num_pools] = pool;
+int MemoryManager::add_pool(MemoryPool* pool) {
+  int index = _num_pools;
+  assert(index < MemoryManager::max_num_pools, "_num_pools exceeds the max");
+  if (index < MemoryManager::max_num_pools) {
+    _pools[index] = pool;
     _num_pools++;
   }
   pool->add_manager(this);
+  return index;
 }
 
 MemoryManager* MemoryManager::get_code_cache_memory_manager() {
@@ -217,6 +219,15 @@
   delete _current_gc_stat;
 }
 
+void GCMemoryManager::add_pool(MemoryPool* pool) {
+  add_pool(pool, true);
+}
+
+void GCMemoryManager::add_pool(MemoryPool* pool, bool always_affected_by_gc) {
+  int index = MemoryManager::add_pool(pool);
+  _pool_always_affected_by_gc[index] = always_affected_by_gc;
+}
+
 void GCMemoryManager::initialize_gc_stat_info() {
   assert(MemoryService::num_memory_pools() > 0, "should have one or more memory pools");
   _last_gc_stat = new(ResourceObj::C_HEAP, mtGC) GCStatInfo(MemoryService::num_memory_pools());
@@ -266,7 +277,8 @@
 void GCMemoryManager::gc_end(bool recordPostGCUsage,
                              bool recordAccumulatedGCTime,
                              bool recordGCEndTime, bool countCollection,
-                             GCCause::Cause cause) {
+                             GCCause::Cause cause,
+                             bool allMemoryPoolsAffected) {
   if (recordAccumulatedGCTime) {
     _accumulated_timer.stop();
   }
@@ -304,8 +316,11 @@
       MemoryUsage usage = pool->get_memory_usage();
 
       // Compare with GC usage threshold
-      pool->set_last_collection_usage(usage);
-      LowMemoryDetector::detect_after_gc_memory(pool);
+      if (allMemoryPoolsAffected || pool_always_affected_by_gc(i)) {
+        // Compare with GC usage threshold
+        pool->set_last_collection_usage(usage);
+        LowMemoryDetector::detect_after_gc_memory(pool);
+      }
     }
   }
 
--- a/src/share/vm/services/memoryManager.hpp	Tue Jan 08 04:55:25 2019 +0000
+++ b/src/share/vm/services/memoryManager.hpp	Mon Jan 28 17:51:10 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -41,11 +41,12 @@
 class OopClosure;
 
 class MemoryManager : public CHeapObj<mtInternal> {
-private:
+protected:
   enum {
     max_num_pools = 10
   };
 
+private:
   MemoryPool* _pools[max_num_pools];
   int         _num_pools;
 
@@ -75,7 +76,7 @@
     return _pools[index];
   }
 
-  void add_pool(MemoryPool* pool);
+  int add_pool(MemoryPool* pool);
 
   bool is_manager(instanceHandle mh)     { return mh() == _memory_mgr_obj; }
 
@@ -177,10 +178,20 @@
   GCStatInfo*  _current_gc_stat;
   int          _num_gc_threads;
   volatile bool _notification_enabled;
+  bool         _pool_always_affected_by_gc[MemoryManager::max_num_pools];
+
 public:
   GCMemoryManager();
   ~GCMemoryManager();
 
+  void add_pool(MemoryPool* pool);
+  void add_pool(MemoryPool* pool, bool always_affected_by_gc);
+
+  bool pool_always_affected_by_gc(int index) {
+    assert(index >= 0 && index < num_memory_pools(), "Invalid index");
+    return _pool_always_affected_by_gc[index];
+  }
+
   void   initialize_gc_stat_info();
 
   bool   is_gc_memory_manager()         { return true; }
@@ -192,7 +203,8 @@
   void   gc_begin(bool recordGCBeginTime, bool recordPreGCUsage,
                   bool recordAccumulatedGCTime);
   void   gc_end(bool recordPostGCUsage, bool recordAccumulatedGCTime,
-                bool recordGCEndTime, bool countCollection, GCCause::Cause cause);
+                bool recordGCEndTime, bool countCollection, GCCause::Cause cause,
+                bool allMemoryPoolsAffected);
 
   void        reset_gc_stat()   { _num_collections = 0; _accumulated_timer.reset(); }
 
--- a/src/share/vm/services/memoryService.cpp	Tue Jan 08 04:55:25 2019 +0000
+++ b/src/share/vm/services/memoryService.cpp	Mon Jan 28 17:51:10 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -187,7 +187,7 @@
   _managers_list->append(_major_gc_manager);
 
   add_g1YoungGen_memory_pool(g1h, _major_gc_manager, _minor_gc_manager);
-  add_g1OldGen_memory_pool(g1h, _major_gc_manager);
+  add_g1OldGen_memory_pool(g1h, _major_gc_manager, _minor_gc_manager);
 }
 #endif // INCLUDE_ALL_GCS
 
@@ -241,8 +241,8 @@
 
 // Add memory pool(s) for one generation
 void MemoryService::add_generation_memory_pool(Generation* gen,
-                                               MemoryManager* major_mgr,
-                                               MemoryManager* minor_mgr) {
+                                               GCMemoryManager* major_mgr,
+                                               GCMemoryManager* minor_mgr) {
   guarantee(gen != NULL, "No generation for memory pool");
   Generation::Name kind = gen->kind();
   int index = _pools_list->length();
@@ -332,7 +332,9 @@
 
 
 #if INCLUDE_ALL_GCS
-void MemoryService::add_psYoung_memory_pool(PSYoungGen* gen, MemoryManager* major_mgr, MemoryManager* minor_mgr) {
+void MemoryService::add_psYoung_memory_pool(PSYoungGen* gen,
+                                            GCMemoryManager* major_mgr,
+                                            GCMemoryManager* minor_mgr) {
   assert(major_mgr != NULL && minor_mgr != NULL, "Should have two managers");
 
   // Add a memory pool for each space and young gen doesn't
@@ -356,7 +358,7 @@
   _pools_list->append(survivor);
 }
 
-void MemoryService::add_psOld_memory_pool(PSOldGen* gen, MemoryManager* mgr) {
+void MemoryService::add_psOld_memory_pool(PSOldGen* gen, GCMemoryManager* mgr) {
   PSGenerationPool* old_gen = new PSGenerationPool(gen,
                                                    "PS Old Gen",
                                                    MemoryPool::Heap,
@@ -366,8 +368,8 @@
 }
 
 void MemoryService::add_g1YoungGen_memory_pool(G1CollectedHeap* g1h,
-                                               MemoryManager* major_mgr,
-                                               MemoryManager* minor_mgr) {
+                                               GCMemoryManager* major_mgr,
+                                               GCMemoryManager* minor_mgr) {
   assert(major_mgr != NULL && minor_mgr != NULL, "should have two managers");
 
   G1EdenPool* eden = new G1EdenPool(g1h);
@@ -382,11 +384,13 @@
 }
 
 void MemoryService::add_g1OldGen_memory_pool(G1CollectedHeap* g1h,
-                                             MemoryManager* mgr) {
-  assert(mgr != NULL, "should have one manager");
+                                             GCMemoryManager* major_mgr,
+                                             GCMemoryManager* minor_mgr) {
+  assert(major_mgr != NULL && minor_mgr != NULL, "should have two managers");
 
   G1OldGenPool* old_gen = new G1OldGenPool(g1h);
-  mgr->add_pool(old_gen);
+  major_mgr->add_pool(old_gen);
+  minor_mgr->add_pool(old_gen, false /* always_affected_by_gc */);
   _pools_list->append(old_gen);
 }
 #endif // INCLUDE_ALL_GCS
@@ -484,7 +488,8 @@
 void MemoryService::gc_end(bool fullGC, bool recordPostGCUsage,
                            bool recordAccumulatedGCTime,
                            bool recordGCEndTime, bool countCollection,
-                           GCCause::Cause cause) {
+                           GCCause::Cause cause,
+                           bool allMemoryPoolsAffected) {
 
   GCMemoryManager* mgr;
   if (fullGC) {
@@ -496,7 +501,7 @@
 
   // register the GC end statistics and memory usage
   mgr->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
-              countCollection, cause);
+              countCollection, cause, allMemoryPoolsAffected);
 }
 
 void MemoryService::oops_do(OopClosure* f) {
@@ -573,10 +578,11 @@
   }
   // this has to be called in a stop the world pause and represent
   // an entire gc pause, start to finish:
-  initialize(_fullGC, cause,true, true, true, true, true, true, true);
+  initialize(_fullGC, cause, true, true, true, true, true, true, true, true);
 }
 TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC,
                                                  GCCause::Cause cause,
+                                                 bool allMemoryPoolsAffected,
                                                  bool recordGCBeginTime,
                                                  bool recordPreGCUsage,
                                                  bool recordPeakUsage,
@@ -584,7 +590,8 @@
                                                  bool recordAccumulatedGCTime,
                                                  bool recordGCEndTime,
                                                  bool countCollection) {
-    initialize(fullGC, cause, recordGCBeginTime, recordPreGCUsage, recordPeakUsage,
+  initialize(fullGC, cause, allMemoryPoolsAffected,
+             recordGCBeginTime, recordPreGCUsage, recordPeakUsage,
              recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
              countCollection);
 }
@@ -593,6 +600,7 @@
 // the MemoryService
 void TraceMemoryManagerStats::initialize(bool fullGC,
                                          GCCause::Cause cause,
+                                         bool allMemoryPoolsAffected,
                                          bool recordGCBeginTime,
                                          bool recordPreGCUsage,
                                          bool recordPeakUsage,
@@ -601,6 +609,7 @@
                                          bool recordGCEndTime,
                                          bool countCollection) {
   _fullGC = fullGC;
+  _allMemoryPoolsAffected = allMemoryPoolsAffected;
   _recordGCBeginTime = recordGCBeginTime;
   _recordPreGCUsage = recordPreGCUsage;
   _recordPeakUsage = recordPeakUsage;
@@ -616,5 +625,5 @@
 
 TraceMemoryManagerStats::~TraceMemoryManagerStats() {
   MemoryService::gc_end(_fullGC, _recordPostGCUsage, _recordAccumulatedGCTime,
-                        _recordGCEndTime, _countCollection, _cause);
+                        _recordGCEndTime, _countCollection, _cause, _allMemoryPoolsAffected);
 }
--- a/src/share/vm/services/memoryService.hpp	Tue Jan 08 04:55:25 2019 +0000
+++ b/src/share/vm/services/memoryService.hpp	Mon Jan 28 17:51:10 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -77,25 +77,26 @@
   static MemoryPool*                    _compressed_class_pool;
 
   static void add_generation_memory_pool(Generation* gen,
-                                         MemoryManager* major_mgr,
-                                         MemoryManager* minor_mgr);
+                                         GCMemoryManager* major_mgr,
+                                         GCMemoryManager* minor_mgr);
   static void add_generation_memory_pool(Generation* gen,
-                                         MemoryManager* major_mgr) {
+                                         GCMemoryManager* major_mgr) {
     add_generation_memory_pool(gen, major_mgr, NULL);
   }
 
 
   static void add_psYoung_memory_pool(PSYoungGen* gen,
-                                      MemoryManager* major_mgr,
-                                      MemoryManager* minor_mgr);
+                                      GCMemoryManager* major_mgr,
+                                      GCMemoryManager* minor_mgr);
   static void add_psOld_memory_pool(PSOldGen* gen,
-                                    MemoryManager* mgr);
+                                    GCMemoryManager* mgr);
 
   static void add_g1YoungGen_memory_pool(G1CollectedHeap* g1h,
-                                         MemoryManager* major_mgr,
-                                         MemoryManager* minor_mgr);
+                                         GCMemoryManager* major_mgr,
+                                         GCMemoryManager* minor_mgr);
   static void add_g1OldGen_memory_pool(G1CollectedHeap* g1h,
-                                       MemoryManager* mgr);
+                                       GCMemoryManager* major_mgr,
+                                       GCMemoryManager* minor_mgr);
 
   static MemoryPool* add_space(ContiguousSpace* space,
                                const char* name,
@@ -162,7 +163,8 @@
   static void gc_end(bool fullGC, bool recordPostGCUsage,
                      bool recordAccumulatedGCTime,
                      bool recordGCEndTime, bool countCollection,
-                     GCCause::Cause cause);
+                     GCCause::Cause cause,
+                     bool allMemoryPoolsAffected);
 
 
   static void oops_do(OopClosure* f);
@@ -185,6 +187,7 @@
 class TraceMemoryManagerStats : public StackObj {
 private:
   bool         _fullGC;
+  bool         _allMemoryPoolsAffected;
   bool         _recordGCBeginTime;
   bool         _recordPreGCUsage;
   bool         _recordPeakUsage;
@@ -197,6 +200,7 @@
   TraceMemoryManagerStats() {}
   TraceMemoryManagerStats(bool fullGC,
                           GCCause::Cause cause,
+                          bool allMemoryPoolsAffected = true,
                           bool recordGCBeginTime = true,
                           bool recordPreGCUsage = true,
                           bool recordPeakUsage = true,
@@ -207,6 +211,7 @@
 
   void initialize(bool fullGC,
                   GCCause::Cause cause,
+                  bool allMemoryPoolsAffected,
                   bool recordGCBeginTime,
                   bool recordPreGCUsage,
                   bool recordPeakUsage,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/TestMemoryMXBeansAndPoolsPresence.java	Mon Jan 28 17:51:10 2019 +0000
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017, 2019, 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 TestMemoryMXBeansAndPoolsPresence
+ * @key gc
+ * @bug 8191564
+ * @summary Tests that GarbageCollectorMXBeans and GC MemoryPools are created.
+ * @library /testlibrary
+ * @requires vm.gc == null
+ * @run main/othervm -XX:+UseParallelGC TestMemoryMXBeansAndPoolsPresence Parallel
+ * @run main/othervm -XX:+UseSerialGC TestMemoryMXBeansAndPoolsPresence Serial
+ * @run main/othervm -XX:+UseConcMarkSweepGC TestMemoryMXBeansAndPoolsPresence CMS
+ * @run main/othervm -XX:+UseG1GC TestMemoryMXBeansAndPoolsPresence G1
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import java.lang.management.*;
+import java.util.stream.*;
+
+import com.oracle.java.testlibrary.Asserts;
+
+class GCBeanDescription {
+    public String name;
+    public String[] poolNames;
+
+    public GCBeanDescription(String name, String[] poolNames) {
+        this.name = name;
+        this.poolNames = poolNames;
+    }
+}
+
+public class TestMemoryMXBeansAndPoolsPresence {
+    public static void test(GCBeanDescription... expectedBeans) {
+        List<MemoryPoolMXBean> memoryPools = ManagementFactory.getMemoryPoolMXBeans();
+
+        List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
+        Asserts.assertEQ(expectedBeans.length, gcBeans.size());
+
+        for (GCBeanDescription desc : expectedBeans) {
+            List<GarbageCollectorMXBean> beans = gcBeans.stream()
+                                                        .filter(b -> b.getName().equals(desc.name))
+                                                        .collect(Collectors.toList());
+            Asserts.assertEQ(beans.size(), 1);
+
+            GarbageCollectorMXBean bean = beans.get(0);
+            Asserts.assertEQ(desc.name, bean.getName());
+
+            String[] pools = bean.getMemoryPoolNames();
+            Asserts.assertEQ(desc.poolNames.length, pools.length);
+            for (int i = 0; i < desc.poolNames.length; i++) {
+                Asserts.assertEQ(desc.poolNames[i], pools[i]);
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        switch (args[0]) {
+            case "G1":
+                test(new GCBeanDescription("G1 Young Generation", new String[] {"G1 Eden Space", "G1 Survivor Space", "G1 Old Gen"}),
+                     new GCBeanDescription("G1 Old Generation",   new String[] {"G1 Eden Space", "G1 Survivor Space", "G1 Old Gen"}));
+                break;
+            case "CMS":
+                test(new GCBeanDescription("ParNew",              new String[] {"Par Eden Space", "Par Survivor Space"}),
+                     new GCBeanDescription("ConcurrentMarkSweep", new String[] {"Par Eden Space", "Par Survivor Space", "CMS Old Gen"}));
+                break;
+            case "Parallel":
+                test(new GCBeanDescription("PS Scavenge",         new String[] {"PS Eden Space", "PS Survivor Space"}),
+                     new GCBeanDescription("PS MarkSweep",        new String[] {"PS Eden Space", "PS Survivor Space", "PS Old Gen"}));
+                break;
+            case "Serial":
+                test(new GCBeanDescription("Copy",              new String[] {"Eden Space", "Survivor Space"}),
+                     new GCBeanDescription("MarkSweepCompact",  new String[] {"Eden Space", "Survivor Space", "Tenured Gen"}));
+                break;
+            default:
+                Asserts.assertTrue(false);
+                break;
+
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/g1/mixedgc/TestOldGenCollectionUsage.java	Mon Jan 28 17:51:10 2019 +0000
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2018, 2019, 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 TestOldGenCollectionUsage.java
+ * @bug 8195115
+ * @summary G1 Old Gen's CollectionUsage.used is zero after mixed GC which is incorrect
+ * @key gc
+ * @requires vm.gc=="G1" | vm.gc=="null"
+ * @requires vm.opt.MaxGCPauseMillis == "null"
+ * @library /testlibrary /testlibrary/whitebox
+ * @build ClassFileInstaller com.oracle.java.testlibrary.* sun.hotspot.WhiteBox TestOldGenCollectionUsage
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *                              sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions
+ *                   -XX:+WhiteBoxAPI -verbose:gc -XX:SurvivorRatio=1 -Xmx14m -Xms14m -XX:MaxTenuringThreshold=1
+ *                   -XX:InitiatingHeapOccupancyPercent=100 -XX:G1MixedGCCountTarget=4 -XX:MaxGCPauseMillis=30000
+ *                   -XX:G1HeapRegionSize=1m -XX:G1HeapWastePercent=0 -XX:G1MixedGCLiveThresholdPercent=100
+ *                   TestOldGenCollectionUsage
+ */
+
+import com.oracle.java.testlibrary.Asserts;
+import sun.hotspot.WhiteBox;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Collections;
+
+import java.lang.management.*;
+
+// 8195115 says that for the "G1 Old Gen" MemoryPool, CollectionUsage.used
+// is zero for G1 after a mixed collection, which is incorrect.
+
+public class TestOldGenCollectionUsage {
+
+    private String poolName = "G1 Old Gen";
+    private String collectorName = "G1 Young Generation";
+
+    public static void main(String [] args) throws Exception {
+        TestOldGenCollectionUsage t = new TestOldGenCollectionUsage();
+        t.run();
+    }
+
+    public TestOldGenCollectionUsage() {
+        System.out.println("Monitor G1 Old Gen pool with G1 Young Generation collector.");
+    }
+
+    public void run() {
+        // Find memory pool and collector
+        List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
+        MemoryPoolMXBean pool = null;
+        boolean foundPool = false;
+        for (int i = 0; i < pools.size(); i++) {
+            pool = pools.get(i);
+            String name = pool.getName();
+            if (name.contains(poolName)) {
+                System.out.println("Found pool: " + name);
+                foundPool = true;
+                break;
+            }
+        }
+        if (!foundPool) {
+            throw new RuntimeException(poolName + " not found, test with -XX:+UseG1GC");
+        }
+
+        List<GarbageCollectorMXBean> collectors = ManagementFactory.getGarbageCollectorMXBeans();
+        GarbageCollectorMXBean collector = null;
+        boolean foundCollector = false;
+        for (int i = 0; i < collectors.size(); i++) {
+            collector = collectors.get(i);
+            String name = collector.getName();
+            if (name.contains(collectorName)) {
+                System.out.println("Found collector: " + name);
+                foundCollector = true;
+                break;
+            }
+        }
+        if (!foundCollector) {
+            throw new RuntimeException(collectorName + " not found, test with -XX:+UseG1GC");
+        }
+
+        MixedGCProvoker gcProvoker = new MixedGCProvoker();
+        gcProvoker.allocateOldObjects();
+
+        // Verify no non-zero result was stored
+        long usage = pool.getCollectionUsage().getUsed();
+        System.out.println(poolName + ": usage after GC = " + usage);
+        if (usage > 0) {
+            throw new RuntimeException("Premature mixed collections(s)");
+        }
+
+        // Verify that collections were done
+        long collectionCount = collector.getCollectionCount();
+        System.out.println(collectorName + ": collection count = "
+                           + collectionCount);
+        long collectionTime = collector.getCollectionTime();
+        System.out.println(collectorName + ": collection time  = "
+                           + collectionTime);
+        if (collectionCount <= 0) {
+            throw new RuntimeException("Collection count <= 0");
+        }
+        if (collectionTime <= 0) {
+            throw new RuntimeException("Collector has not run");
+        }
+
+        gcProvoker.provokeMixedGC();
+
+        usage = pool.getCollectionUsage().getUsed();
+        System.out.println(poolName + ": usage after GC = " + usage);
+        if (usage <= 0) {
+            throw new RuntimeException(poolName + " found with zero usage");
+        }
+
+        long newCollectionCount = collector.getCollectionCount();
+        System.out.println(collectorName + ": collection count = "
+                           + newCollectionCount);
+        long newCollectionTime = collector.getCollectionTime();
+        System.out.println(collectorName + ": collection time  = "
+                           + newCollectionTime);
+        if (newCollectionCount <= collectionCount) {
+            throw new RuntimeException("No new collection");
+        }
+        if (newCollectionTime <= collectionTime) {
+            throw new RuntimeException("Collector has not run some more");
+        }
+
+        System.out.println("Test passed.");
+    }
+
+    /**
+     * Utility class to guarantee a mixed GC. The class allocates several arrays and
+     * promotes them to the oldgen. After that it tries to provoke mixed GC by
+     * allocating new objects.
+     *
+     * The necessary condition for guaranteed mixed GC is running MixedGCProvoker is
+     * running in VM with the following flags: -XX:MaxTenuringThreshold=1 -Xms12M
+     * -Xmx12M -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0
+     * -XX:G1HeapRegionSize=1m
+     */
+    public class MixedGCProvoker {
+        private final WhiteBox WB = WhiteBox.getWhiteBox();
+        private final List<byte[]> liveOldObjects = new ArrayList<>();
+        private final List<byte[]> newObjects = new ArrayList<>();
+
+        public static final int ALLOCATION_SIZE = 20000;
+        public static final int ALLOCATION_COUNT = 15;
+
+        public void allocateOldObjects() {
+            List<byte[]> deadOldObjects = new ArrayList<>();
+            // Allocates buffer and promotes it to the old gen. Mix live and dead old
+            // objects
+            for (int i = 0; i < ALLOCATION_COUNT; ++i) {
+                liveOldObjects.add(new byte[ALLOCATION_SIZE * 5]);
+                deadOldObjects.add(new byte[ALLOCATION_SIZE * 5]);
+            }
+
+            // Do two young collections, MaxTenuringThreshold=1 will force promotion.
+            // G1HeapRegionSize=1m guarantees that old gen regions will be filled.
+            WB.youngGC();
+            WB.youngGC();
+            // Check it is promoted & keep alive
+            Asserts.assertTrue(WB.isObjectInOldGen(liveOldObjects),
+                               "List of the objects is suppose to be in OldGen");
+            Asserts.assertTrue(WB.isObjectInOldGen(deadOldObjects),
+                               "List of the objects is suppose to be in OldGen");
+        }
+
+        /**
+         * Waits until Concurent Mark Cycle finishes
+         * @param wb  Whitebox instance
+         * @param sleepTime sleep time
+         */
+        private void waitTillCMCFinished(int sleepTime) {
+            while (WB.g1InConcurrentMark()) {
+                if (sleepTime > -1) {
+                    try {
+                        Thread.sleep(sleepTime);
+                    } catch (InterruptedException e) {
+                        System.out.println("Got InterruptedException while waiting for ConcMarkCycle to finish");
+                    }
+                }
+            }
+        }
+
+        public void provokeMixedGC() {
+            waitTillCMCFinished(0);
+            WB.g1StartConcMarkCycle();
+            waitTillCMCFinished(0);
+            WB.youngGC();
+
+            System.out.println("Allocating new objects to provoke mixed GC");
+            // Provoke a mixed collection. G1MixedGCLiveThresholdPercent=100
+            // guarantees that full old gen regions will be included.
+            for (int i = 0; i < (ALLOCATION_COUNT * 20); i++) {
+                try {
+                    newObjects.add(new byte[ALLOCATION_SIZE]);
+                } catch (OutOfMemoryError e) {
+                    newObjects.clear();
+                    WB.youngGC();
+                    WB.youngGC();
+                    System.out.println("OutOfMemoryError is reported, stop allocating new objects");
+                    break;
+                }
+            }
+            // check that liveOldObjects still alive
+            Asserts.assertTrue(WB.isObjectInOldGen(liveOldObjects),
+                               "List of the objects is suppose to be in OldGen");
+        }
+
+    }
+
+}