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