1 /* 2 * Copyright (c) 2016, 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 package gc.g1.humongousObjects.objectGraphTest; 26 27 import jdk.test.lib.process.OutputAnalyzer; 28 import sun.hotspot.WhiteBox; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.lang.ref.Reference; 33 import java.lang.ref.ReferenceQueue; 34 import java.lang.ref.SoftReference; 35 import java.lang.ref.WeakReference; 36 import java.nio.file.Files; 37 import java.util.Map; 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.HashSet; 41 import java.util.Set; 42 import java.util.function.BiConsumer; 43 import java.util.function.Consumer; 44 import java.util.function.Predicate; 45 import java.util.stream.Collectors; 46 47 48 /** 49 * @test TestObjectGraphAfterGC 50 * @summary Checks that objects' graph behave as expected after gc 51 * @requires vm.gc.G1 52 * @requires vm.opt.ExplicitGCInvokesConcurrent != true 53 * @library /test/lib / 54 * @modules java.management java.base/jdk.internal.misc 55 * @build sun.hotspot.WhiteBox 56 * @ignore 8156755 57 * 58 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 59 * sun.hotspot.WhiteBox$WhiteBoxPermission 60 * 61 * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. 62 * -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=30000 -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0 63 * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_MIXED_GC.gc.log -XX:MaxTenuringThreshold=1 64 * -XX:G1MixedGCCountTarget=1 -XX:G1OldCSetRegionThresholdPercent=100 -XX:SurvivorRatio=1 -XX:InitiatingHeapOccupancyPercent=0 65 * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC MIXED_GC 66 * 67 * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. 68 * -XX:G1HeapRegionSize=1M -Xlog:gc*=debug:file=TestObjectGraphAfterGC_YOUNG_GC.gc.log 69 * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC YOUNG_GC 70 * 71 * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. 72 * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC.gc.log 73 * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC 74 * 75 * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. 76 * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_FULL_GC_MEMORY_PRESSURE.gc.log 77 * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC FULL_GC_MEMORY_PRESSURE 78 * 79 * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. 80 * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_CMC.gc.log -XX:MaxTenuringThreshold=16 81 * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC CMC 82 * 83 * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. 84 * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_CMC_NO_SURV_ROOTS.gc.log -XX:MaxTenuringThreshold=1 85 * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC CMC_NO_SURV_ROOTS 86 * 87 */ 88 89 /** 90 * Checks that objects' graph behave as expected after gc 91 * See README file for detailed info on test's logic 92 */ 93 public class TestObjectGraphAfterGC { 94 95 private static final int simpleAllocationSize = 1024; 96 97 /** 98 * Entry point 99 * 100 * @param args - first argument - gc name 101 */ 102 public static void main(String[] args) { 103 104 if (args.length < 1) { 105 throw new Error("Expected gc name wasn't provided as command line argument"); 106 } 107 108 GC gcType = GC.valueOf(args[0].toUpperCase()); 109 110 System.out.println("Testing " + gcType.name()); 111 112 TestcaseData.getPregeneratedTestcases().stream().forEach(testcase -> { 113 System.out.println("Testcase: " + testcase); 114 115 try { 116 TestObjectGraphAfterGC.doTesting(testcase, gcType.get(), gcType.getChecker(), 117 gcType.getGcLogName(TestObjectGraphAfterGC.class.getSimpleName()), gcType.shouldContain(), 118 gcType.shouldNotContain()); 119 } catch (IOException e) { 120 throw new Error("Problems trying to find or open " + TestObjectGraphAfterGC.class.getSimpleName() 121 + ".gc.log", e); 122 } 123 System.out.println(" Passed"); 124 }); 125 } 126 127 /** 128 * Implements testing with 3 methods - allocateObjectGraph, checkResults and checkGCLog 129 * 130 * @param testcaseData testcase in the following notation: 131 * H - humongous node 132 * S - non-humongous node 133 * s - external soft reference 134 * w - external weak reference 135 * Hs->Sw - 1st node is humongous, externally soft referenced and strong references to 136 * non-humongous node 2 which is externally weak referenced 137 * H->1 - humongous node connects to the first node of chain 138 * @param doGC method that initiates gc 139 * @param checker consumer that checks node's state after gc and throws Error if it's wrong 140 * @param gcLogName name of gc log 141 * @param shouldContain list of tokens that should be contained in gc log 142 * @param shouldNotContain list of tokens that should not be contained in gc log 143 * @throws IOException if there are some issues with gc log 144 */ 145 private static void doTesting(String testcaseData, Runnable doGC, Consumer<ReferenceInfo<Object[]>> checker, 146 String gcLogName, List<String> shouldContain, List<String> shouldNotContain) 147 throws IOException { 148 Set<ReferenceInfo<Object[]>> nodeData = allocateObjectGraph(testcaseData); 149 doGC.run(); 150 checkResults(nodeData, checker); 151 checkGCLog(gcLogName, shouldContain, shouldNotContain); 152 } 153 154 /** 155 * Allocates a number of objects of humongous and regular size and links then with strong references. 156 * How many objects to create, their size and links between them is encoded in the given parameters. 157 * As the result an object graph will be created. 158 * For the testing purpose for each created object (a graph node) an extra ReferenceInfo object will be created. 159 * The ReferenceInfo instances will contain either weak or soft reference to the graph node. 160 * 161 * @param testcaseData testcase in the 162 * <p> 163 * H - humongous node 164 * S - non-humongous node 165 * s - external soft reference 166 * w - external weak reference 167 * Hs->Sw - 1st node is humongous, externally soft referenced and strong references to 168 * non-humongous node 2 which is externally weak referenced 169 * H->1 - humongous node connects to the first node of chain 170 * @return set of ReferenceInfo objects containing weak/soft reference to the graph node and other data on how 171 * objects should behave after gc 172 */ 173 private static Set<ReferenceInfo<Object[]>> allocateObjectGraph(String testcaseData) { 174 Map<Object[], String> nodeIds = new HashMap<>(); 175 Set<Object[]> humongousNodes = new HashSet<>(); 176 Set<Object[]> externalSoftReferenced = new HashSet<>(); 177 Set<Object[]> externalWeakReferenced = new HashSet<>(); 178 179 Map<Predicate<TestcaseData.FinalParsedNode>, BiConsumer<TestcaseData.FinalParsedNode, Object[][]>> visitors 180 = new HashMap<>(); 181 182 visitors.put((parsedNode -> true), 183 (parsedNode, objects) -> nodeIds.put(objects[Integer.valueOf(parsedNode.id)], parsedNode.id) 184 ); 185 186 visitors.put((parsedNode -> parsedNode.isHumongous), 187 (parsedNode, objects) -> humongousNodes.add(objects[Integer.valueOf(parsedNode.id)]) 188 ); 189 190 visitors.put(parsedNode -> parsedNode.getReferencesTypes().stream(). 191 anyMatch(referenceType -> referenceType == ObjectGraph.ReferenceType.SOFT), 192 (parsedNode, objects) -> externalSoftReferenced.add(objects[Integer.valueOf(parsedNode.id)]) 193 ); 194 195 visitors.put(parsedNode -> parsedNode.getReferencesTypes().stream(). 196 anyMatch(referenceType -> referenceType == ObjectGraph.ReferenceType.WEAK), 197 (parsedNode, objects) -> externalWeakReferenced.add(objects[Integer.valueOf(parsedNode.id)]) 198 ); 199 200 List<TestcaseData.FinalParsedNode> internalParsedNodes = TestcaseData.parse(testcaseData); 201 202 Object[] root = ObjectGraph.generateObjectNodes(internalParsedNodes, visitors, 203 WhiteBox.getWhiteBox().g1RegionSize(), simpleAllocationSize); 204 205 ObjectGraph.propagateTransitiveProperty(humongousNodes, humongousNodes::add); 206 Set<Object[]> effectiveSoftReferenced = new HashSet<>(); 207 ObjectGraph.propagateTransitiveProperty(externalSoftReferenced, effectiveSoftReferenced::add); 208 209 // Create external references 210 ReferenceQueue<Object[]> referenceQueue = new ReferenceQueue<>(); 211 Set<Reference<Object[]>> externalRefs = new HashSet<>(); 212 213 externalWeakReferenced.stream() 214 .forEach(objects -> externalRefs.add(new WeakReference<>(objects, referenceQueue))); 215 externalSoftReferenced.stream() 216 .forEach(objects -> externalRefs.add(new SoftReference<>(objects, referenceQueue))); 217 218 return externalRefs.stream() 219 .map(ref -> new ReferenceInfo<>(ref, testcaseData, nodeIds.get(ref.get()), 220 effectiveSoftReferenced.contains(ref.get()), humongousNodes.contains(ref.get()))) 221 .collect(Collectors.toSet()); 222 223 } 224 225 /** 226 * Checks that object' state after gc is as expected 227 * 228 * @param nodeData array with information about nodes 229 * @param checker consumer that checks node's state after gc and throws Error if it's wrong 230 */ 231 private static void checkResults(Set<ReferenceInfo<Object[]>> nodeData, Consumer<ReferenceInfo<Object[]>> checker) { 232 nodeData.stream().forEach(checker::accept); 233 } 234 235 /** 236 * Checks that gc log contains what we expected and does not contain what we didn't expect 237 * 238 * @param gcLogName gc log name 239 * @param shouldContain list of tokens that should be contained in gc log 240 * @param shouldNotContain list of tokens that should not be contained in gc log 241 * @throws IOException if there are some issues with gc log 242 */ 243 private static void checkGCLog(String gcLogName, List<String> shouldContain, List<String> shouldNotContain) 244 throws IOException { 245 246 if (gcLogName == null) { 247 return; 248 } 249 String gcLog = new String(Files.readAllBytes(new File(gcLogName).toPath())); 250 251 OutputAnalyzer outputAnalyzer = new OutputAnalyzer(gcLog, ""); 252 253 shouldContain.stream().forEach(outputAnalyzer::shouldContain); 254 shouldNotContain.stream().forEach(outputAnalyzer::shouldNotContain); 255 } 256 257 }