LowMemoryTest2.java revision 9330:8b1f1c2a400f
1/*
2 * Copyright (c) 2004, 2013, 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 low memory detection of non-heap memory pool.
26 *
27 * The test set a listener to be notified when any of the non-heap pools
28 * exceed 80%. It then starts a thread that continuously loads classes.
29 * In the HotSpot implementation this causes metaspace to be consumed.
30 * Test completes when we the notification is received or an OutOfMemory
31 * is generated.
32 */
33
34import java.lang.management.*;
35import javax.management.*;
36import javax.management.openmbean.CompositeData;
37import java.util.*;
38
39public class LowMemoryTest2 {
40
41    private static volatile boolean listenerInvoked = false;
42
43    private static String INDENT = "    ";
44
45    static class SensorListener implements NotificationListener {
46        public void handleNotification(Notification notif, Object handback) {
47            String type = notif.getType();
48            if (type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED) ||
49                type.equals(MemoryNotificationInfo.
50                    MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) {
51                listenerInvoked = true;
52                MemoryNotificationInfo minfo = MemoryNotificationInfo.
53                    from((CompositeData) notif.getUserData());
54
55                System.out.print("Notification for " + minfo.getPoolName());
56                System.out.print(" [type = " + type);
57                System.out.println(" count = " + minfo.getCount() + "]");
58                System.out.println(INDENT + "usage = " + minfo.getUsage());
59            }
60        }
61    }
62
63    // Loads classes Test100001, Test100002, .... until OutOfMemoryErrror or
64    // low memory notification
65
66    static class BoundlessLoaderThread extends ClassLoader implements Runnable {
67
68        static int count = 100000;
69
70        Class loadNext() throws ClassNotFoundException {
71
72            // public class TestNNNNNN extends java.lang.Object{
73            // public TestNNNNNN();
74            //   Code:
75            //    0:    aload_0
76            //    1:    invokespecial   #1; //Method java/lang/Object."<init>":()V
77            //    4:    return
78            // }
79
80            int begin[] = {
81                0xca, 0xfe, 0xba, 0xbe, 0x00, 0x00, 0x00, 0x30,
82                0x00, 0x0a, 0x0a, 0x00, 0x03, 0x00, 0x07, 0x07,
83                0x00, 0x08, 0x07, 0x00, 0x09, 0x01, 0x00, 0x06,
84                0x3c, 0x69, 0x6e, 0x69, 0x74, 0x3e, 0x01, 0x00,
85                0x03, 0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43,
86                0x6f, 0x64, 0x65, 0x0c, 0x00, 0x04, 0x00, 0x05,
87                0x01, 0x00, 0x0a, 0x54, 0x65, 0x73, 0x74 };
88
89            int end [] = {
90                0x01, 0x00, 0x10,
91                0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e,
92                0x67, 0x2f, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
93                0x00, 0x21, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00,
94                0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x04,
95                0x00, 0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00,
96                0x00, 0x11, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
97                0x00, 0x05, 0x2a, 0xb7, 0x00, 0x01, 0xb1, 0x00,
98                0x00, 0x00, 0x00, 0x00, 0x00 };
99
100
101            // TestNNNNNN
102
103            int load_count = count++;
104            if (load_count > 999999) {
105                // The test will create a corrupt class file if the count
106                // exceeds 999999. Fix the test if this exception is thrown.
107                throw new RuntimeException("Load count exceeded");
108            }
109
110            String name = "Test" + Integer.toString(load_count);
111
112            byte value[];
113            try {
114                value = name.substring(4).getBytes("UTF-8");
115            } catch (java.io.UnsupportedEncodingException x) {
116                throw new Error();
117            }
118
119            // construct class file
120
121            int len = begin.length + value.length + end.length;
122            byte b[] = new byte[len];
123            int i, pos=0;
124            for (i=0; i<begin.length; i++) {
125                b[pos++] = (byte)begin[i];
126            }
127            for (i=0; i<value.length; i++) {
128                b[pos++] = value[i];
129            }
130            for (i=0; i<end.length; i++) {
131                b[pos++] = (byte)end[i];
132            }
133
134            return defineClass(name, b, 0, b.length);
135        }
136
137        /*
138         * Run method for thread that continuously loads classes.
139         *
140         * Note: Once the usage threshold has been exceeded the low memory
141         * detector thread will attempt to deliver its notification - this can
142         * potentially create a race condition with this thread contining to
143         * fill up metaspace. To avoid the low memory detector getting an
144         * OutOfMemory we throttle this thread once the threshold has been
145         * exceeded.
146         */
147        public void run() {
148            List pools = ManagementFactory.getMemoryPoolMXBeans();
149            boolean thresholdExceeded = false;
150
151            for (;;) {
152                try {
153                    // the classes are small so we load 10 at a time
154                    for (int i=0; i<10; i++) {
155                        loadNext();
156                    }
157                } catch (ClassNotFoundException x) {
158                    return;
159                }
160                if (listenerInvoked) {
161                    return;
162                }
163
164                // if threshold has been exceeded we put in a delay to allow
165                // the low memory detector do its job.
166                if (thresholdExceeded) {
167                    try {
168                        Thread.currentThread().sleep(100);
169                    } catch (InterruptedException x) { }
170                } else {
171                    // check if the threshold has been exceeded
172                    ListIterator i = pools.listIterator();
173                    while (i.hasNext()) {
174                        MemoryPoolMXBean p = (MemoryPoolMXBean) i.next();
175                        if (p.getType() == MemoryType.NON_HEAP &&
176                            p.isUsageThresholdSupported())
177                        {
178                            thresholdExceeded = p.isUsageThresholdExceeded();
179                        }
180                    }
181                }
182            }
183        }
184    }
185
186    public static void main(String args[]) {
187        ListIterator iter = ManagementFactory.getMemoryPoolMXBeans().listIterator();
188
189        // Set threshold of 80% of all NON_HEAP memory pools
190        // In the Hotspot implementation this means we should get a notification
191        // if the CodeCache or metaspace fills up.
192
193        while (iter.hasNext()) {
194            MemoryPoolMXBean p = (MemoryPoolMXBean) iter.next();
195            if (p.getType() == MemoryType.NON_HEAP && p.isUsageThresholdSupported()) {
196
197                // set threshold
198                MemoryUsage mu = p.getUsage();
199                long max = mu.getMax();
200                if (max < 0) {
201                    throw new RuntimeException("There is no maximum set for "
202                            + p.getName() + " memory pool so the test is invalid");
203                }
204                long threshold = (max * 80) / 100;
205
206                p.setUsageThreshold(threshold);
207
208                System.out.println("Selected memory pool for low memory " +
209                        "detection.");
210                MemoryUtil.printMemoryPool(p);
211
212            }
213        }
214
215
216        // Install the listener
217
218        MemoryMXBean mm = ManagementFactory.getMemoryMXBean();
219        SensorListener l2 = new SensorListener();
220
221        NotificationEmitter emitter = (NotificationEmitter) mm;
222        emitter.addNotificationListener(l2, null, null);
223
224        // Start the thread loading classes
225
226        Thread thr = new Thread(new BoundlessLoaderThread());
227        thr.start();
228
229        // Wait for the thread to terminate
230        try {
231            thr.join();
232        } catch (InterruptedException x) {
233            throw new RuntimeException(x);
234        }
235
236        if (listenerInvoked) {
237            System.out.println("Notification received - test passed.");
238        } else {
239            throw new RuntimeException("Test failed - notification not received!");
240        }
241    }
242
243}
244