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 static com.oracle.java.testlibrary.Asserts.assertLessThanOrEqual; 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; 40 41 public class TestShrinkAuxiliaryData { 42 43 private final static String[] initialOpts = new String[]{ 44 "-XX:MinHeapFreeRatio=10", 45 "-XX:MaxHeapFreeRatio=11", 46 "-XX:+UseG1GC", 47 "-XX:G1HeapRegionSize=1m", 48 "-XX:+PrintGCDetails" 49 }; 50 51 private final int RSetCacheSize; 52 53 protected TestShrinkAuxiliaryData(int RSetCacheSize) { 54 this.RSetCacheSize = RSetCacheSize; 55 } 56 57 protected void test() throws Exception { 58 ArrayList<String> vmOpts = new ArrayList(); 59 Collections.addAll(vmOpts, initialOpts); 60 61 int maxCacheSize = Math.max(0, Math.min(31, getMaxCacheSize())); 62 if (maxCacheSize < RSetCacheSize) { 63 System.out.format("Skiping test for %d cache size due max cache size %d", 64 RSetCacheSize, maxCacheSize 65 ); 66 return; 67 } 68 69 printTestInfo(maxCacheSize); 70 71 vmOpts.add("-XX:G1ConcRSLogCacheSize=" + RSetCacheSize); 72 73 vmOpts.addAll(Arrays.asList(Utils.getFilteredTestJavaOpts( 74 ShrinkAuxiliaryDataTest.prohibitedVmOptions))); 75 76 // for 32 bits ObjectAlignmentInBytes is not a option 77 if (Platform.is32bit()) { 78 ArrayList<String> vmOptsWithoutAlign = new ArrayList(vmOpts); 79 vmOptsWithoutAlign.add(ShrinkAuxiliaryDataTest.class.getName()); 80 performTest(vmOptsWithoutAlign); 81 return; 82 } 83 84 for (int alignment = 3; alignment <= 8; alignment++) { 85 ArrayList<String> vmOptsWithAlign = new ArrayList(vmOpts); 86 vmOptsWithAlign.add("-XX:ObjectAlignmentInBytes=" 87 + (int) Math.pow(2, alignment)); 88 vmOptsWithAlign.add(ShrinkAuxiliaryDataTest.class.getName()); 89 90 performTest(vmOptsWithAlign); 91 } 92 } 93 94 private void performTest(List<String> opts) throws Exception { 95 ProcessBuilder pb 96 = ProcessTools.createJavaProcessBuilder( 97 opts.toArray(new String[opts.size()]) 98 ); 99 100 OutputAnalyzer output = new OutputAnalyzer(pb.start()); 101 output.shouldHaveExitValue(0); 102 } 103 104 private void printTestInfo(int maxCacheSize) { 105 106 DecimalFormat grouped = new DecimalFormat("000,000"); 107 DecimalFormatSymbols formatSymbols = grouped.getDecimalFormatSymbols(); 108 formatSymbols.setGroupingSeparator(' '); 109 grouped.setDecimalFormatSymbols(formatSymbols); 110 111 System.out.format("Test will use %s bytes of memory of %s available%n" 112 + "Available memory is %s with %d bytes pointer size - can save %s pointers%n" 113 + "Max cache size: 2^%d = %s elements%n", 114 grouped.format(ShrinkAuxiliaryDataTest.getMemoryUsedByTest()), 115 grouped.format(Runtime.getRuntime().freeMemory()), 116 grouped.format(Runtime.getRuntime().freeMemory() 117 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest()), 118 Unsafe.ADDRESS_SIZE, 119 grouped.format((Runtime.getRuntime().freeMemory() 120 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest()) 121 / Unsafe.ADDRESS_SIZE), 122 maxCacheSize, 123 grouped.format((int) Math.pow(2, maxCacheSize)) 124 ); 125 } 126 127 /** 128 * Detects maximum possible size of G1ConcRSLogCacheSize available for 129 * current process based on maximum available process memory size 130 * 131 * @return power of two 132 */ 133 private static int getMaxCacheSize() { 134 long availableMemory = Runtime.getRuntime().freeMemory() 135 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest() - 1l; 136 if (availableMemory <= 0) { 137 return 0; 138 } 139 long availablePointersCount = availableMemory / Unsafe.ADDRESS_SIZE; 140 return (63 - (int) Long.numberOfLeadingZeros(availablePointersCount)); 141 } 142 143 static class ShrinkAuxiliaryDataTest { 144 145 public static void main(String[] args) throws IOException { 146 int iterateCount = DEFAULT_ITERATION_COUNT; 147 148 if (args.length > 0) { 149 try { 150 iterateCount = Integer.parseInt(args[0]); 151 } catch (NumberFormatException e) { 152 //num_iterate remains default 153 } 154 } 155 156 new ShrinkAuxiliaryDataTest().test(iterateCount); 157 } 158 159 class GarbageObject { 160 161 private final List<byte[]> payload = new ArrayList(); 162 private final List<GarbageObject> ref = new LinkedList(); 163 164 public GarbageObject(int size) { 165 payload.add(new byte[size]); 166 } 167 168 public void addRef(GarbageObject g) { 169 ref.add(g); 170 } 171 172 public void mutate() { 173 if (!payload.isEmpty() && payload.get(0).length > 0) { 174 payload.get(0)[0] = (byte) (Math.random() * Byte.MAX_VALUE); 175 } 176 } 177 } 178 179 private final List<GarbageObject> garbage = new ArrayList(); 180 181 public void test(int num_iterate) throws IOException { 182 183 allocate(); 184 link(); 185 mutate(); 186 deallocate(); 187 188 MemoryUsage muBeforeHeap 189 = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); 190 MemoryUsage muBeforeNonHeap 191 = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage(); 192 193 for (int i = 0; i < num_iterate; i++) { 194 allocate(); 195 link(); 196 mutate(); 197 deallocate(); 198 } 199 200 System.gc(); 201 MemoryUsage muAfterHeap 202 = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(); 203 MemoryUsage muAfterNonHeap 204 = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage(); 205 206 assertLessThanOrEqual(muAfterHeap.getCommitted(), muBeforeHeap.getCommitted(), 207 String.format("heap decommit failed - after > before: %d > %d", 208 muAfterHeap.getCommitted(), muBeforeHeap.getCommitted() 209 ) 210 ); 211 212 if (muAfterHeap.getCommitted() < muBeforeHeap.getCommitted()) { 213 assertLessThanOrEqual(muAfterNonHeap.getCommitted(), muBeforeNonHeap.getCommitted(), 214 String.format("non-heap decommit failed - after > before: %d > %d", 215 muAfterNonHeap.getCommitted(), muBeforeNonHeap.getCommitted() 216 ) 217 ); 218 } 219 } 220 221 private void allocate() { 222 for (int r = 0; r < REGIONS_TO_ALLOCATE; r++) { 223 for (int i = 0; i < NUM_OBJECTS_PER_REGION; i++) { 224 GarbageObject g = new GarbageObject(REGION_SIZE 225 / NUM_OBJECTS_PER_REGION); 226 garbage.add(g); 227 } 228 } 229 } 230 231 /** 232 * Iterate through all allocated objects, and link to objects in another 233 * regions 234 */ 235 private void link() { 236 for (int ig = 0; ig < garbage.size(); ig++) { 237 int regionNumber = ig / NUM_OBJECTS_PER_REGION; 238 239 for (int i = 0; i < NUM_LINKS; i++) { 240 int regionToLink; 241 do { 242 regionToLink = (int) (Math.random() 243 * REGIONS_TO_ALLOCATE); 244 } while (regionToLink == regionNumber); 245 246 // get random garbage object from random region 247 garbage.get(ig).addRef(garbage.get(regionToLink 248 * NUM_OBJECTS_PER_REGION + (int) (Math.random() 249 * NUM_OBJECTS_PER_REGION))); 250 } 251 } 252 } 253 254 private void mutate() { 255 for (int ig = 0; ig < garbage.size(); ig++) { 256 garbage.get(ig).mutate(); 257 } 258 } 259 260 private void deallocate() { 261 garbage.clear(); 262 System.gc(); 263 } 264 265 static long getMemoryUsedByTest() { 266 return REGIONS_TO_ALLOCATE * REGION_SIZE; 267 } 268 269 private static final int REGION_SIZE = 1024 * 1024; 270 private static final int DEFAULT_ITERATION_COUNT = 1; // iterate main scenario 271 private static final int REGIONS_TO_ALLOCATE = 5; 272 private static final int NUM_OBJECTS_PER_REGION = 10; 273 private static final int NUM_LINKS = 20; // how many links create for each object 274 275 private static final String[] prohibitedVmOptions = { 276 // remove this when @requires option will be on duty 277 "-XX:\\+UseParallelGC", 278 "-XX:\\+UseSerialGC", 279 "-XX:\\+UseConcMarkSweepGC", 280 "-XX:\\+UseParallelOldGC", 281 "-XX:\\+UseParNewGC", 282 "-Xconcgc", 283 "-Xincgc" 284 }; 285 } 286 }