1 /*
   2  * Copyright (c) 2011, 2019, 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  * @bug     6173675
  27  * @summary Basic test of ThreadMXBean.getThreadAllocatedBytes
  28  * @author  Paul Hohensee
  29  */
  30 
  31 import java.lang.management.*;
  32 
  33 public class ThreadAllocatedMemory {
  34     private static com.sun.management.ThreadMXBean mbean =
  35         (com.sun.management.ThreadMXBean)ManagementFactory.getThreadMXBean();
  36     private static boolean testFailed = false;
  37     private static volatile boolean done = false;
  38     private static volatile boolean done1 = false;
  39     private static Object obj = new Object();
  40     private static final int NUM_THREADS = 10;
  41     private static Thread[] threads = new Thread[NUM_THREADS];
  42     private static long[] sizes = new long[NUM_THREADS];
  43 
  44     public static void main(String[] argv)
  45         throws Exception {
  46 
  47         if (!mbean.isThreadAllocatedMemorySupported()) {
  48             return;
  49         }
  50 
  51         // disable allocated memory measurement
  52         if (mbean.isThreadAllocatedMemoryEnabled()) {
  53             mbean.setThreadAllocatedMemoryEnabled(false);
  54         }
  55 
  56         if (mbean.isThreadAllocatedMemoryEnabled()) {
  57             throw new RuntimeException(
  58                 "ThreadAllocatedMemory is expected to be disabled");
  59         }
  60 
  61         Thread curThread = Thread.currentThread();
  62         long id = curThread.getId();
  63 
  64         long s = mbean.getThreadAllocatedBytes(id);
  65         if (s != -1) {
  66             throw new RuntimeException(
  67                 "Invalid ThreadAllocatedBytes returned = " +
  68                 s + " expected = -1");
  69         }
  70 
  71         // enable allocated memory measurement
  72         if (!mbean.isThreadAllocatedMemoryEnabled()) {
  73             mbean.setThreadAllocatedMemoryEnabled(true);
  74         }
  75 
  76         if (!mbean.isThreadAllocatedMemoryEnabled()) {
  77             throw new RuntimeException(
  78                 "ThreadAllocatedMemory is expected to be enabled");
  79         }
  80 
  81 
  82         // Test current thread two ways
  83 
  84 
  85         // First way, getCurrentThreadAllocatedBytes
  86 
  87         long size = mbean.getCurrentThreadAllocatedBytes();
  88         // implementation could have started measurement when
  89         // measurement was enabled, in which case size can be 0
  90         if (size < 0) {
  91             throw new RuntimeException(
  92                 "Invalid allocated bytes returned = " + size);
  93         }
  94 
  95         doit();
  96 
  97         // Expected to be size1 >= size
  98         long size1 = mbean.getThreadAllocatedBytes(id);
  99         if (size1 < size) {
 100             throw new RuntimeException("Allocated bytes " + size1 +
 101                 " expected >= " + size);
 102         }
 103         System.out.println(curThread.getName() +
 104             " Previous allocated bytes = " + size +
 105             " Current allocated bytes = " + size1);
 106 
 107         // back-to-back calls shouldn't allocate any memory
 108         size = mbean.getCurrentThreadAllocatedBytes();
 109         size1 = mbean.getCurrentThreadAllocatedBytes();
 110         if (size1 != size) {
 111             throw new RuntimeException("Allocated bytes " + size1 +
 112                 " expected == " + size);
 113         }
 114         System.out.println(curThread.getName() +
 115             " Previous allocated bytes = " + size +
 116             " Current allocated bytes = " + size1);
 117 
 118 
 119         // Second way, getThreadAllocatedBytes
 120 
 121         size = mbean.getThreadAllocatedBytes(id);
 122         // implementation could have started measurement when
 123         // measurement was enabled, in which case size can be 0
 124         if (size < 0) {
 125             throw new RuntimeException(
 126                 "Invalid allocated bytes returned = " + size);
 127         }
 128 
 129         doit();
 130 
 131         // Expected to be size1 >= size
 132         size1 = mbean.getThreadAllocatedBytes(id);
 133         if (size1 < size) {
 134             throw new RuntimeException("Allocated bytes " + size1 +
 135                 " expected >= " + size);
 136         }
 137         System.out.println(curThread.getName() +
 138             " Previous allocated bytes = " + size +
 139             " Current allocated bytes = " + size1);
 140 
 141         // back-to-back calls shouldn't allocate any memory
 142         size = mbean.getThreadAllocatedBytes(id);
 143         size1 = mbean.getThreadAllocatedBytes(id);
 144         if (size1 != size) {
 145             throw new RuntimeException("Allocated bytes " + size1 +
 146                 " expected == " + size);
 147         }
 148         System.out.println(curThread.getName() +
 149             " Previous allocated bytes = " + size +
 150             " Current allocated bytes = " + size1);
 151 
 152 
 153         // Test a single thread that isn't ourself
 154 
 155         // Start one thread, wait for it to block
 156         // after doing some allocation
 157         done = false; done1 = false;
 158         curThread = new MyThread("MyThread");
 159         curThread.start();
 160         id = curThread.getId();
 161         waitUntilThreadBlocked(curThread);
 162         size = mbean.getThreadAllocatedBytes(id);
 163 
 164         // let thread go to do some more allocation
 165         synchronized (obj) {
 166             done = true;
 167             obj.notifyAll();
 168         }
 169 
 170         // wait for thread to get going again and sample it
 171         goSleep(400);
 172         size1 = mbean.getThreadAllocatedBytes(id);
 173         if (size > size1) {
 174             throw new RuntimeException(
 175                 curThread.getName() +
 176                 " previous allocated bytes = " + size +
 177                 " > current allocated bytes = " + size1);
 178         }
 179         System.out.println(curThread.getName() +
 180             " Previous allocated bytes = " + size +
 181             " Current allocated bytes = " + size1);
 182 
 183         // let thread exit
 184         synchronized (obj) {
 185             done1 = true;
 186             obj.notifyAll();
 187         }
 188 
 189         try {
 190             curThread.join();
 191         } catch (InterruptedException e) {
 192             System.out.println("Unexpected exception is thrown.");
 193             e.printStackTrace(System.out);
 194             testFailed = true;
 195         }
 196         if (testFailed) {
 197             throw new RuntimeException("TEST FAILED");
 198         }
 199 
 200 
 201         // Test many threads
 202 
 203         // start threads, wait for them to block
 204         done = false; done1 = false;
 205         for (int i = 0; i < NUM_THREADS; i++) {
 206             threads[i] = new MyThread("MyThread-" + i);
 207             threads[i].start();
 208         }
 209 
 210         // threads block after doing some allocation
 211         waitUntilThreadsBlocked();
 212 
 213         for (int i = 0; i < NUM_THREADS; i++) {
 214             sizes[i] = mbean.getThreadAllocatedBytes(threads[i].getId());
 215         }
 216 
 217         // let threads go and do some more allocation
 218         synchronized (obj) {
 219             done = true;
 220             obj.notifyAll();
 221         }
 222 
 223         // wait for threads to get going again.  we don't care if we
 224         // catch them in mid-execution or if some of them haven't
 225         // restarted after we're done sleeping.
 226         goSleep(400);
 227 
 228         for (int i = 0; i < NUM_THREADS; i++) {
 229             long newSize = mbean.getThreadAllocatedBytes(threads[i].getId());
 230             if (sizes[i] > newSize) {
 231                 throw new RuntimeException(
 232                     threads[i].getName() +
 233                     " previous allocated bytes = " + sizes[i] +
 234                     " > current allocated bytes = " + newSize);
 235             }
 236             System.out.println(threads[i].getName() +
 237                 " Previous allocated bytes = " + sizes[i] +
 238                 " Current allocated bytes = " + newSize);
 239         }
 240 
 241         // let threads exit
 242         synchronized (obj) {
 243             done1 = true;
 244             obj.notifyAll();
 245         }
 246 
 247         for (int i = 0; i < NUM_THREADS; i++) {
 248             try {
 249                 threads[i].join();
 250             } catch (InterruptedException e) {
 251                 System.out.println("Unexpected exception is thrown.");
 252                 e.printStackTrace(System.out);
 253                 testFailed = true;
 254                 break;
 255             }
 256         }
 257         if (testFailed) {
 258             throw new RuntimeException("TEST FAILED");
 259         }
 260 
 261 
 262         System.out.println("Test passed");
 263     }
 264 
 265 
 266     private static void goSleep(long ms) throws Exception {
 267         try {
 268             Thread.sleep(ms);
 269         } catch (InterruptedException e) {
 270             System.out.println("Unexpected exception is thrown.");
 271             throw e;
 272         }
 273     }
 274 
 275     private static void waitUntilThreadBlocked(Thread thread)
 276         throws Exception {
 277         while (true) {
 278             goSleep(100);
 279             ThreadInfo info = mbean.getThreadInfo(thread.getId());
 280             if (info.getThreadState() == Thread.State.WAITING) {
 281                 break;
 282             }
 283         }
 284     }
 285 
 286     private static void waitUntilThreadsBlocked()
 287         throws Exception {
 288         int count = 0;
 289         while (count != NUM_THREADS) {
 290             goSleep(100);
 291             count = 0;
 292             for (int i = 0; i < NUM_THREADS; i++) {
 293                 ThreadInfo info = mbean.getThreadInfo(threads[i].getId());
 294                 if (info.getThreadState() == Thread.State.WAITING) {
 295                     count++;
 296                 }
 297             }
 298         }
 299     }
 300 
 301     public static void doit() {
 302         String tmp = "";
 303         long hashCode = 0;
 304         for (int counter = 0; counter < 1000; counter++) {
 305             tmp += counter;
 306             hashCode = tmp.hashCode();
 307         }
 308         System.out.println(Thread.currentThread().getName() +
 309                            " hashcode: " + hashCode);
 310     }
 311 
 312     static class MyThread extends Thread {
 313         public MyThread(String name) {
 314             super(name);
 315         }
 316 
 317         public void run() {
 318             ThreadAllocatedMemory.doit();
 319 
 320             synchronized (obj) {
 321                 while (!done) {
 322                     try {
 323                         obj.wait();
 324                     } catch (InterruptedException e) {
 325                         System.out.println("Unexpected exception is thrown.");
 326                         e.printStackTrace(System.out);
 327                         testFailed = true;
 328                         break;
 329                     }
 330                 }
 331             }
 332 
 333             long size1 = mbean.getThreadAllocatedBytes(getId());
 334             ThreadAllocatedMemory.doit();
 335             long size2 = mbean.getThreadAllocatedBytes(getId());
 336 
 337             System.out.println(getName() + ": " +
 338                 "ThreadAllocatedBytes  = " + size1 +
 339                 " ThreadAllocatedBytes  = " + size2);
 340 
 341             if (size1 > size2) {
 342                 throw new RuntimeException(getName() +
 343                     " ThreadAllocatedBytes = " + size1 +
 344                     " > ThreadAllocatedBytes = " + size2);
 345             }
 346 
 347             synchronized (obj) {
 348                 while (!done1) {
 349                     try {
 350                         obj.wait();
 351                     } catch (InterruptedException e) {
 352                         System.out.println("Unexpected exception is thrown.");
 353                         e.printStackTrace(System.out);
 354                         testFailed = true;
 355                         break;
 356                     }
 357                 }
 358             }
 359         }
 360     }
 361 }