1 /* 2 * Copyright (c) 2013, 2018, 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 package metaspace.gc; 25 26 import java.io.IOException; 27 import java.util.regex.Matcher; 28 import java.util.regex.Pattern; 29 import static metaspace.gc.MetaspaceBaseGC.PAGE_SIZE; 30 31 /** 32 * Test for metaspace GC 33 * 34 * <ul> 35 * <li>MetaspaceSize 36 * </ul> 37 * Test checks that the first GC happens when metaspace committed is next to 38 * MetaspaceSize value. 39 * 40 * Based on actual events (JDK 8 GC tuning document) 41 * 42 * Quating: Java SE 8 HotSpot[tm] Virtual Machine Garbage Collection Tuning 43 * <pre> 44 * Class metadata is deallocated when the corresponding Java class is unloaded. 45 * Java classes are unloaded as a results of garbage collection and garbage 46 * collections may be induced in order to unload classes and deallocate class 47 * metadata. When the space used for class metadata reaches a certain level 48 * (call it a high-water mark), a garbage collection is induced. 49 * 50 * The flag MetaspaceSize can be set higher to avoid early garbage collections 51 * induced for class metadata. The amount of class metadata allocated for 52 * an application is application dependent and general guidelines do not 53 * exist for the selection of MetaspaceSize. The default size of MetaspaceSize 54 * is platform dependent and ranges from 12 MB to about 20 MB. 55 * </pre> 56 */ 57 public class FirstGCTest extends MetaspaceBaseGC { 58 /** 59 * Current amount of the used metaspace 60 */ 61 protected long used = 0; 62 63 /** 64 * Current amount of the committed metaspace 65 */ 66 protected long committed = 0; 67 68 /** 69 * Previous amount of the used metaspace 70 */ 71 protected long p_used = 0 ; 72 73 /** 74 * Previous amount of the committed metaspace 75 */ 76 protected long p_committed = 0; 77 78 public static void main(String... args) { 79 new FirstGCTest().run(args); 80 } 81 82 // value given in -XX:metaspaceSize=<value> 83 private long metaspaceSize = -1; 84 85 86 @Override 87 protected void parseArgs(String[] args) { 88 final String XXSize = "-XX:MetaspaceSize="; 89 for (String va: vmArgs) { 90 if (va.startsWith(XXSize)) { 91 metaspaceSize = parseValue(va.substring(XXSize.length())); 92 } 93 } 94 } 95 96 @Override 97 protected String getPoolName() { 98 return "Metaspace"; 99 } 100 101 /** 102 * Check for the first GC moment. 103 * 104 * Eats memory until GC is invoked (amount of used metaspace became less); 105 * Checks that committed memory is close to MemaspaceSize. 106 * Eats memory until the second GC to check min/max ratio options have effect. 107 */ 108 @Override 109 public void doCheck() { 110 int gcCount = super.getMetaspaceGCCount(); 111 if (gcCount == 0) { 112 // gc hasn't happened yet. Start loading classes. 113 boolean gcHappened = this.eatMemoryUntilGC(50000); 114 if (!gcHappened) { 115 throw new Fault("GC hasn't happened"); 116 } 117 System.out.println("% GC: " + super.lastGCLogLine()); 118 System.out.println("% used : " + p_used + " --> " + used); 119 System.out.println("% committed: " + p_committed + " --> " + committed); 120 checkCommitted(p_committed); 121 } else { 122 // everything has happened before 123 checkCommitted(detectCommittedFromGCLog()); 124 } 125 } 126 127 /** 128 * Check that committed amount is close to expected value (MetaspaceSize) 129 * 130 * @param committedAmount - value to check 131 */ 132 void checkCommitted(long committedAmount) { 133 if (metaspaceSize > 0) { 134 // -XX:MetaspaceSize is given 135 if (Math.abs((int) (metaspaceSize - committedAmount)) < PAGE_SIZE) { 136 System.out.println("% GC happened at the right moment"); 137 return; 138 } 139 if (!isMetaspaceGC()) { 140 System.out.println("% GC wasn't induced by metaspace, cannot check the moment :("); 141 return; 142 } 143 System.err.println("%## GC happened at the wrong moment, " 144 + "the amount of committed space significantly differs " 145 + "from the expected amount"); 146 System.err.println("%## Real : " + committedAmount); 147 System.err.println("%## Exepcted: " + metaspaceSize); 148 throw new Fault("GC happened at the wrong moment"); 149 } else { 150 // -XX:MetaspaceSize is not given, check for default values 151 if (11_500_000 < committedAmount && committedAmount < 22_500_000) { 152 System.out.println("% GC happened when the committed amout was from 12 MB to about 20 MB."); 153 return; 154 } 155 if (!isMetaspaceGC()) { 156 System.out.println("% GC wasn't induced by metaspace, this is excuse"); 157 return; 158 } 159 System.err.println("%## GC happened at the wrong moment, " 160 + "the amount of committed space was expected from 12 MB to about 20 MB"); 161 System.err.println("%## Real : " + committedAmount); 162 throw new Fault("It was the wrong moment when GC happened"); 163 } 164 } 165 166 /** 167 * Load new classes without keeping references to them trying to provoke GC. 168 * Stops if GC is detected, or number of attempts exceeds the given limit. 169 * 170 * @param times limit of attempts to provoke GC 171 * @return true if GC has happened, false if limit has exceeded. 172 */ 173 protected boolean eatMemoryUntilGC(int times) { 174 System.out.println("%%%% Loading classes"); 175 System.out.println("% iter# : used : commited"); 176 System.out.println(".............................."); 177 for (int i = 1; i < times; i++) { 178 loadNewClasses(1, false); 179 if (i % 1000 == 0) { 180 printMemoryUsage("% " + i + " "); 181 } 182 p_used = used; 183 p_committed = committed; 184 used = getUsed(); 185 committed = getCommitted(); 186 187 if (used < p_used) { 188 return true; 189 } 190 } 191 return false; 192 } 193 194 /** 195 * If the first full GC has already happened we will try to detect 196 * the committed amount from the gc.log file. 197 * 198 * @return committed amount detected 199 * @throws Fault if failed to detect. 200 */ 201 protected long detectCommittedFromGCLog() { 202 // parse gc.log to extract the committed value from string like: 203 // Metaspace used 10133K, capacity 10190K, committed 10240K, reserved 10240Kl 204 System.out.println("%%%% Parsing gc log to detect the moment of the first GC"); 205 String format = ".*Metaspace.* used .*, capacity .*, committed (\\d+)([KMGkmg]), reserved .*"; 206 Pattern p = Pattern.compile(format); 207 try { 208 for (String line: readGCLog()) { 209 Matcher m = p.matcher(line); 210 if (m.matches()) { 211 int amount = Integer.parseInt(m.group(1)); 212 int multi = 1; 213 switch (m.group(2).toLowerCase()) { 214 case "k": multi = 1024; break; 215 case "m": multi = 1024*1024; break; 216 case "g": multi = 1024*1024*1024; break; 217 } 218 long value = amount * multi; 219 System.out.println("% Committed detected: " + value); 220 return value; 221 } 222 } 223 } catch (IOException e) { 224 throw new Fault("Cannot read from the GC log"); 225 } 226 System.out.println("% String that matches pattern '" + format + "' not found in the GC log file."); 227 throw new Fault("Unable to detect the moment of GC from log file"); 228 } 229 230 }