1 /*
2  * Copyright (c) 2002, 2021, 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  * @key stress randomness
27  *
28  * @summary converted from VM Testbase gc/gctests/ReferencesGC.
29  * VM Testbase keywords: [gc, stress, stressopt, nonconcurrent, quick]
30  *
31  * @library /vmTestbase
32  *          /test/lib
33  * @run main/othervm
34  *      -XX:-UseGCOverheadLimit
35  *      gc.gctests.ReferencesGC.ReferencesGC
36  *      -range 200
37  *      -ratio 0.9
38  *      -t 1
39  */
40 
41 package gc.gctests.ReferencesGC;
42 
43 import java.lang.ref.*;
44 import nsk.share.TestFailure;
45 import nsk.share.gc.Algorithms;
46 import nsk.share.gc.GC;
47 import nsk.share.gc.ThreadedGCTest;
48 import nsk.share.gc.gp.GarbageProducer;
49 import nsk.share.gc.gp.GarbageUtils;
50 import nsk.share.test.ExecutionController;
51 
52 public class ReferencesGC extends ThreadedGCTest {
53 
54     static int RANGE = 256;
55     static float RATIO = (float) 1.0;
56     static int REMOVE;          // Initialized in parseArgs.
57     static int RETAIN;          // Initialized in parseArgs.
58 
main(String[] args)59     public static void main(String[] args) {
60         parseArgs(args);
61         GC.runTest(new ReferencesGC(), args);
62     }
63 
parseArgs(String[] args)64     public static void parseArgs(String[] args) {
65         for (int i = 0; i < args.length; i++) {
66             if (args[i].compareTo("-range") == 0) {
67                 RANGE = Integer.valueOf(args[++i]).intValue();
68             } else if (args[i].compareTo("-ratio") == 0) {
69                 RATIO = Float.valueOf(args[++i]).floatValue();
70             }
71         }
72         REMOVE = (int) (RANGE * RATIO);
73         RETAIN = RANGE - REMOVE;
74     }
75 
76     private class Worker implements Runnable {
77 
78         static final int WEAK = 0;
79         static final int SOFT = 1;
80         static final int PHANTOM = 2;
81         private ExecutionController stresser;
82         int finalizationMaxTime = 1000 * 60 * runParams.getNumberOfThreads();
83         ReferenceQueue refq = null; // Reinitialized each time through loop
84         int[] alive = null;         // Reinitialized each time through loop
85         int[] wrong = null;         // Reinitialized each time through loop
86         CircularLinkedList holder[] = new CircularLinkedList[RANGE];
87         WeakReference wr[] = new WeakReference[RANGE];
88         SoftReference sr[] = new SoftReference[RANGE];
89         PhantomReference phr[] = new PhantomReference[RANGE];
90         GarbageProducer gp = GarbageUtils.getArrayProducers().get(0);
91         int iter = 0;
92 
93         @Override
run()94         public void run() {
95             if (stresser == null) {
96                 stresser = getExecutionController();
97             }
98 
99             while (stresser.continueExecution()) {
100                 int totalLive = 0;
101                 try {
102                     refq = new ReferenceQueue();
103                     alive = new int[3];
104                     wrong = new int[3];
105                     for (int j = 0; j < RANGE; j++) {
106                         holder[j] = new CircularLinkedList();
107                         holder[j].addNelements(300);
108                         wr[j] = new WeakReference(holder[j], refq);
109                         sr[j] = new SoftReference(holder[j], refq);
110                         phr[j] = new PhantomReference(holder[j], refq);
111                     }
112                 } catch (OutOfMemoryError oome) {
113                     // we should just skip the test
114                     // the other thread could eat all memory
115                     continue;
116                 }
117 
118                 for (int i = 0; i < RANGE; i++) {
119                     if (wr[i].refersTo(holder[i])) {
120                         ++totalLive;
121                     }
122                     if (sr[i].refersTo(holder[i])) {
123                         ++totalLive;
124                     }
125                     if (phr[i].refersTo(holder[i])) {
126                         ++totalLive;
127                     }
128                 }
129                 if (totalLive != 3 * RANGE) {
130                     throw new TestFailure("There are " + (3 * RANGE - totalLive) + " references cleared before null-assigment.");
131                 }
132 
133                 for (int i = 0; i < REMOVE; i++) {
134                     holder[i] = null;
135                 }
136 
137                 Algorithms.eatMemory(stresser);
138                 if (!stresser.continueExecution()) {
139                     break;
140                 }
141                 // At this point OOME was thrown and accordingly to spec
142                 // all weak refs should be processed
143 
144                 long waitTime = System.currentTimeMillis() + finalizationMaxTime;
145                 int totalQ = 0;
146                 while ((totalQ < (3 * REMOVE)) && (System.currentTimeMillis() < waitTime)) {
147                     alive[WEAK] = alive[SOFT] = alive[PHANTOM] = 0;
148                     wrong[WEAK] = wrong[SOFT] = wrong[PHANTOM] = 0;
149                     for (int i = 0; i < RANGE; i++) {
150                         if (!wr[i].refersTo(holder[i])) {
151                             ++wrong[WEAK];
152                         } else if (holder[i] != null) {
153                             ++alive[WEAK];
154                         }
155 
156                         if (!sr[i].refersTo(holder[i])) {
157                             ++wrong[SOFT];
158                         } else if (holder[i] != null) {
159                             ++alive[SOFT];
160                         }
161 
162                         if (!phr[i].refersTo(holder[i])) {
163                             ++wrong[PHANTOM];
164                         } else if (holder[i] != null) {
165                             ++alive[PHANTOM];
166                         }
167                     }
168 
169                     try {
170                         while (refq.remove(100) != null) {
171                             ++totalQ;
172                         }
173                     } catch (InterruptedException ie) {
174                     }
175                     if (totalQ < (3 * REMOVE)) {
176                         log.debug("After null-assignment to " + REMOVE +
177                                   " referent values and provoking gc found:\n\t" +
178                                   totalQ + " queued refs.");
179                         try {
180                             log.debug("sleeping to give reference processing more time ...");
181                             Thread.sleep(1000);
182                         } catch (InterruptedException ie) {
183                         }
184                     }
185                 }
186                 log.debug("iteration.... " + iter++);
187                 if (wrong[WEAK] != 0) {
188                     throw new TestFailure("Expected " + RETAIN + " weak references still alive: " + alive[WEAK]);
189                 } else if (wrong[SOFT] != 0) {
190                     throw new TestFailure("Expected " + RETAIN + " soft references still alive: " + alive[SOFT]);
191                 } else if (wrong[PHANTOM] != 0) {
192                     throw new TestFailure("Expected " + RETAIN + " phantom references still alive: " + alive[PHANTOM]);
193                 } else if (totalQ != (3 * REMOVE)) {
194                     throw new TestFailure("Expected " + (3 * REMOVE) + " references enqueued: " + totalQ);
195                 }
196             }
197         }
198     }
199 
200     @Override
createRunnable(int i)201     protected Runnable createRunnable(int i) {
202         return new Worker();
203     }
204 }
205