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