1 /*
2  * Copyright (c) 2011, 2020, 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 package nsk.monitoring.ThreadMXBean;
25 
26 import java.util.ArrayList;
27 import java.util.List;
28 import nsk.share.gc.gp.GarbageProducer;
29 import nsk.share.gc.gp.GarbageUtils;
30 import nsk.share.test.Stresser;
31 import nsk.share.test.LocalRandom;
32 
33 
34 public class MXBeanTestThread extends Thread {
35 
36     /**
37      * BarrierHandler instance for synchronization with management thread
38      */
39     protected BarrierHandler handler;
40 
41     /**
42      * List where allocated objects are stored
43      */
44     private List<Object> allocatedList;
45     /**
46      * Number of simultaneously running threads
47      */
48     private static int threadCount;
49     /**
50      * Expected amount of memory allocated during stress test
51      */
52     private long stressAllocatedBytes;
53     /**
54      * Maximum memory in bytes that one thread is allowed to use at once
55      */
56     private long maxThreadMemory = 0;
57     /**
58      * GarbageProducer for objects creation
59      */
60     private GarbageProducer gp;
61     /**
62      * Stresser instance for allocateStress()
63      */
64     private Stresser stresser;
65 
warmUp(String garbageProducerId)66     public static void warmUp(String garbageProducerId) {
67         MXBeanTestThread warmUpThread = new MXBeanTestThread(garbageProducerId) {
68                 @Override
69                 public void doWork() {
70                     allocate();
71                 }
72             };
73         warmUpThread.start();
74         do {
75             try {
76                 warmUpThread.join();
77             } catch (InterruptedException ie) {}
78         } while(warmUpThread.isAlive());
79     }
80 
81     /**
82      * Sets BarrierHandler for this thread
83      *
84      * @param handler BarrierHandler to synchronize with
85      */
setHandler(BarrierHandler handler)86     public void setHandler(BarrierHandler handler) {
87         this.handler = handler;
88     }
89 
90     /**
91      * Returns an instance of MXBeanTestThread with already defined
92      * allocatedList List and GarbageProducer
93      */
MXBeanTestThread(String garbageProducerId)94     public MXBeanTestThread(String garbageProducerId) {
95         super(Integer.toString(++threadCount));
96         allocatedList = new ArrayList<Object>();
97         gp = GarbageUtils.getGarbageProducer(garbageProducerId);
98         maxThreadMemory = Runtime.getRuntime().maxMemory()/4;
99     }
100 
101     /**
102      * Returns an instance of MXBeanTestThread with already defined
103      * allocatedList List and default GarbageProducer
104      */
MXBeanTestThread()105     public MXBeanTestThread() {
106         this("intArr");
107     }
108 
109     /**
110      * Returns an instance of MXBeanTestThread with already defined
111      * allocatedList List and Stresser
112      */
MXBeanTestThread(Stresser stresser)113     public MXBeanTestThread(Stresser stresser) {
114         this("intArr");
115         this.stresser = stresser;
116     }
117 
118     /**
119      * Sets maximum amount of memory that could be used at once for each
120      * TestThread in StressTest
121      */
setMaxThreadMemory(long memory)122     public void setMaxThreadMemory (long memory) {
123         maxThreadMemory = memory;
124     }
125 
126     /**
127      * Returns expected memory allocated by TestThread during StressTest
128      * @return expected memory amount
129      */
getStressAllocatedBytes()130     public long getStressAllocatedBytes() {
131         return stressAllocatedBytes;
132     }
133 
134     @Override
run()135     public void run() {
136         doWork();
137     }
138 
139     /**
140      * Implementation of TestThread behavior logic
141      */
doWork()142     public void doWork() {
143         handler.ready();
144         allocate();
145         handler.ready();
146     }
147 
148     /**
149      * Allocates memory for amount of time specified in Stresser instance
150      */
allocateStress()151     protected void allocateStress() {
152         // Size of long[] array that allocates 2 Mb + 1 byte
153         int MAX_ARR_SIZE=262146;
154         // Anount of memory allocated by thread with existing links
155         // Which means that these objects can't be collected by GC
156         long actuallyAllocated = 0;
157         // ensure LocalRandom is loaded and has enough memory
158         LocalRandom.init();
159         try {
160             while (stresser.continueExecution()) {
161                 //int chunkSize = LocalRandom.nextInt(MAX_OBJECT_SIZE);
162                 //Object obj = gp.create(chunkSize);
163                 int chunkSize = LocalRandom.nextInt(MAX_ARR_SIZE);
164                 Object obj = new long[chunkSize];
165                 allocatedList.add(obj);
166                 actuallyAllocated += chunkSize*8;
167                 if (actuallyAllocated > maxThreadMemory) {
168                     // Allocated more then allowed to one thread
169                     // re-define allocatedList to allow GC to delete
170                     // created objects
171                     stressAllocatedBytes += actuallyAllocated;
172                     actuallyAllocated = 0;
173                     allocatedList = new ArrayList<Object>();
174                 }
175             }
176         } catch (OutOfMemoryError e) {
177         } finally {
178             stressAllocatedBytes += actuallyAllocated;
179         }
180     }
181 
182     /**
183      * Allocates memory once according test settings
184      */
allocate()185     protected void allocate() {
186         long actuallyAllocated = 0;
187         for (int i = 0; i < ThreadMXBeanTestBase.allocArr.length; i++) {
188             long size = ThreadMXBeanTestBase.allocArr[i];
189             if (actuallyAllocated + size > maxThreadMemory) {
190                 break;
191             }
192             allocatedList.add(gp.create(size));
193             actuallyAllocated += size;
194         }
195     }
196 }
197