1 /* 2 * Copyright (c) 2014, 2015, 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 import jdk.test.lib.Asserts; 25 import jdk.test.lib.OutputAnalyzer; 26 import jdk.test.lib.Platform; 27 import jdk.test.lib.ProcessTools; 28 import jdk.test.lib.Utils; 29 import java.io.IOException; 30 import java.lang.management.ManagementFactory; 31 import java.lang.management.MemoryUsage; 32 import java.text.DecimalFormat; 33 import java.text.DecimalFormatSymbols; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Collections; 37 import java.util.LinkedList; 38 import java.util.List; 39 import jdk.internal.misc.Unsafe; // for ADDRESS_SIZE 40 import sun.hotspot.WhiteBox; 41 42 public class TestShrinkAuxiliaryData { 43 44 private static final int REGION_SIZE = 1024 * 1024; 45 46 private final static String[] initialOpts = new String[]{ 47 "-XX:MinHeapFreeRatio=10", 48 "-XX:MaxHeapFreeRatio=11", 49 "-XX:+UseG1GC", 50 "-XX:G1HeapRegionSize=" + REGION_SIZE, 51 "-XX:-ExplicitGCInvokesConcurrent", 52 "-Xlog:gc=debug", 53 "-XX:+UnlockDiagnosticVMOptions", 54 "-XX:+WhiteBoxAPI", 55 "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", 56 "-Xbootclasspath/a:.", 57 }; 58 59 private final int hotCardTableSize; 60 61 protected TestShrinkAuxiliaryData(int hotCardTableSize) { 62 this.hotCardTableSize = hotCardTableSize; 63 } 64 65 protected void test() throws Exception { 66 ArrayList<String> vmOpts = new ArrayList(); 67 Collections.addAll(vmOpts, initialOpts); 68 69 int maxCacheSize = Math.max(0, Math.min(31, getMaxCacheSize())); 70 if (maxCacheSize < hotCardTableSize) { 71 System.out.format("Skiping test for %d cache size due max cache size %d", 72 hotCardTableSize, maxCacheSize 73 ); 74 return; 75 } 76 77 printTestInfo(maxCacheSize); 78 79 vmOpts.add("-XX:G1ConcRSLogCacheSize=" + hotCardTableSize); 80 81 // for 32 bits ObjectAlignmentInBytes is not a option 82 if (Platform.is32bit()) { 83 ArrayList<String> vmOptsWithoutAlign = new ArrayList(vmOpts); 84 vmOptsWithoutAlign.add(ShrinkAuxiliaryDataTest.class.getName()); 85 performTest(vmOptsWithoutAlign); 86 return; 87 } 88 89 for (int alignment = 3; alignment <= 8; alignment++) { 90 ArrayList<String> vmOptsWithAlign = new ArrayList(vmOpts); 91 vmOptsWithAlign.add("-XX:ObjectAlignmentInBytes=" 92 + (int) Math.pow(2, alignment)); 93 vmOptsWithAlign.add(ShrinkAuxiliaryDataTest.class.getName()); 94 95 performTest(vmOptsWithAlign); 96 } 97 } 98 99 private void performTest(List<String> opts) throws Exception { 100 ProcessBuilder pb 101 = ProcessTools.createJavaProcessBuilder(true, 102 opts.toArray(new String[opts.size()]) 103 ); 104 105 OutputAnalyzer output = new OutputAnalyzer(pb.start()); 106 System.out.println(output.getStdout()); 107 System.err.println(output.getStderr()); 108 output.shouldHaveExitValue(0); 109 } 110 111 private void printTestInfo(int maxCacheSize) { 112 113 DecimalFormat grouped = new DecimalFormat("000,000"); 114 DecimalFormatSymbols formatSymbols = grouped.getDecimalFormatSymbols(); 115 formatSymbols.setGroupingSeparator(' '); 116 grouped.setDecimalFormatSymbols(formatSymbols); 117 118 System.out.format( 119 "Test will use %s bytes of memory of %s available%n" 120 + "Available memory is %s with %d bytes pointer size - can save %s pointers%n" 121 + "Max cache size: 2^%d = %s elements%n", 122 grouped.format(ShrinkAuxiliaryDataTest.getMemoryUsedByTest()), 123 grouped.format(Runtime.getRuntime().maxMemory()), 124 grouped.format(Runtime.getRuntime().maxMemory() 125 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest()), 126 Unsafe.ADDRESS_SIZE, 127 grouped.format((Runtime.getRuntime().freeMemory() 128 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest()) 129 / Unsafe.ADDRESS_SIZE), 130 maxCacheSize, 131 grouped.format((int) Math.pow(2, maxCacheSize)) 132 ); 133 } 134 135 /** 136 * Detects maximum possible size of G1ConcRSLogCacheSize available for 137 * current process based on maximum available process memory size 138 * 139 * @return power of two 140 */ 141 private static int getMaxCacheSize() { 142 long availableMemory = Runtime.getRuntime().freeMemory() 143 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest() - 1l; 144 if (availableMemory <= 0) { 145 return 0; 146 } 147 148 long availablePointersCount = availableMemory / Unsafe.ADDRESS_SIZE; 149 return (63 - (int) Long.numberOfLeadingZeros(availablePointersCount)); 150 } 151 152 static class ShrinkAuxiliaryDataTest { 153 154 public static void main(String[] args) throws IOException { 155 156 ShrinkAuxiliaryDataTest testCase = new ShrinkAuxiliaryDataTest(); 157 158 if (!testCase.checkEnvApplicability()) { 159 return; 160 } 161 162 testCase.test(); 163 } 164 165 /** 166 * Checks is this environment suitable to run this test 167 * - memory is enough to decommit (page size is not big) 168 * - RSet cache size is not too big 169 * 170 * @return true if test could run, false if test should be skipped 171 */ 172 protected boolean checkEnvApplicability() { 173 174 int pageSize = WhiteBox.getWhiteBox().getVMPageSize(); 175 System.out.println( "Page size = " + pageSize 176 + " region size = " + REGION_SIZE 177 + " aux data ~= " + (REGION_SIZE * 3 / 100)); 178 // If auxdata size will be less than page size it wouldn't decommit. 179 // Auxiliary data size is about ~3.6% of heap size. 180 if (pageSize >= REGION_SIZE * 3 / 100) { 181 System.out.format("Skipping test for too large page size = %d", 182 pageSize 183 ); 184 return false; 185 } 186 187 if (REGION_SIZE * REGIONS_TO_ALLOCATE > Runtime.getRuntime().maxMemory()) { 188 System.out.format("Skipping test for too low available memory. " 189 + "Need %d, available %d", 190 REGION_SIZE * REGIONS_TO_ALLOCATE, 191 Runtime.getRuntime().maxMemory() 192 ); 193 return false; 194 } 195 196 return true; 197 } 198 199 class GarbageObject { 200 201 private final List<byte[]> payload = new ArrayList(); 202 private final List<GarbageObject> ref = new LinkedList(); 203 204 public GarbageObject(int size) { 205 payload.add(new byte[size]); 206 } 207 208 public void addRef(GarbageObject g) { 209 ref.add(g); 210 } 211 212 public void mutate() { 213 if (!payload.isEmpty() && payload.get(0).length > 0) { 214 payload.get(0)[0] = (byte) (Math.random() * Byte.MAX_VALUE); 215 } 216 } 217 } 218 219 private final List<GarbageObject> garbage = new ArrayList(); 220 221 public void test() throws IOException { 222 223 MemoryUsage muFull, muFree, muAuxDataFull, muAuxDataFree; 224 float auxFull, auxFree; 225 226 allocate(); 227 link(); 228 mutate(); 229 230 muFull = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); 231 long numUsedRegions = WhiteBox.getWhiteBox().g1NumMaxRegions() 232 - WhiteBox.getWhiteBox().g1NumFreeRegions(); 233 muAuxDataFull = WhiteBox.getWhiteBox().g1AuxiliaryMemoryUsage(); 234 auxFull = (float)muAuxDataFull.getUsed() / numUsedRegions; 235 236 System.out.format("Full aux data ratio= %f, regions max= %d, used= %d\n", 237 auxFull, WhiteBox.getWhiteBox().g1NumMaxRegions(), numUsedRegions 238 ); 239 240 deallocate(); 241 System.gc(); 242 243 muFree = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); 244 muAuxDataFree = WhiteBox.getWhiteBox().g1AuxiliaryMemoryUsage(); 245 246 numUsedRegions = WhiteBox.getWhiteBox().g1NumMaxRegions() 247 - WhiteBox.getWhiteBox().g1NumFreeRegions(); 248 auxFree = (float)muAuxDataFree.getUsed() / numUsedRegions; 249 250 System.out.format("Free aux data ratio= %f, regions max= %d, used= %d\n", 251 auxFree, WhiteBox.getWhiteBox().g1NumMaxRegions(), numUsedRegions 252 ); 253 254 Asserts.assertLessThanOrEqual(muFree.getCommitted(), muFull.getCommitted(), 255 String.format("heap decommit failed - full > free: %d > %d", 256 muFree.getCommitted(), muFull.getCommitted() 257 ) 258 ); 259 260 System.out.format("State used committed\n"); 261 System.out.format("Full aux data: %10d %10d\n", muAuxDataFull.getUsed(), muAuxDataFull.getCommitted()); 262 System.out.format("Free aux data: %10d %10d\n", muAuxDataFree.getUsed(), muAuxDataFree.getCommitted()); 263 264 // if decommited check that aux data has same ratio 265 if (muFree.getCommitted() < muFull.getCommitted()) { 266 Asserts.assertLessThanOrEqual(auxFree, auxFull, 267 String.format("auxiliary data decommit failed - full > free: %f > %f", 268 auxFree, auxFull 269 ) 270 ); 271 } 272 } 273 274 private void allocate() { 275 for (int r = 0; r < REGIONS_TO_ALLOCATE; r++) { 276 for (int i = 0; i < NUM_OBJECTS_PER_REGION; i++) { 277 GarbageObject g = new GarbageObject(REGION_SIZE 278 / NUM_OBJECTS_PER_REGION); 279 garbage.add(g); 280 } 281 } 282 } 283 284 /** 285 * Iterate through all allocated objects, and link to objects in another 286 * regions 287 */ 288 private void link() { 289 for (int ig = 0; ig < garbage.size(); ig++) { 290 int regionNumber = ig / NUM_OBJECTS_PER_REGION; 291 292 for (int i = 0; i < NUM_LINKS; i++) { 293 int regionToLink; 294 do { 295 regionToLink = (int) (Math.random() * REGIONS_TO_ALLOCATE); 296 } while (regionToLink == regionNumber); 297 298 // get random garbage object from random region 299 garbage.get(ig).addRef(garbage.get(regionToLink 300 * NUM_OBJECTS_PER_REGION + (int) (Math.random() 301 * NUM_OBJECTS_PER_REGION))); 302 } 303 } 304 } 305 306 private void mutate() { 307 for (int ig = 0; ig < garbage.size(); ig++) { 308 garbage.get(ig).mutate(); 309 } 310 } 311 312 private void deallocate() { 313 garbage.clear(); 314 System.gc(); 315 } 316 317 static long getMemoryUsedByTest() { 318 return REGIONS_TO_ALLOCATE * REGION_SIZE; 319 } 320 321 private static final int REGIONS_TO_ALLOCATE = 100; 322 private static final int NUM_OBJECTS_PER_REGION = 10; 323 private static final int NUM_LINKS = 20; // how many links create for each object 324 } 325 }