1 /* 2 * Copyright (c) 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 /* 25 * @test TestGreyReclaimedHumongousObjects.java 26 * @bug 8069367 27 * @requires vm.gc == "G1" | vm.gc == "null" 28 * @summary Test handling of marked but unscanned reclaimed humongous objects. 29 * @key gc 30 * @run main/othervm -XX:+UseG1GC -Xss32m -Xmx128m -XX:G1HeapRegionSize=1m 31 * -XX:+UnlockExperimentalVMOptions 32 * -XX:+G1EagerReclaimHumongousObjects 33 * -XX:+G1EagerReclaimHumongousObjectsWithStaleRefs 34 * TestGreyReclaimedHumongousObjects 1048576 90 35 */ 36 37 // This test spawns a bunch of threads, each of them rapidly 38 // allocating large objects and storing them into a circular buffer 39 // associated with the thread. The circular buffer results in these 40 // objects becoming dead in fairly short order. 41 // 42 // The situation we're trying to provoke is 43 // 44 // (1) A humongous object H is marked and added to the mark stack. 45 // 46 // (2) An evacuation pause determines H is no longer live, and 47 // reclaims it. This occurs before concurrent marking has gotten 48 // around to processing the mark stack entry for H. 49 // 50 // (3) Concurrent marking processes the mark stack entry for H. The 51 // bug is that it would attempt to scan the now dead object. 52 // 53 // Unfortunately, this test is *very* sensitive to configuration. 54 // Among the parameters that affect whether / how often we'll get into 55 // the desired situation within a reasonable amount of time are: 56 // 57 // - THREAD_COUNT: The number of allocating threads. 58 // 59 // - OLD_COUNT: The number of objects each thread keeps. 60 // 61 // - MAX_MEMORY: The maximum heap size. 62 // 63 // - G1HeapRegionSize 64 // 65 // - The size of the objects being allocated. 66 // 67 // The parameter values specified here: 68 // 69 // - THREAD_COUNT = 12 70 // - OLD_COUNT == 4 71 // - MAX_MEMORY == 128m 72 // - G1HeapRegionSize = 1m 73 // - Object size = 1048576 (2 regions after header overhead and roundup) 74 // 75 // seems to work well at provoking the desired state fairly quickly. 76 // Even relatively small perturbations may change that. The key 77 // factors seem to be keeping the heap mostly full of live objects but 78 // having them become dead fairly quickly. 79 80 import java.util.Date; 81 import java.util.concurrent.ExecutorService; 82 import java.util.concurrent.Executors; 83 import java.util.concurrent.ThreadFactory; 84 import java.util.concurrent.TimeUnit; 85 import sun.management.ManagementFactoryHelper; 86 import com.sun.management.HotSpotDiagnosticMXBean; 87 import com.sun.management.VMOption; 88 89 public class TestGreyReclaimedHumongousObjects { 90 91 static class NamedThreadFactory implements ThreadFactory { 92 private int threadNum = 0; 93 94 @Override 95 public Thread newThread(Runnable r) { 96 return new Thread(r, THREAD_NAME + (threadNum++)); 97 } 98 } 99 100 static class Runner extends Thread { 101 private final Date startDate = new Date(); 102 private final int obj_size; 103 private final Object[] old_garbage; 104 private int old_index = 0; 105 106 public Runner(int obj_size) { 107 this.obj_size = obj_size; 108 old_garbage = new Object[OLD_COUNT]; 109 } 110 111 private void allocate_garbage() { 112 byte[] garbage = new byte[obj_size]; 113 old_garbage[Math.abs(++old_index % OLD_COUNT)] = garbage; 114 } 115 116 @Override 117 public void run() { 118 try { 119 while (!isInterrupted()) { 120 allocate_garbage(); 121 Thread.sleep(0); // Yield, to ensure interruptable. 122 } 123 } catch (InterruptedException e) { 124 System.out.println("Aborted after " 125 + (new Date().getTime() - startDate.getTime()) 126 + " ms"); 127 interrupt(); 128 } 129 } 130 } 131 132 public static void main(String[] args) throws Exception { 133 HotSpotDiagnosticMXBean diagnostic = ManagementFactoryHelper.getDiagnosticMXBean(); 134 135 System.out.println("Max memory= " + MAX_MEMORY + " bytes"); 136 137 int obj_size = 0; 138 long seconds_to_run = 0; 139 if (args.length != 2) { 140 throw new RuntimeException("Object size argument must be supplied"); 141 } else { 142 obj_size = Integer.parseInt(args[0]); 143 seconds_to_run = Integer.parseInt(args[1]); 144 } 145 System.out.println("Objects size= " + obj_size + " bytes"); 146 System.out.println("Seconds to run=" + seconds_to_run); 147 148 int region_size = 149 Integer.parseInt(diagnostic.getVMOption("G1HeapRegionSize").getValue()); 150 if (obj_size < (region_size / 2)) { 151 throw new RuntimeException("Object size " + obj_size + 152 " is not humongous with region size " + region_size); 153 } 154 155 ExecutorService executor = 156 Executors.newFixedThreadPool(THREAD_COUNT, new NamedThreadFactory()); 157 System.out.println("Starting " + THREAD_COUNT + " threads"); 158 159 for (int i = 0; i < THREAD_COUNT; i++) { 160 executor.execute(new Runner(obj_size)); 161 } 162 163 Thread.sleep(seconds_to_run * 1000); 164 executor.shutdownNow(); 165 166 if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { 167 System.err.println("Thread pool did not terminate after 10 seconds after shutdown"); 168 } 169 } 170 171 private static final long MAX_MEMORY = Runtime.getRuntime().maxMemory(); 172 private static final int OLD_COUNT = 4; 173 private static final int THREAD_COUNT = 12; 174 private static final String THREAD_NAME = "TestGreyRH-"; 175 } 176