1 /*
2  * Copyright (c) 2010, 2019, 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 import java.io.BufferedReader;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 
28 import static jdk.test.lib.Asserts.assertGreaterThan;
29 import jdk.test.lib.process.ProcessTools;
30 
31 import com.sun.tools.attach.AttachNotSupportedException;
32 import com.sun.tools.attach.VirtualMachine;
33 
34 import sun.tools.attach.HotSpotVirtualMachine;
35 
36 /*
37  * @test
38  * @bug 6942989
39  * @summary Check for WeakReference leak in Logger and anonymous Logger objects
40  * @library /test/lib
41  * @modules jdk.attach/sun.tools.attach
42  *          java.logging
43  * @build jdk.test.lib.process.ProcessTools
44  * @run main/othervm -Djdk.attach.allowAttachSelf TestLoggerWeakRefLeak Logger
45  * @run main/othervm -Djdk.attach.allowAttachSelf TestLoggerWeakRefLeak AnonymousLogger
46  */
47 public class TestLoggerWeakRefLeak {
48 
49     private static final String TARGET_CLASS = "java.lang.ref.WeakReference";
50     private static final int INSTANCE_COUNT = 100;
51     private static int loggerCount = 0;
52 
main(String[] args)53     public static void main(String[] args) throws Exception {
54         if (args[0].equals("AnonymousLogger")) {
55             System.out.println("Test for WeakReference leak in AnonymousLogger object");
56             testIfLeaking(TestLoggerWeakRefLeak::callAnonymousLogger);
57         } else {
58             System.out.println("Test for WeakReference leak in Logger object");
59             testIfLeaking(TestLoggerWeakRefLeak::callLogger);
60         }
61     }
62 
63     /**
64      * For these particular WeakReference leaks, the count was always observed
65      * to be increasing so if decreasing or the same count is observed,
66      * then there is no leak.
67      */
testIfLeaking(Runnable callLogger)68     private static void testIfLeaking(Runnable callLogger) throws Exception {
69         long count = 0;
70         int instanceCount = 0;
71         int previousInstanceCount = 0;
72         int increasingCount = 0;
73         int decreasingCount = 0;
74 
75         while (true) {
76             callLogger.run();
77             count += INSTANCE_COUNT;
78 
79             if ((count % 1000) == 0) {
80                 System.out.println("call count = " + count);
81 
82                 instanceCount = getInstanceCountFromHeapHisto();
83                 if (instanceCount > previousInstanceCount) {
84                     increasingCount++;
85                 } else {
86                     decreasingCount++;
87                     System.out.println("increasing count: " + increasingCount);
88                     System.out.println("decreasing or the same count: " + decreasingCount);
89                     System.out.println("Test passed: decreasing or the same instance count is observed");
90                     break;
91                 }
92                 previousInstanceCount = instanceCount;
93             }
94 
95             delayExecution();
96         }
97     }
98 
99     /**
100      * This Logger call is leaking two different WeakReferences:
101      * - one in LogManager.LogNode
102      * - one in Logger.kids
103      */
callLogger()104     private static void callLogger() {
105         for (int i = 0; i < INSTANCE_COUNT; i++) {
106             java.util.logging.Logger.getLogger("logger-" + loggerCount);
107             if (++loggerCount >= 25000) {
108                 // Limit the Logger namespace used by the test so the weak refs
109                 // in LogManager.loggers that are being properly managed
110                 // don't skew the counts by too much.
111                 loggerCount = 0;
112             }
113         }
114     }
115 
116     /**
117      * This Logger call is leaking a WeakReference in Logger.kids
118      */
callAnonymousLogger()119     private static void callAnonymousLogger() {
120         for (int i = 0; i < INSTANCE_COUNT; i++) {
121             java.util.logging.Logger.getAnonymousLogger();
122         }
123     }
124 
125     /**
126      * 'vm.heapHisto("-live")' will request a full GC
127      */
getInstanceCountFromHeapHisto()128     private static int getInstanceCountFromHeapHisto() throws AttachNotSupportedException, Exception {
129         int instanceCount = 0;
130 
131         HotSpotVirtualMachine vm = (HotSpotVirtualMachine) VirtualMachine
132                 .attach(Long.toString(ProcessTools.getProcessId()));
133         try {
134             try (InputStream heapHistoStream = vm.heapHisto("-live");
135                     BufferedReader in = new BufferedReader(new InputStreamReader(heapHistoStream))) {
136                 String inputLine;
137                 while ((inputLine = in.readLine()) != null) {
138                     if (inputLine.contains(TARGET_CLASS)) {
139                         instanceCount = Integer.parseInt(inputLine
140                                 .split("[ ]+")[2]);
141                         System.out.println("instance count: " + instanceCount);
142                         break;
143                     }
144                 }
145             }
146         } finally {
147             vm.detach();
148         }
149 
150         assertGreaterThan(instanceCount, 0, "No instances of " + TARGET_CLASS + " are found");
151 
152         return instanceCount;
153     }
154 
155     /**
156      * Delay for 1/10 of a second to avoid CPU saturation
157      */
delayExecution()158     private static void delayExecution() {
159         try {
160             Thread.sleep(100);
161         } catch (InterruptedException ie) {
162             // Ignore any exceptions
163         }
164     }
165 
166 }
167