1/*
2 * Copyright (c) 2003, 2016, 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     4530538 6980984
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 * @modules jdk.management
34 * @build MemoryManagement MemoryUtil
35 * @run main/othervm/timeout=600 -Xmn8m -XX:+IgnoreUnrecognizedVMOptions -XX:G1HeapRegionSize=1 -XX:-UseLargePages MemoryManagement
36 */
37
38import java.lang.management.*;
39import java.util.*;
40import javax.management.*;
41import javax.management.openmbean.CompositeData;
42
43public class MemoryManagement {
44    private static final MemoryMXBean mm = ManagementFactory.getMemoryMXBean();
45    private static final List pools =
46            Collections.synchronizedList(ManagementFactory.getMemoryPoolMXBeans());
47    private static final List managers =
48            Collections.synchronizedList(ManagementFactory.getMemoryManagerMXBeans());
49    private static volatile MemoryPoolMXBean mpool = null;
50    private static volatile boolean trace = false;
51    private static volatile boolean testFailed = false;
52    private static final int NUM_CHUNKS = 2;
53    // Must match -Xmn set on the @run line
54    private static final int YOUNG_GEN_SIZE = 8 * 1024 * 1024;
55    private static volatile long chunkSize;
56    private static volatile int listenerInvoked = 0;
57
58    static class SensorListener implements NotificationListener {
59        public void handleNotification(Notification notif, Object handback) {
60            String type = notif.getType();
61            if (type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED) ||
62                type.equals(MemoryNotificationInfo.
63                    MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) {
64
65                MemoryNotificationInfo minfo = MemoryNotificationInfo.
66                    from((CompositeData) notif.getUserData());
67
68                MemoryUtil.printMemoryNotificationInfo(minfo, type);
69                listenerInvoked++;
70            }
71        }
72    }
73
74    private static long newThreshold;
75    public static void main(String args[]) throws Exception {
76        if (args.length > 0 && args[0].equals("trace")) {
77            trace = true;
78        }
79
80        if (trace) {
81            MemoryUtil.printMemoryPools(pools);
82            MemoryUtil.printMemoryManagers(managers);
83        }
84
85        // Find the Old generation which supports low memory detection
86        ListIterator iter = pools.listIterator();
87        while (iter.hasNext()) {
88            MemoryPoolMXBean p = (MemoryPoolMXBean) iter.next();
89            if (p.getType() == MemoryType.HEAP &&
90                p.isUsageThresholdSupported()) {
91                mpool = p;
92                if (trace) {
93                    System.out.println("Selected memory pool for low memory " +
94                        "detection.");
95                    MemoryUtil.printMemoryPool(mpool);
96                }
97                break;
98            }
99        }
100
101        SensorListener listener = new SensorListener();
102        NotificationEmitter emitter = (NotificationEmitter) mm;
103        emitter.addNotificationListener(listener, null, null);
104
105        Thread allocator = new AllocatorThread();
106
107        // The chunk size needs to be larger than YOUNG_GEN_SIZE,
108        // otherwise we will get intermittent failures when objects
109        // end up in the young gen instead of the old gen.
110        final long epsilon = 1024;
111        chunkSize = YOUNG_GEN_SIZE + epsilon;
112
113        // Now set threshold
114        MemoryUsage mu = mpool.getUsage();
115        newThreshold = mu.getUsed() + (chunkSize * NUM_CHUNKS);
116
117        // Sanity check. Make sure the new threshold isn't too large.
118        // Tweak the test if this fails.
119        final long headRoom = chunkSize * 2;
120        final long max = mu.getMax();
121        if (max != -1 && newThreshold > max - headRoom) {
122            throw new RuntimeException("TEST FAILED: newThreshold: " + newThreshold +
123                    " is too near the maximum old gen size: " + max +
124                    " used: " + mu.getUsed() + " headRoom: " + headRoom);
125        }
126
127        System.out.println("Setting threshold for " + mpool.getName() +
128            " from " + mpool.getUsageThreshold() + " to " + newThreshold +
129            ".  Current used = " + mu.getUsed());
130        mpool.setUsageThreshold(newThreshold);
131
132        if (mpool.getUsageThreshold() != newThreshold) {
133            throw new RuntimeException("TEST FAILED: " +
134                "Threshold for Memory pool " + mpool.getName() +
135                "is " + mpool.getUsageThreshold() + " but expected to be" +
136                newThreshold);
137        }
138
139        // Start the AllocatorThread to continously allocate memory
140        System.out.println("Starting an AllocatorThread to allocate memory.");
141        allocator.start();
142
143        try {
144            allocator.join();
145        } catch (InterruptedException e) {
146            e.printStackTrace();
147            System.out.println("Unexpected exception.");
148            testFailed = true;
149        }
150
151        if (listenerInvoked == 0) {
152            throw new RuntimeException("No listener is invoked");
153        }
154
155        if (testFailed)
156            throw new RuntimeException("TEST FAILED.");
157
158        System.out.println("Test passed.");
159
160    }
161
162    static class AllocatorThread extends Thread {
163        private List objectPool = new ArrayList();
164        public void run() {
165            int iterations = 0;
166            int numElements = (int) (chunkSize / 4); // minimal object size
167            while (listenerInvoked == 0) {
168                iterations++;
169                if (trace) {
170                    System.out.println("    Iteration " + iterations +
171                        ": before allocation " +
172                        mpool.getUsage().getUsed());
173                }
174
175                Object[] o = new Object[numElements];
176                if (iterations <= NUM_CHUNKS) {
177                    // only hold a reference to the first NUM_CHUNKS
178                    // allocated objects
179                    objectPool.add(o);
180                }
181
182                if (trace) {
183                    System.out.println("                " +
184                        "  after allocation " +
185                        mpool.getUsage().getUsed());
186                }
187                try {
188                    Thread.sleep(100);
189                } catch (InterruptedException e) {
190                    e.printStackTrace();
191                    System.out.println("Unexpected exception.");
192                    testFailed = true;
193                }
194            }
195
196            System.out.println("AllocatedThread finished memory allocation " +
197                " num_iteration = " + iterations);
198        }
199    }
200
201}
202