--- old/src/hotspot/share/gc/g1/g1CollectedHeap.cpp 2018-01-29 12:15:29.000000000 -0800 +++ new/src/hotspot/share/gc/g1/g1CollectedHeap.cpp 2018-01-29 12:15:29.000000000 -0800 @@ -1783,7 +1783,7 @@ _memory_manager.add_pool(_eden_pool); _memory_manager.add_pool(_survivor_pool); - + _memory_manager.add_pool(_old_pool, false /* always_affected_by_gc */); } void G1CollectedHeap::stop() { @@ -2914,7 +2914,7 @@ log_info(gc,task)("Using %u workers of %u for evacuation", active_workers, workers()->total_workers()); TraceCollectorStats tcs(g1mm()->incremental_collection_counters()); - TraceMemoryManagerStats tms(&_memory_manager, gc_cause()); + TraceMemoryManagerStats tms(&_memory_manager, gc_cause(), collector_state()->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; --- old/src/hotspot/share/services/memoryManager.cpp 2018-01-29 12:15:30.000000000 -0800 +++ new/src/hotspot/share/services/memoryManager.cpp 2018-01-29 12:15:30.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2018, 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 @@ -42,13 +42,15 @@ (void)const_cast(_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() { @@ -188,7 +190,13 @@ delete _current_gc_stat; } -void GCMemoryManager::initialize_gc_stat_info() { +int 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; + return index; +} + + 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()); _current_gc_stat = new(ResourceObj::C_HEAP, mtGC) GCStatInfo(MemoryService::num_memory_pools()); @@ -229,7 +237,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(); } @@ -258,9 +267,11 @@ MemoryPool* pool = get_memory_pool(i); 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); + } } } --- old/src/hotspot/share/services/memoryManager.hpp 2018-01-29 12:15:31.000000000 -0800 +++ new/src/hotspot/share/services/memoryManager.hpp 2018-01-29 12:15:31.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2018, 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 @@ -44,11 +44,12 @@ class OopClosure; class MemoryManager : public CHeapObj { -private: +protected: enum { max_num_pools = 10 }; +private: MemoryPool* _pools[max_num_pools]; int _num_pools; @@ -66,7 +67,7 @@ return _pools[index]; } - void add_pool(MemoryPool* pool); + int add_pool(MemoryPool* pool); bool is_manager(instanceHandle mh) { return mh() == _memory_mgr_obj; } @@ -143,11 +144,19 @@ int _num_gc_threads; volatile bool _notification_enabled; const char* _gc_end_message; + bool _pool_always_affected_by_gc[MemoryManager::max_num_pools]; + public: GCMemoryManager(const char* name, const char* gc_end_message); ~GCMemoryManager(); - void initialize_gc_stat_info(); + int add_pool(MemoryPool* pool, bool always_affected_by_gc = true); + 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; } jlong gc_time_ms() { return _accumulated_timer.milliseconds(); } @@ -158,7 +167,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(); } --- old/src/hotspot/share/services/memoryService.cpp 2018-01-29 12:15:32.000000000 -0800 +++ new/src/hotspot/share/services/memoryService.cpp 2018-01-29 12:15:32.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2018, 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 @@ -180,10 +180,11 @@ void MemoryService::gc_end(GCMemoryManager* manager, bool recordPostGCUsage, bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection, - GCCause::Cause cause) { + GCCause::Cause cause, + bool allMemoryPoolsAffected) { // register the GC end statistics and memory usage manager->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, - countCollection, cause); + countCollection, cause, allMemoryPoolsAffected); } void MemoryService::oops_do(OopClosure* f) { @@ -236,6 +237,7 @@ TraceMemoryManagerStats::TraceMemoryManagerStats(GCMemoryManager* gc_memory_manager, GCCause::Cause cause, + bool allMemoryPoolsAffected, bool recordGCBeginTime, bool recordPreGCUsage, bool recordPeakUsage, @@ -245,7 +247,7 @@ bool countCollection) { initialize(gc_memory_manager, cause, recordGCBeginTime, recordPreGCUsage, recordPeakUsage, recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, - countCollection); + countCollection, allMemoryPoolsAffected); } // for a subclass to create then initialize an instance before invoking @@ -258,8 +260,10 @@ bool recordPostGCUsage, bool recordAccumulatedGCTime, bool recordGCEndTime, - bool countCollection) { + bool countCollection, + bool allMemoryPoolsAffected) { _gc_memory_manager = gc_memory_manager; + _allMemoryPoolsAffected = allMemoryPoolsAffected; _recordGCBeginTime = recordGCBeginTime; _recordPreGCUsage = recordPreGCUsage; _recordPeakUsage = recordPeakUsage; @@ -275,5 +279,5 @@ TraceMemoryManagerStats::~TraceMemoryManagerStats() { MemoryService::gc_end(_gc_memory_manager, _recordPostGCUsage, _recordAccumulatedGCTime, - _recordGCEndTime, _countCollection, _cause); + _recordGCEndTime, _countCollection, _cause, _allMemoryPoolsAffected); } --- old/src/hotspot/share/services/memoryService.hpp 2018-01-29 12:15:34.000000000 -0800 +++ new/src/hotspot/share/services/memoryService.hpp 2018-01-29 12:15:33.000000000 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2018, 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 @@ -102,7 +102,8 @@ static void gc_end(GCMemoryManager* manager, bool recordPostGCUsage, bool recordAccumulatedGCTime, bool recordGCEndTime, bool countCollection, - GCCause::Cause cause); + GCCause::Cause cause, + bool allMemoryPoolsAffected); static void oops_do(OopClosure* f); @@ -116,6 +117,7 @@ class TraceMemoryManagerStats : public StackObj { private: GCMemoryManager* _gc_memory_manager; + bool _allMemoryPoolsAffected; bool _recordGCBeginTime; bool _recordPreGCUsage; bool _recordPeakUsage; @@ -128,6 +130,7 @@ TraceMemoryManagerStats() {} TraceMemoryManagerStats(GCMemoryManager* gc_memory_manager, GCCause::Cause cause, + bool allMemoryPoolsAffected = true, bool recordGCBeginTime = true, bool recordPreGCUsage = true, bool recordPeakUsage = true, @@ -144,7 +147,8 @@ bool recordPostGCUsage, bool recordAccumulatedGCTime, bool recordGCEndTime, - bool countCollection); + bool countCollection, + bool allMemoryPoolsAffected = true); ~TraceMemoryManagerStats(); }; --- old/test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java 2018-01-29 12:15:35.000000000 -0800 +++ new/test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java 2018-01-29 12:15:34.000000000 -0800 @@ -77,7 +77,7 @@ 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"}), + 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": --- /dev/null 2018-01-29 12:15:36.000000000 -0800 +++ new/test/hotspot/jtreg/gc/g1/TestMixedOldGenCollectionUsage.java 2018-01-29 12:15:36.000000000 -0800 @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2018, 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 TestMixedOldGenCollectionUsage.java + * @bug 8195115 + * @requires vm.gc.G1 + * @summary G1 Old Gen's CollectionUsage.used is zero after mixed GC which is incorrect + * @run main/othervm -Xmx64m -Xms64m -verbose:gc -XX:+UseG1GC TestMixedOldGenCollectionUsage + */ +import java.util.*; +import java.lang.management.*; + +// 8195115 says that for the "G1 Old Gen" MemoryPool, CollectionUsage.used +// is zero for G1 after a mixed collection. + +public class TestMixedOldGenCollectionUsage { + + private String poolName = "G1 Old Gen"; + private String collectorName = "G1 Young Generation"; + + public static void main(String [] args) { + TestMixedOldGenCollectionUsage t = new TestMixedOldGenCollectionUsage(); + t.run(); + } + + public TestMixedOldGenCollectionUsage() { + System.out.println("Monitor G1 Old Gen pool with G1 Young Generation collector."); + } + + public void run() { + // Find memory pool and collector + List 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 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"); + } + + // Use some memory, enough that young, but not mixed, collections + // have happened. + allocationWork(20*1024*1024, 0); + System.out.println("Done allocationWork for pure young collections"); + + // 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"); + } + + // Must run with options to ensure no stop the world full GC, + // but at least one GC cycle that includes a mixed collection. + allocationWork(20*1024*1024, 10*1024*1024); + System.out.println("Done allocationWork for mixed collections"); + + 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."); + } + + public void allocationWork(long persistentTarget, long target) { + long persistentSizeAllocated = 0; + long sizeAllocated = 0; + char[] template = new char[10240]; + Random rand = new Random(); + PrimitiveIterator.OfInt indexiter = rand.ints().iterator(); + PrimitiveIterator.OfInt valueiter = rand.ints().iterator(); + + Set persistentStrings = new HashSet<>(1000); + while (persistentSizeAllocated < persistentTarget) { + for (int i = 0; i < 100; i++) { + template[Math.abs(indexiter.next() % 10240)] = (char)valueiter.nextInt(); + persistentStrings.add(new String(template)); + } + persistentSizeAllocated += 100*10240*2; + } + while (sizeAllocated < target) { + Set strings = new HashSet<>(1000); + for (int i = 0; i < 1000; i++) { + template[Math.abs(indexiter.next() % 10240)] = (char)valueiter.nextInt(); + strings.add(new String(template)); + } + sizeAllocated += 1000*10240*2; + } + } +}