1 /* 2 * Copyright (c) 2018, Red Hat, Inc. 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 /* 26 * @test id=passive 27 * @summary Check that MX notifications are reported for all cycles 28 * @library /test/lib / 29 * @requires vm.gc.Shenandoah 30 * 31 * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions 32 * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive 33 * -XX:+ShenandoahDegeneratedGC -Dprecise=true 34 * TestChurnNotifications 35 * 36 * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions 37 * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive 38 * -XX:-ShenandoahDegeneratedGC -Dprecise=true 39 * TestChurnNotifications 40 */ 41 42 /* 43 * @test id=aggressive 44 * @summary Check that MX notifications are reported for all cycles 45 * @library /test/lib / 46 * @requires vm.gc.Shenandoah 47 * 48 * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions 49 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive 50 * -Dprecise=false 51 * TestChurnNotifications 52 */ 53 54 /* 55 * @test id=adaptive 56 * @summary Check that MX notifications are reported for all cycles 57 * @library /test/lib / 58 * @requires vm.gc.Shenandoah 59 * 60 * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions 61 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive 62 * -Dprecise=false 63 * TestChurnNotifications 64 */ 65 66 /* 67 * @test id=static 68 * @summary Check that MX notifications are reported for all cycles 69 * @library /test/lib / 70 * @requires vm.gc.Shenandoah 71 * 72 * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions 73 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=static 74 * -Dprecise=false 75 * TestChurnNotifications 76 */ 77 78 /* 79 * @test id=compact 80 * @summary Check that MX notifications are reported for all cycles 81 * @library /test/lib / 82 * @requires vm.gc.Shenandoah 83 * 84 * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions 85 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact 86 * -Dprecise=false 87 * TestChurnNotifications 88 */ 89 90 import java.util.*; 91 import java.util.concurrent.atomic.*; 92 import javax.management.*; 93 import java.lang.management.*; 94 import javax.management.openmbean.*; 95 96 import jdk.test.lib.Utils; 97 98 import com.sun.management.GarbageCollectionNotificationInfo; 99 100 public class TestChurnNotifications { 101 102 static final long HEAP_MB = 128; // adjust for test configuration above 103 static final long TARGET_MB = Long.getLong("target", 2_000); // 2 Gb allocation 104 105 // Should we track the churn precisely? 106 // Precise tracking is only reliable when GC is fully stop-the-world. Otherwise, 107 // we cannot tell, looking at heap used before/after, what was the GC churn. 108 static final boolean PRECISE = Boolean.getBoolean("precise"); 109 110 static final long M = 1024 * 1024; 111 112 static volatile Object sink; 113 114 public static void main(String[] args) throws Exception { 115 final long startTimeNanos = System.nanoTime(); 116 117 final AtomicLong churnBytes = new AtomicLong(); 118 119 NotificationListener listener = new NotificationListener() { 120 @Override 121 public void handleNotification(Notification n, Object o) { 122 if (n.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { 123 GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from((CompositeData) n.getUserData()); 124 Map<String, MemoryUsage> mapBefore = info.getGcInfo().getMemoryUsageBeforeGc(); 125 Map<String, MemoryUsage> mapAfter = info.getGcInfo().getMemoryUsageAfterGc(); 126 127 MemoryUsage before = mapBefore.get("Shenandoah"); 128 MemoryUsage after = mapAfter.get("Shenandoah"); 129 130 if ((before != null) && (after != null)) { 131 long diff = before.getUsed() - after.getUsed(); 132 if (diff > 0) { 133 churnBytes.addAndGet(diff); 134 } 135 } 136 } 137 } 138 }; 139 140 for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { 141 ((NotificationEmitter) bean).addNotificationListener(listener, null, null); 142 } 143 144 final int size = 100_000; 145 long count = TARGET_MB * 1024 * 1024 / (16 + 4 * size); 146 147 long mem = count * (16 + 4 * size); 148 149 for (int c = 0; c < count; c++) { 150 sink = new int[size]; 151 } 152 153 System.gc(); 154 155 long minExpected = PRECISE ? (mem - HEAP_MB * 1024 * 1024) : 1; 156 long maxExpected = mem + HEAP_MB * 1024 * 1024; 157 long actual = 0; 158 159 // Look at test timeout to figure out how long we can wait without breaking into timeout. 160 // Default to 1/4 of the remaining time in 1s steps. 161 final long STEP_MS = 1000; 162 long spentTimeNanos = System.nanoTime() - startTimeNanos; 163 long maxTries = (Utils.adjustTimeout(Utils.DEFAULT_TEST_TIMEOUT) - (spentTimeNanos / 1_000_000L)) / STEP_MS / 4; 164 165 // Wait until enough notifications are accrued to match minimum boundary. 166 long tries = 0; 167 while (tries++ < maxTries) { 168 actual = churnBytes.get(); 169 if (minExpected <= actual) { 170 // Wait some more to test if we are breaking the maximum boundary. 171 Thread.sleep(5000); 172 actual = churnBytes.get(); 173 break; 174 } 175 Thread.sleep(STEP_MS); 176 } 177 178 String msg = "Expected = [" + minExpected / M + "; " + maxExpected / M + "] (" + mem / M + "), actual = " + actual / M; 179 if (minExpected <= actual && actual <= maxExpected) { 180 System.out.println(msg); 181 } else { 182 throw new IllegalStateException(msg); 183 } 184 } 185 }