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