1 /* 2 * Copyright (c) 2014, 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 26 * @summary Stress test for malloc tracking 27 * @key nmt jcmd stress 28 * @library /test/lib 29 * @modules java.base/jdk.internal.misc 30 * java.management 31 * @build sun.hotspot.WhiteBox 32 * @ignore - This test is disabled since it will stress NMT and timeout during normal testing 33 * @run main ClassFileInstaller sun.hotspot.WhiteBox 34 * @run main/othervm/timeout=600 -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail MallocStressTest 35 */ 36 37 import java.util.concurrent.atomic.AtomicInteger; 38 import java.util.ArrayList; 39 import java.util.List; 40 import java.util.Random; 41 import jdk.test.lib.process.ProcessTools; 42 import jdk.test.lib.process.OutputAnalyzer; 43 import sun.hotspot.WhiteBox; 44 45 public class MallocStressTest { 46 private static int K = 1024; 47 48 // The stress test runs in three phases: 49 // 1. alloc: A lot of malloc with fewer free, which simulates a burst memory allocation 50 // that is usually seen during startup or class loading. 51 // 2. pause: Pause the test to check accuracy of native memory tracking 52 // 3. release: Release all malloc'd memory and check native memory tracking result. 53 public enum TestPhase { 54 alloc, 55 pause, 56 release 57 }; 58 59 static TestPhase phase = TestPhase.alloc; 60 61 // malloc'd memory 62 static ArrayList<MallocMemory> mallocd_memory = new ArrayList<MallocMemory>(); 63 static long mallocd_total = 0; 64 static WhiteBox whiteBox; 65 static AtomicInteger pause_count = new AtomicInteger(); 66 67 static boolean is_64_bit_system; 68 69 private static boolean is_64_bit_system() { return is_64_bit_system; } 70 71 public static void main(String args[]) throws Exception { 72 is_64_bit_system = (Platform.is64bit()); 73 74 OutputAnalyzer output; 75 whiteBox = WhiteBox.getWhiteBox(); 76 77 // Grab my own PID 78 String pid = Long.toString(ProcessTools.getProcessId()); 79 ProcessBuilder pb = new ProcessBuilder(); 80 81 AllocThread[] alloc_threads = new AllocThread[256]; 82 ReleaseThread[] release_threads = new ReleaseThread[64]; 83 84 int index; 85 // Create many allocation threads 86 for (index = 0; index < alloc_threads.length; index ++) { 87 alloc_threads[index] = new AllocThread(); 88 } 89 90 // Fewer release threads 91 for (index = 0; index < release_threads.length; index ++) { 92 release_threads[index] = new ReleaseThread(); 93 } 94 95 if (is_64_bit_system()) { 96 sleep_wait(2*60*1000); 97 } else { 98 sleep_wait(60*1000); 99 } 100 // pause the stress test 101 phase = TestPhase.pause; 102 while (pause_count.intValue() < alloc_threads.length + release_threads.length) { 103 sleep_wait(10); 104 } 105 106 long mallocd_total_in_KB = (mallocd_total + K / 2) / K; 107 108 // Now check if the result from NMT matches the total memory allocated. 109 String expected_test_summary = "Test (reserved=" + mallocd_total_in_KB +"KB, committed=" + mallocd_total_in_KB + "KB)"; 110 // Run 'jcmd <pid> VM.native_memory summary' 111 pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); 112 output = new OutputAnalyzer(pb.start()); 113 output.shouldContain(expected_test_summary); 114 115 // Release all allocated memory 116 phase = TestPhase.release; 117 synchronized(mallocd_memory) { 118 mallocd_memory.notifyAll(); 119 } 120 121 // Join all threads 122 for (index = 0; index < alloc_threads.length; index ++) { 123 try { 124 alloc_threads[index].join(); 125 } catch (InterruptedException e) { 126 } 127 } 128 129 for (index = 0; index < release_threads.length; index ++) { 130 try { 131 release_threads[index].join(); 132 } catch (InterruptedException e) { 133 } 134 } 135 136 // All test memory allocated should be released 137 output = new OutputAnalyzer(pb.start()); 138 output.shouldNotContain("Test (reserved="); 139 140 // Verify that tracking level has not been downgraded 141 pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "statistics"}); 142 output = new OutputAnalyzer(pb.start()); 143 output.shouldNotContain("Tracking level has been downgraded due to lack of resources"); 144 } 145 146 private static void sleep_wait(int n) { 147 try { 148 Thread.sleep(n); 149 } catch (InterruptedException e) { 150 } 151 } 152 153 154 static class MallocMemory { 155 private long addr; 156 private int size; 157 158 MallocMemory(long addr, int size) { 159 this.addr = addr; 160 this.size = size; 161 } 162 163 long addr() { return this.addr; } 164 int size() { return this.size; } 165 } 166 167 static class AllocThread extends Thread { 168 AllocThread() { 169 this.setName("MallocThread"); 170 this.start(); 171 } 172 173 // AllocThread only runs "Alloc" phase 174 public void run() { 175 Random random = new Random(); 176 while (MallocStressTest.phase == TestPhase.alloc) { 177 int r = Math.abs(random.nextInt()); 178 // Only malloc small amount to avoid OOM 179 int size = r % 32; 180 if (is_64_bit_system()) { 181 r = r % 32 * K; 182 } else { 183 r = r % 64; 184 } 185 if (size == 0) size = 1; 186 long addr = MallocStressTest.whiteBox.NMTMallocWithPseudoStack(size, r); 187 if (addr != 0) { 188 MallocMemory mem = new MallocMemory(addr, size); 189 synchronized(MallocStressTest.mallocd_memory) { 190 MallocStressTest.mallocd_memory.add(mem); 191 MallocStressTest.mallocd_total += size; 192 } 193 } else { 194 System.out.println("Out of malloc memory"); 195 break; 196 } 197 } 198 MallocStressTest.pause_count.incrementAndGet(); 199 } 200 } 201 202 static class ReleaseThread extends Thread { 203 private Random random = new Random(); 204 ReleaseThread() { 205 this.setName("ReleaseThread"); 206 this.start(); 207 } 208 209 public void run() { 210 while(true) { 211 switch(MallocStressTest.phase) { 212 case alloc: 213 slow_release(); 214 break; 215 case pause: 216 enter_pause(); 217 break; 218 case release: 219 quick_release(); 220 return; 221 } 222 } 223 } 224 225 private void enter_pause() { 226 MallocStressTest.pause_count.incrementAndGet(); 227 while (MallocStressTest.phase != MallocStressTest.TestPhase.release) { 228 try { 229 synchronized(MallocStressTest.mallocd_memory) { 230 MallocStressTest.mallocd_memory.wait(10); 231 } 232 } catch (InterruptedException e) { 233 } 234 } 235 } 236 237 private void quick_release() { 238 List<MallocMemory> free_list; 239 while (true) { 240 synchronized(MallocStressTest.mallocd_memory) { 241 if (MallocStressTest.mallocd_memory.isEmpty()) return; 242 int size = Math.min(MallocStressTest.mallocd_memory.size(), 5000); 243 List<MallocMemory> subList = MallocStressTest.mallocd_memory.subList(0, size); 244 free_list = new ArrayList<MallocMemory>(subList); 245 subList.clear(); 246 } 247 for (int index = 0; index < free_list.size(); index ++) { 248 MallocMemory mem = free_list.get(index); 249 MallocStressTest.whiteBox.NMTFree(mem.addr()); 250 } 251 } 252 } 253 254 private void slow_release() { 255 try { 256 Thread.sleep(10); 257 } catch (InterruptedException e) { 258 } 259 synchronized(MallocStressTest.mallocd_memory) { 260 if (MallocStressTest.mallocd_memory.isEmpty()) return; 261 int n = Math.abs(random.nextInt()) % MallocStressTest.mallocd_memory.size(); 262 MallocMemory mem = mallocd_memory.remove(n); 263 MallocStressTest.whiteBox.NMTFree(mem.addr()); 264 MallocStressTest.mallocd_total -= mem.size(); 265 } 266 } 267 } 268 }