1 /*
2  * Copyright (c) 2011, 2018, 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     7036199
27  * @summary Check that GarbageCollectionNotification contents are reasonable
28  * @author  Frederic Parain
29  * @requires vm.opt.ExplicitGCInvokesConcurrent == null | vm.opt.ExplicitGCInvokesConcurrent == false
30  * @modules java.management/sun.management
31  *          jdk.management
32  * @run     main/othervm -Xms64m -Xmx64m GarbageCollectionNotificationContentTest
33   */
34 
35 import java.util.*;
36 import java.lang.management.*;
37 import java.lang.reflect.*;
38 import javax.management.*;
39 import javax.management.openmbean.*;
40 import com.sun.management.GarbageCollectionNotificationInfo;
41 import com.sun.management.GcInfo;
42 import java.security.AccessController;
43 import java.security.PrivilegedAction;
44 import java.lang.reflect.Field;
45 
46 public class GarbageCollectionNotificationContentTest {
47     private static HashMap<String,GarbageCollectionNotificationInfo> listenerInvoked
48         = new HashMap<String,GarbageCollectionNotificationInfo>();
49     static volatile long count = 0;
50     static volatile long number = 0;
51     static Object synchronizer = new Object();
52 
53     static class GcListener implements NotificationListener {
handleNotification(Notification notif, Object handback)54         public void handleNotification(Notification notif, Object handback) {
55             String type = notif.getType();
56             if (type.equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
57                 GarbageCollectionNotificationInfo gcNotif =
58                     GarbageCollectionNotificationInfo.from((CompositeData) notif.getUserData());
59                 String source = ((ObjectName)notif.getSource()).getCanonicalName();
60                 synchronized(synchronizer) {
61                     if(listenerInvoked.get(source) == null) {
62                             listenerInvoked.put(((ObjectName)notif.getSource()).getCanonicalName(),gcNotif);
63                             count++;
64                             if(count >= number) {
65                                 synchronizer.notify();
66                             }
67                     }
68                 }
69             }
70         }
71     }
72 
main(String[] args)73     public static void main(String[] args) throws Exception {
74         MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
75         final boolean isNotificationSupported =
76                  sun.management.ManagementFactoryHelper.getVMManagement().isGcNotificationSupported();
77 
78         if(!isNotificationSupported) {
79             System.out.println("GC Notification not supported by the JVM, test skipped");
80             return;
81         }
82         final ObjectName gcMXBeanPattern =
83                 new ObjectName("java.lang:type=GarbageCollector,*");
84         Set<ObjectName> names =
85                 mbs.queryNames(gcMXBeanPattern, null);
86         if (names.isEmpty())
87             throw new Exception("Test incorrect: no GC MXBeans");
88         number = names.size();
89         for (ObjectName n : names) {
90             if(mbs.isInstanceOf(n,"javax.management.NotificationEmitter")) {
91                 listenerInvoked.put(n.getCanonicalName(),null);
92                 GcListener listener = new GcListener();
93                 mbs.addNotificationListener(n, listener, null, null);
94             }
95         }
96         // Invocation of System.gc() to trigger major GC
97         System.gc();
98         // Allocation of many short living and small objects to trigger minor GC
99         Object data[] = new Object[32];
100         for(int i = 0; i<10000000; i++) {
101             data[i%32] = new int[8];
102         }
103         int wakeup = 0;
104         synchronized(synchronizer) {
105             while(count != number) {
106                 synchronizer.wait(10000);
107                 wakeup++;
108                 if(wakeup > 10)
109                     break;
110             }
111         }
112         for (GarbageCollectionNotificationInfo notif : listenerInvoked.values() ) {
113             checkGarbageCollectionNotificationInfoContent(notif);
114         }
115         System.out.println("Test passed");
116     }
117 
checkGarbageCollectionNotificationInfoContent(GarbageCollectionNotificationInfo notif)118     private static void checkGarbageCollectionNotificationInfoContent(GarbageCollectionNotificationInfo notif) throws Exception {
119         System.out.println("GC notification for "+notif.getGcName());
120         System.out.print("Action: "+notif.getGcAction());
121         System.out.println(" Cause: "+notif.getGcCause());
122         GcInfo info = notif.getGcInfo();
123         System.out.print("GC Info #" + info.getId());
124         System.out.print(" start:" + info.getStartTime());
125         System.out.print(" end:" + info.getEndTime());
126         System.out.println(" (" + info.getDuration() + "ms)");
127         Map<String, MemoryUsage> usage = info.getMemoryUsageBeforeGc();
128 
129         List<String> pnames = new ArrayList<String>();
130         for (Map.Entry entry : usage.entrySet() ) {
131             String poolname = (String) entry.getKey();
132             pnames.add(poolname);
133             MemoryUsage busage = (MemoryUsage) entry.getValue();
134             MemoryUsage ausage = (MemoryUsage) info.getMemoryUsageAfterGc().get(poolname);
135             if (ausage == null) {
136                 throw new RuntimeException("After Gc Memory does not exist" +
137                     " for " + poolname);
138             }
139             System.out.println("Usage for pool " + poolname);
140             System.out.println("   Before GC: " + busage);
141             System.out.println("   After GC: " + ausage);
142 
143             checkMemoryUsage(poolname, busage, ausage);
144         }
145 
146         // check if memory usage for all memory pools are returned
147         List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
148         for (MemoryPoolMXBean p : pools ) {
149             if (!pnames.contains(p.getName())) {
150                 throw new RuntimeException("GcInfo does not contain " +
151                     "memory usage for pool " + p.getName());
152             }
153         }
154     }
155 
checkMemoryUsage(String poolname, MemoryUsage busage, MemoryUsage ausage)156     private static void checkMemoryUsage(String poolname, MemoryUsage busage, MemoryUsage ausage) throws Exception {
157         if (poolname.contains("Eden Space") && busage.getUsed() > 0) {
158             // Used size at Eden Space should be decreased or
159             if (busage.getUsed() <= ausage.getUsed()) {
160                 throw new RuntimeException("Used size at Eden Space should be decreased.");
161             }
162         }
163     }
164 }
165