blob: daf56e46965cd96b175625622844ba8abba64fd6 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2004 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 * @test
26 * @bug 4530538
27 * @summary Basic unit test of memory management testing:
28 * 1) setUsageThreshold() and getUsageThreshold()
29 * 2) test low memory detection on the old generation.
30 *
31 * @author Mandy Chung
32 *
33 * @build LowMemoryTest MemoryUtil
34 * @run main/timeout=600 LowMemoryTest
35 */
36
37import java.lang.management.*;
38import java.util.*;
39import javax.management.*;
40import javax.management.openmbean.CompositeData;
41
42public class LowMemoryTest {
43 private static MemoryMXBean mm = ManagementFactory.getMemoryMXBean();
44 private static List pools = ManagementFactory.getMemoryPoolMXBeans();
45 private static List managers = ManagementFactory.getMemoryManagerMXBeans();
46 private static MemoryPoolMXBean mpool = null;
47 private static boolean trace = false;
48 private static boolean testFailed = false;
49 private static final int NUM_TRIGGERS = 5;
50 private static final int NUM_CHUNKS = 2;
51 private static long chunkSize;
52
53 private static boolean listenerInvoked = false;
54 static class SensorListener implements NotificationListener {
55 public void handleNotification(Notification notif, Object handback) {
56 String type = notif.getType();
57 if (type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED) ||
58 type.equals(MemoryNotificationInfo.
59 MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) {
60
61 MemoryNotificationInfo minfo = MemoryNotificationInfo.
62 from((CompositeData) notif.getUserData());
63
64 MemoryUtil.printMemoryNotificationInfo(minfo, type);
65 listenerInvoked = true;
66 }
67 }
68 }
69
70 static class TestListener implements NotificationListener {
71 private int triggers = 0;
72 private long[] count = new long[NUM_TRIGGERS * 2];
73 private long[] usedMemory = new long[NUM_TRIGGERS * 2];
74 public void handleNotification(Notification notif, Object handback) {
75 MemoryNotificationInfo minfo = MemoryNotificationInfo.
76 from((CompositeData) notif.getUserData());
77 count[triggers] = minfo.getCount();
78 usedMemory[triggers] = minfo.getUsage().getUsed();
79 triggers++;
80 }
81 public void checkResult() throws Exception {
82 if (triggers != NUM_TRIGGERS) {
83 throw new RuntimeException("Unexpected number of triggers = " +
84 triggers + " but expected to be " + NUM_TRIGGERS);
85 }
86
87 for (int i = 0; i < triggers; i++) {
88 if (count[i] != i+1) {
89 throw new RuntimeException("Unexpected count of" +
90 " notification #" + i +
91 " count = " + count[i] +
92 " but expected to be " + (i+1));
93 }
94 if (usedMemory[i] < newThreshold) {
95 throw new RuntimeException("Used memory = " +
96 usedMemory[i] + " is less than the threshold = " +
97 newThreshold);
98 }
99 }
100 }
101 }
102
103 private static long newThreshold;
104 public static void main(String args[]) throws Exception {
105 if (args.length > 0 && args[0].equals("trace")) {
106 trace = true;
107 }
108
109 // Find the Old generation which supports low memory detection
110 ListIterator iter = pools.listIterator();
111 while (iter.hasNext()) {
112 MemoryPoolMXBean p = (MemoryPoolMXBean) iter.next();
113 if (p.getType() == MemoryType.HEAP &&
114 p.isUsageThresholdSupported()) {
115 mpool = p;
116 if (trace) {
117 System.out.println("Selected memory pool for low memory " +
118 "detection.");
119 MemoryUtil.printMemoryPool(mpool);
120 }
121 break;
122 }
123 }
124
125 TestListener listener = new TestListener();
126 SensorListener l2 = new SensorListener();
127 NotificationEmitter emitter = (NotificationEmitter) mm;
128 emitter.addNotificationListener(listener, null, null);
129 emitter.addNotificationListener(l2, null, null);
130
131 Thread allocator = new AllocatorThread();
132 Thread sweeper = new SweeperThread();
133
134 // Now set threshold
135 MemoryUsage mu = mpool.getUsage();
136 chunkSize = (mu.getMax() - mu.getUsed()) / 20;
137 newThreshold = mu.getUsed() + (chunkSize * NUM_CHUNKS);
138
139 System.out.println("Setting threshold for " + mpool.getName() +
140 " from " + mpool.getUsageThreshold() + " to " + newThreshold +
141 ". Current used = " + mu.getUsed());
142 mpool.setUsageThreshold(newThreshold);
143
144 if (mpool.getUsageThreshold() != newThreshold) {
145 throw new RuntimeException("TEST FAILED: " +
146 "Threshold for Memory pool " + mpool.getName() +
147 "is " + mpool.getUsageThreshold() + " but expected to be" +
148 newThreshold);
149 }
150
151 allocator.start();
152 sweeper.start();
153
154 try {
155 allocator.join();
156 sweeper.join();
157 } catch (InterruptedException e) {
158 e.printStackTrace();
159 System.out.println("Unexpected exception.");
160 testFailed = true;
161 }
162
163 listener.checkResult();
164
165 if (testFailed)
166 throw new RuntimeException("TEST FAILED.");
167
168 System.out.println("Test passed.");
169
170 }
171
172 private static void goSleep(long ms) {
173 try {
174 Thread.sleep(ms);
175 } catch (InterruptedException e) {
176 e.printStackTrace();
177 System.out.println("Unexpected exception.");
178 testFailed = true;
179 }
180 }
181
182 private static Object go = new Object();
183 private static boolean waiting = false; // No thread is waiting.
184
185 // Synchronizes two thread. If no thread is waiting then wait
186 // for notification from a different thread and if it is
187 // is waiting then send notification.
188 // In this test case this method is used to synchronize sweeper
189 // thread and alocater thread to reach a particular point.
190 private static void wait_or_notify() {
191 synchronized (go) {
192 if (waiting == false) {
193 waiting = true;
194 System.out.println(" Waiting ");
195 try {
196 go.wait();
197 } catch (InterruptedException e) {
198 e.printStackTrace();
199 testFailed = true;
200 }
201 waiting = false;
202 } else {
203 System.out.println(" Notify ");
204 go.notify();
205 }
206 }
207 }
208
209 private static List objectPool = new ArrayList();
210 static class AllocatorThread extends Thread {
211 public void doTask() {
212 int iterations = 0;
213 int numElements = (int) (chunkSize / 4); // minimal object size
214 while (!listenerInvoked) {
215 iterations++;
216 if (trace) {
217 System.out.println(" Iteration " + iterations +
218 ": before allocation " +
219 mpool.getUsage().getUsed());
220 }
221
222 Object[] o = new Object[numElements];
223 if (iterations <= NUM_CHUNKS) {
224 // only hold a reference to the first NUM_CHUNKS
225 // allocated objects
226 objectPool.add(o);
227 }
228
229 if (trace) {
230 System.out.println(" " +
231 " after allocation " +
232 mpool.getUsage().getUsed());
233 }
234 goSleep(100);
235 }
236 }
237 public void run() {
238 for (int i = 1; i <= NUM_TRIGGERS; i++) {
239 System.out.println("AllocatorThread is doing task " + i);
240 doTask();
241 synchronized (sweep) {
242 sweep.notify();
243 }
244 // System.out.print(" Allocater Thread ");
245 // If sweeper thread is waiting then send notify
246 // else wait for notification from sweeper thread.
247 wait_or_notify();
248 if (testFailed) return;
249 }
250 }
251 }
252
253 private static Object sweep = new Object();
254 static class SweeperThread extends Thread {
255 private void doTask() {
256 for (; mpool.getUsage().getUsed() >=
257 mpool.getUsageThreshold();) {
258 // clear all allocated objects and invoke GC
259 objectPool.clear();
260 mm.gc();
261 goSleep(100);
262 }
263 }
264 public void run() {
265 for (int i = 1; i <= NUM_TRIGGERS; i++) {
266 synchronized (sweep) {
267 while (!listenerInvoked) {
268 try {
269 sweep.wait();
270 } catch (InterruptedException e) {
271 e.printStackTrace();
272 System.out.println("Unexpected exception.");
273 testFailed = true;
274 }
275 }
276 }
277 System.out.println("SweepThread is doing task " + i);
278 doTask();
279
280 listenerInvoked = false;
281
282 // System.out.print(" Sweeper Thread ");
283 // If Allocater thread is waiting wait send notify
284 // else wait for notfication from allocater thread.
285 wait_or_notify();
286 if (testFailed) return;
287 }
288 }
289 }
290}