1 /*
2  * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test TestOldGenCollectionUsage.java
26  * @bug 8195115
27  * @summary G1 Old Gen's CollectionUsage.used is zero after mixed GC which is incorrect
28  * @key gc
29  * @requires vm.gc.G1
30  * @library /test/lib
31  * @modules java.base/jdk.internal.misc
32  * @modules java.management
33  * @build sun.hotspot.WhiteBox
34  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
35  * @run main/othervm -Xbootclasspath/a:. -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -verbose:gc -XX:SurvivorRatio=1 -Xmx12m -Xms12m -XX:MaxTenuringThreshold=1 -XX:InitiatingHeapOccupancyPercent=100 -XX:-G1UseAdaptiveIHOP -XX:G1MixedGCCountTarget=4 -XX:MaxGCPauseMillis=30000 -XX:G1HeapRegionSize=1m -XX:G1HeapWastePercent=0 -XX:G1MixedGCLiveThresholdPercent=100 TestOldGenCollectionUsage
36  */
37 
38 import jdk.test.lib.Asserts;
39 import sun.hotspot.WhiteBox;
40 
41 import java.util.ArrayList;
42 import java.util.List;
43 import java.util.Collections;
44 
45 import java.lang.management.*;
46 
47 // 8195115 says that for the "G1 Old Gen" MemoryPool, CollectionUsage.used
48 // is zero for G1 after a mixed collection, which is incorrect.
49 
50 public class TestOldGenCollectionUsage {
51 
52     private String poolName = "G1 Old Gen";
53     private String collectorName = "G1 Young Generation";
54 
main(String [] args)55     public static void main(String [] args) throws Exception {
56         TestOldGenCollectionUsage t = new TestOldGenCollectionUsage();
57         t.run();
58     }
59 
TestOldGenCollectionUsage()60     public TestOldGenCollectionUsage() {
61         System.out.println("Monitor G1 Old Gen pool with G1 Young Generation collector.");
62     }
63 
run()64     public void run() {
65         // Find memory pool and collector
66         List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
67         MemoryPoolMXBean pool = null;
68         boolean foundPool = false;
69         for (int i = 0; i < pools.size(); i++) {
70             pool = pools.get(i);
71             String name = pool.getName();
72             if (name.contains(poolName)) {
73                 System.out.println("Found pool: " + name);
74                 foundPool = true;
75                 break;
76             }
77         }
78         if (!foundPool) {
79             throw new RuntimeException(poolName + " not found, test with -XX:+UseG1GC");
80         }
81 
82         List<GarbageCollectorMXBean> collectors = ManagementFactory.getGarbageCollectorMXBeans();
83         GarbageCollectorMXBean collector = null;
84         boolean foundCollector = false;
85         for (int i = 0; i < collectors.size(); i++) {
86             collector = collectors.get(i);
87             String name = collector.getName();
88             if (name.contains(collectorName)) {
89                 System.out.println("Found collector: " + name);
90                 foundCollector = true;
91                 break;
92             }
93         }
94         if (!foundCollector) {
95             throw new RuntimeException(collectorName + " not found, test with -XX:+UseG1GC");
96         }
97 
98         MixedGCProvoker gcProvoker = new MixedGCProvoker();
99         gcProvoker.allocateOldObjects();
100 
101         // Verify no non-zero result was stored
102         long usage = pool.getCollectionUsage().getUsed();
103         System.out.println(poolName + ": usage after GC = " + usage);
104         if (usage > 0) {
105             throw new RuntimeException("Premature mixed collections(s)");
106         }
107 
108         // Verify that collections were done
109         long collectionCount = collector.getCollectionCount();
110         System.out.println(collectorName + ": collection count = "
111                            + collectionCount);
112         long collectionTime = collector.getCollectionTime();
113         System.out.println(collectorName + ": collection time  = "
114                            + collectionTime);
115         if (collectionCount <= 0) {
116             throw new RuntimeException("Collection count <= 0");
117         }
118         if (collectionTime <= 0) {
119             throw new RuntimeException("Collector has not run");
120         }
121 
122         gcProvoker.provokeMixedGC();
123 
124         usage = pool.getCollectionUsage().getUsed();
125         System.out.println(poolName + ": usage after GC = " + usage);
126         if (usage <= 0) {
127             throw new RuntimeException(poolName + " found with zero usage");
128         }
129 
130         long newCollectionCount = collector.getCollectionCount();
131         System.out.println(collectorName + ": collection count = "
132                            + newCollectionCount);
133         long newCollectionTime = collector.getCollectionTime();
134         System.out.println(collectorName + ": collection time  = "
135                            + newCollectionTime);
136         if (newCollectionCount <= collectionCount) {
137             throw new RuntimeException("No new collection");
138         }
139         if (newCollectionTime <= collectionTime) {
140             throw new RuntimeException("Collector has not run some more");
141         }
142 
143         System.out.println("Test passed.");
144     }
145 
146     /**
147      * Utility class to guarantee a mixed GC. The class allocates several arrays and
148      * promotes them to the oldgen. After that it tries to provoke mixed GC by
149      * allocating new objects.
150      *
151      * The necessary condition for guaranteed mixed GC is running MixedGCProvoker is
152      * running in VM with the following flags: -XX:MaxTenuringThreshold=1 -Xms12M
153      * -Xmx12M -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0
154      * -XX:G1HeapRegionSize=1m
155      */
156     public class MixedGCProvoker {
157         private final WhiteBox WB = WhiteBox.getWhiteBox();
158         private final List<byte[]> liveOldObjects = new ArrayList<>();
159         private final List<byte[]> newObjects = new ArrayList<>();
160 
161         public static final int ALLOCATION_SIZE = 20000;
162         public static final int ALLOCATION_COUNT = 15;
163 
allocateOldObjects()164         public void allocateOldObjects() {
165             List<byte[]> deadOldObjects = new ArrayList<>();
166             // Allocates buffer and promotes it to the old gen. Mix live and dead old
167             // objects
168             for (int i = 0; i < ALLOCATION_COUNT; ++i) {
169                 liveOldObjects.add(new byte[ALLOCATION_SIZE * 5]);
170                 deadOldObjects.add(new byte[ALLOCATION_SIZE * 5]);
171             }
172 
173             // Do two young collections, MaxTenuringThreshold=1 will force promotion.
174             // G1HeapRegionSize=1m guarantees that old gen regions will be filled.
175             WB.youngGC();
176             WB.youngGC();
177             // Check it is promoted & keep alive
178             Asserts.assertTrue(WB.isObjectInOldGen(liveOldObjects),
179                                "List of the objects is suppose to be in OldGen");
180             Asserts.assertTrue(WB.isObjectInOldGen(deadOldObjects),
181                                "List of the objects is suppose to be in OldGen");
182         }
183 
184         /**
185          * Waits until Concurent Mark Cycle finishes
186          * @param wb  Whitebox instance
187          * @param sleepTime sleep time
188          */
waitTillCMCFinished(int sleepTime)189         private void waitTillCMCFinished(int sleepTime) {
190             while (WB.g1InConcurrentMark()) {
191                 if (sleepTime > -1) {
192                     try {
193                         Thread.sleep(sleepTime);
194                     } catch (InterruptedException e) {
195                         System.out.println("Got InterruptedException while waiting for ConcMarkCycle to finish");
196                     }
197                 }
198             }
199         }
200 
provokeMixedGC()201         public void provokeMixedGC() {
202             waitTillCMCFinished(0);
203             WB.g1StartConcMarkCycle();
204             waitTillCMCFinished(0);
205             WB.youngGC();
206 
207             System.out.println("Allocating new objects to provoke mixed GC");
208             // Provoke a mixed collection. G1MixedGCLiveThresholdPercent=100
209             // guarantees that full old gen regions will be included.
210             for (int i = 0; i < (ALLOCATION_COUNT * 20); i++) {
211                 newObjects.add(new byte[ALLOCATION_SIZE]);
212             }
213             // check that liveOldObjects still alive
214             Asserts.assertTrue(WB.isObjectInOldGen(liveOldObjects),
215                                "List of the objects is suppose to be in OldGen");
216         }
217 
218     }
219 
220 }
221