1 /* 2 * Copyright (c) 2018, Google 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 package MyPackage; 25 26 import java.util.ArrayList; 27 import java.util.List; 28 import java.util.concurrent.BlockingQueue; 29 import java.util.concurrent.LinkedBlockingQueue; 30 31 /** API for handling heap allocating threads. */ 32 class ThreadInformation { 33 private Thread thread; 34 private BlockingQueue<Object> waitingQueue; 35 private Allocator allocator; 36 37 public ThreadInformation(Thread thread, BlockingQueue<Object> waitingQueue, 38 Allocator allocator) { 39 this.thread = thread; 40 this.waitingQueue = waitingQueue; 41 this.allocator = allocator; 42 } 43 44 public void waitForToken() { 45 allocator.waitForToken(); 46 } 47 48 public void stop() { 49 try { 50 allocator.stopRun(); 51 thread.join(); 52 53 if (!allocator.endedNormally()) { 54 throw new RuntimeException("Thread did not end normally..."); 55 } 56 57 } catch(InterruptedException e) { 58 throw new RuntimeException("Thread got interrupted..."); 59 } 60 } 61 62 private void start() { 63 allocator.start(); 64 } 65 66 public static void startThreads(List<ThreadInformation> threadList) { 67 for (ThreadInformation info : threadList) { 68 info.start(); 69 } 70 } 71 72 public static void stopThreads(List<ThreadInformation> threadList) { 73 for (ThreadInformation info : threadList) { 74 info.stop(); 75 } 76 } 77 78 public Thread getThread() { 79 return thread; 80 } 81 82 public static void waitForThreads(List<ThreadInformation> threadList) { 83 // Wait until all threads have put an object in the queue. 84 for (ThreadInformation info : threadList) { 85 info.waitForToken(); 86 } 87 } 88 89 public static List<ThreadInformation> createThreadList(int numThreads) { 90 List<ThreadInformation> threadList = new ArrayList<>(); 91 for (int i = 0 ; i < numThreads; i++) { 92 BlockingQueue<Object> queue = new LinkedBlockingQueue<>(); 93 Allocator allocator = new Allocator(i, queue); 94 Thread thread = new Thread(allocator, "Allocator" + i); 95 96 ThreadInformation info = new ThreadInformation(thread, queue, allocator); 97 threadList.add(info); 98 thread.start(); 99 } 100 return threadList; 101 } 102 } 103 104 class Allocator implements Runnable { 105 private int depth; 106 private List<int[]> currentList; 107 private BlockingQueue<Object> waitingQueue; 108 private BlockingQueue<Object> jobCanStop; 109 private boolean failed; 110 111 public Allocator(int depth, BlockingQueue<Object> waitingQueue) { 112 jobCanStop = new LinkedBlockingQueue<>(); 113 this.waitingQueue = waitingQueue; 114 this.depth = depth; 115 } 116 117 public boolean endedNormally() { 118 return !failed; 119 } 120 121 private void helper() { 122 List<int[]> newList = new ArrayList<>(); 123 // Let us assume that the array is 24 bytes of memory, by default we sample at 512k, keep in 124 // memory at least 2MB without counting the link-list itself, which adds to this. 125 int iterations = (1 << 21) / 24; 126 for (int i = 0; i < iterations; i++) { 127 int newTmp[] = new int[1]; 128 // Force it to be kept. 129 newList.add(newTmp); 130 } 131 132 // Replace old list with new list, which provokes two things: 133 // Old list will get GC'd at some point. 134 // New list forces that this thread has some allocations still sampled. 135 currentList = newList; 136 } 137 138 private void recursiveWrapper(int depth) { 139 if (depth > 0) { 140 recursiveWrapper(depth - 1); 141 } 142 helper(); 143 } 144 145 public void stopRun() throws InterruptedException { 146 jobCanStop.put(new Object()); 147 } 148 149 public void run() { 150 // Wait till we are told to really start. 151 waitForToken(); 152 153 for (int j = 0; j < 50; j++) { 154 recursiveWrapper(depth); 155 } 156 System.err.println("Here done allocating"); 157 158 try { 159 // Tell the main thread we are done. 160 waitingQueue.put(new Object()); 161 System.err.println("Waited for queue"); 162 163 // Wait until the main thread says we can stop. 164 jobCanStop.take(); 165 } catch (InterruptedException e) { 166 failed = true; 167 } 168 System.err.println("Done !!!!"); 169 } 170 171 public void waitForToken() { 172 try { 173 waitingQueue.take(); 174 } catch(InterruptedException e) { 175 throw new RuntimeException("Thread got interrupted..."); 176 } 177 } 178 179 public void start() { 180 try { 181 waitingQueue.put(new Object()); 182 } catch(InterruptedException e) { 183 throw new RuntimeException("Thread got interrupted..."); 184 } 185 } 186 }