25 * @test
26 * @bug 6460501 6236036 6500694 6490770
27 * @summary Repeated failed timed waits shouldn't leak memory
28 * @library /test/lib
29 * @author Martin Buchholz
30 */
31
32 import static java.util.concurrent.TimeUnit.MILLISECONDS;
33 import static java.util.concurrent.TimeUnit.NANOSECONDS;
34
35 import java.io.File;
36 import java.io.FileOutputStream;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.InputStreamReader;
40 import java.io.OutputStream;
41 import java.io.PrintStream;
42 import java.io.Reader;
43 import java.lang.ref.ReferenceQueue;
44 import java.lang.ref.WeakReference;
45 import java.util.concurrent.BlockingQueue;
46 import java.util.concurrent.Callable;
47 import java.util.concurrent.CountDownLatch;
48 import java.util.concurrent.CyclicBarrier;
49 import java.util.concurrent.ExecutorService;
50 import java.util.concurrent.Executors;
51 import java.util.concurrent.Future;
52 import java.util.concurrent.LinkedBlockingQueue;
53 import java.util.concurrent.Semaphore;
54 import java.util.concurrent.ThreadLocalRandom;
55 import java.util.concurrent.locks.ReentrantLock;
56 import java.util.concurrent.locks.ReentrantReadWriteLock;
57 import java.util.regex.Matcher;
58 import java.util.regex.Pattern;
59 import jdk.test.lib.Utils;
60
61 public class TimedAcquireLeak {
62 static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
63
64 static String javahome() {
65 String jh = System.getProperty("java.home");
66 return (jh.endsWith("jre")) ? jh.substring(0, jh.length() - 4) : jh;
67 }
68
69 static final File bin = new File(javahome(), "bin");
70
71 static String javaProgramPath(String programName) {
72 return new File(bin, programName).getPath();
73 }
74
75 static final String java = javaProgramPath("java");
76 static final String jmap = javaProgramPath("jmap");
77 static final String jps = javaProgramPath("jps");
78
79 static String outputOf(Reader r) throws IOException {
80 final StringBuilder sb = new StringBuilder();
81 final char[] buf = new char[1024];
82 int n;
83 while ((n = r.read(buf)) > 0)
84 sb.append(buf, 0, n);
85 return sb.toString();
86 }
87
88 static String outputOf(InputStream is) throws IOException {
89 return outputOf(new InputStreamReader(is, "UTF-8"));
90 }
91
92 static final ExecutorService drainers = Executors.newFixedThreadPool(12);
93 static Future<String> futureOutputOf(final InputStream is) {
94 return drainers.submit(
95 new Callable<String>() { public String call() throws IOException {
96 return outputOf(is); }});}
97
142 }
143 timeoutMillis *= 4;
144 }
145 } catch (InterruptedException unexpected) {
146 throw new AssertionError("unexpected InterruptedException");
147 }
148 throw new AssertionError("failed to do a \"full\" gc");
149 }
150
151 // To be called exactly twice by the child process
152 public static void rendezvousChild() {
153 try {
154 forceFullGc();
155 sendByte(System.out);
156 System.in.read();
157 } catch (Throwable t) { throw new Error(t); }
158 }
159
160 static String match(String s, String regex, int group) {
161 Matcher matcher = Pattern.compile(regex).matcher(s);
162 matcher.find();
163 return matcher.group(group);
164 }
165
166 /** It's all about sending a message! */
167 static void sendByte(OutputStream s) throws IOException {
168 s.write('!');
169 s.flush();
170 }
171
172 static int objectsInUse(final Process child,
173 final String childPid,
174 final String className) {
175 final String regex =
176 "(?m)^ *[0-9]+: +([0-9]+) +[0-9]+ +\\Q"+className+"\\E(?:$| )";
177 final Callable<Integer> objectsInUse =
178 new Callable<Integer>() { public Integer call() {
179 Integer i = Integer.parseInt(
180 match(commandOutputOf(jmap, "-histo:live", childPid),
181 regex, 1));
182 if (i > 100)
183 System.out.print(
184 commandOutputOf(jmap,
185 "-dump:file=dump,format=b",
186 childPid));
187 return i;
188 }};
189 try { return rendezvousParent(child, objectsInUse); }
190 catch (Throwable t) { unexpected(t); return -1; }
191 }
192
193 static void realMain(String[] args) throws Throwable {
194 // jmap doesn't work on Windows
195 if (System.getProperty("os.name").startsWith("Windows"))
196 return;
197
198 final String childClassName = Job.class.getName();
199 final String classToCheckForLeaks = Job.classToCheckForLeaks();
200 final String uniqueID =
201 String.valueOf(ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE));
202
203 final String[] jobCmd = {
204 java, "-Xmx8m", "-XX:+UsePerfData",
205 "-classpath", System.getProperty("test.class.path"),
206 childClassName, uniqueID
207 };
208 final Process p = new ProcessBuilder(jobCmd).start();
209 // Ensure subprocess jvm has started, so that jps can find it
210 p.getInputStream().read();
211 sendByte(p.getOutputStream());
212
213 final String childPid =
214 match(commandOutputOf(jps, "-m"),
215 "(?m)^ *([0-9]+) +\\Q"+childClassName+"\\E *"+uniqueID+"$", 1);
216
217 final int n0 = objectsInUse(p, childPid, classToCheckForLeaks);
218 final int n1 = objectsInUse(p, childPid, classToCheckForLeaks);
219 equal(p.waitFor(), 0);
220 equal(p.exitValue(), 0);
221 failed += p.exitValue();
222
223 // Check that no objects were leaked.
224 //
225 // TODO: This test is very brittle, depending on current JDK
226 // implementation, and needing occasional adjustment.
227 System.out.printf("%d -> %d%n", n0, n1);
228 // Almost always n0 == n1
229 // Maximum jitter observed in practice is 10 -> 17
230 check(Math.abs(n1 - n0) < 10);
231 check(n1 < 25);
232 drainers.shutdown();
233 if (!drainers.awaitTermination(LONG_DELAY_MS, MILLISECONDS)) {
234 drainers.shutdownNow(); // last resort
235 throw new AssertionError("thread pool did not terminate");
236 }
237 }
238
239 //----------------------------------------------------------------
240 // The main class of the child process.
241 // Job's job is to:
242 // - provide the name of a class to check for leaks.
243 // - call rendezvousChild exactly twice, while quiescent.
244 // - in between calls to rendezvousChild, run code that may leak.
245 //----------------------------------------------------------------
246 public static class Job {
247 static String classToCheckForLeaks() {
248 return
249 "java.util.concurrent.locks.AbstractQueuedSynchronizer$Node";
250 }
251
252 public static void main(String[] args) throws Throwable {
253 // Synchronize with parent process, so that jps can find us
254 sendByte(System.out);
255 System.in.read();
256
257 final ReentrantLock lock = new ReentrantLock();
258 lock.lock();
259
260 final ReentrantReadWriteLock rwlock
261 = new ReentrantReadWriteLock();
262 final ReentrantReadWriteLock.ReadLock readLock
263 = rwlock.readLock();
264 final ReentrantReadWriteLock.WriteLock writeLock
265 = rwlock.writeLock();
266 rwlock.writeLock().lock();
267
268 final BlockingQueue<Object> q = new LinkedBlockingQueue<>();
269 final Semaphore fairSem = new Semaphore(0, true);
|
25 * @test
26 * @bug 6460501 6236036 6500694 6490770
27 * @summary Repeated failed timed waits shouldn't leak memory
28 * @library /test/lib
29 * @author Martin Buchholz
30 */
31
32 import static java.util.concurrent.TimeUnit.MILLISECONDS;
33 import static java.util.concurrent.TimeUnit.NANOSECONDS;
34
35 import java.io.File;
36 import java.io.FileOutputStream;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.InputStreamReader;
40 import java.io.OutputStream;
41 import java.io.PrintStream;
42 import java.io.Reader;
43 import java.lang.ref.ReferenceQueue;
44 import java.lang.ref.WeakReference;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.concurrent.BlockingQueue;
48 import java.util.concurrent.Callable;
49 import java.util.concurrent.CountDownLatch;
50 import java.util.concurrent.CyclicBarrier;
51 import java.util.concurrent.ExecutorService;
52 import java.util.concurrent.Executors;
53 import java.util.concurrent.Future;
54 import java.util.concurrent.LinkedBlockingQueue;
55 import java.util.concurrent.Semaphore;
56 import java.util.concurrent.ThreadLocalRandom;
57 import java.util.concurrent.locks.ReentrantLock;
58 import java.util.concurrent.locks.ReentrantReadWriteLock;
59 import java.util.regex.Matcher;
60 import java.util.regex.Pattern;
61 import jdk.test.lib.Utils;
62
63 public class TimedAcquireLeak {
64 static final long LONG_DELAY_MS = Utils.adjustTimeout(10_000);
65
66 static String javahome() {
67 String jh = System.getProperty("java.home");
68 return (jh.endsWith("jre")) ? jh.substring(0, jh.length() - 4) : jh;
69 }
70
71 static final File bin = new File(javahome(), "bin");
72
73 static String javaProgramPath(String programName) {
74 return new File(bin, programName).getPath();
75 }
76
77 static final String javaPath = javaProgramPath("java");
78 static final String jmapPath = javaProgramPath("jmap");
79 static final String jpsPath = javaProgramPath("jps");
80
81 static String outputOf(Reader r) throws IOException {
82 final StringBuilder sb = new StringBuilder();
83 final char[] buf = new char[1024];
84 int n;
85 while ((n = r.read(buf)) > 0)
86 sb.append(buf, 0, n);
87 return sb.toString();
88 }
89
90 static String outputOf(InputStream is) throws IOException {
91 return outputOf(new InputStreamReader(is, "UTF-8"));
92 }
93
94 static final ExecutorService drainers = Executors.newFixedThreadPool(12);
95 static Future<String> futureOutputOf(final InputStream is) {
96 return drainers.submit(
97 new Callable<String>() { public String call() throws IOException {
98 return outputOf(is); }});}
99
144 }
145 timeoutMillis *= 4;
146 }
147 } catch (InterruptedException unexpected) {
148 throw new AssertionError("unexpected InterruptedException");
149 }
150 throw new AssertionError("failed to do a \"full\" gc");
151 }
152
153 // To be called exactly twice by the child process
154 public static void rendezvousChild() {
155 try {
156 forceFullGc();
157 sendByte(System.out);
158 System.in.read();
159 } catch (Throwable t) { throw new Error(t); }
160 }
161
162 static String match(String s, String regex, int group) {
163 Matcher matcher = Pattern.compile(regex).matcher(s);
164 if (! matcher.find()) {
165 String msg = String.format(
166 "match failed: s=%s regex=%s", s, regex);
167 throw new AssertionError(msg);
168 }
169 return matcher.group(group);
170 }
171
172 /** It's all about sending a message! */
173 static void sendByte(OutputStream s) throws IOException {
174 s.write('!');
175 s.flush();
176 }
177
178 static int objectsInUse(final Process child,
179 final String childPid,
180 final String classNameRegex) {
181 String regex =
182 "(?m)^ *[0-9]+: +([0-9]+) +[0-9]+ +"+classNameRegex+"(?:$| )";
183 Callable<Integer> objectsInUse = () -> {
184 int i = Integer.parseInt(
185 match(commandOutputOf(jmapPath, "-histo:live", childPid),
186 regex, 1));
187 if (i > 100)
188 System.out.print(
189 commandOutputOf(jmapPath,
190 "-dump:file=dump,format=b",
191 childPid));
192 return i;
193 };
194 try { return rendezvousParent(child, objectsInUse); }
195 catch (Throwable t) { unexpected(t); return -1; }
196 }
197
198 static void realMain(String[] args) throws Throwable {
199 // jmap doesn't work on Windows
200 if (System.getProperty("os.name").startsWith("Windows"))
201 return;
202
203 final String childClassName = Job.class.getName();
204 final String classNameRegex = Job.classNameRegexToCheckForLeaks();
205 final String uniqueID = String.valueOf(
206 ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE));
207
208 final ArrayList<String> jobCmd = new ArrayList<>();
209 Collections.addAll(
210 jobCmd, javaPath, "-Xmx8m", "-XX:+UsePerfData",
211 "-classpath", System.getProperty("test.class.path"));
212 Collections.addAll(jobCmd, Utils.getTestJavaOpts());
213 Collections.addAll(jobCmd, childClassName, uniqueID);
214 final Process p = new ProcessBuilder(jobCmd).start();
215 // Ensure subprocess jvm has started, so that jps can find it
216 p.getInputStream().read();
217 sendByte(p.getOutputStream());
218
219 final String childPid =
220 match(commandOutputOf(jpsPath, "-m"),
221 "(?m)^ *([0-9]+) +\\Q"+childClassName+"\\E *"+uniqueID+"$", 1);
222
223 final int n0 = objectsInUse(p, childPid, classNameRegex);
224 final int n1 = objectsInUse(p, childPid, classNameRegex);
225 equal(p.waitFor(), 0);
226 equal(p.exitValue(), 0);
227 failed += p.exitValue();
228
229 // Check that no objects were leaked.
230 //
231 // TODO: This test is very brittle, depending on current JDK
232 // implementation, and needing occasional adjustment.
233 System.out.printf("%d -> %d%n", n0, n1);
234 // Almost always n0 == n1
235 // Maximum jitter observed in practice is 7
236 check(Math.abs(n1 - n0) < 10);
237 check(n1 < 25);
238 drainers.shutdown();
239 if (!drainers.awaitTermination(LONG_DELAY_MS, MILLISECONDS)) {
240 drainers.shutdownNow(); // last resort
241 throw new AssertionError("thread pool did not terminate");
242 }
243 }
244
245 //----------------------------------------------------------------
246 // The main class of the child process.
247 // Job's job is to:
248 // - provide the name of a class to check for leaks.
249 // - call rendezvousChild exactly twice, while quiescent.
250 // - in between calls to rendezvousChild, run code that may leak.
251 //----------------------------------------------------------------
252 public static class Job {
253 static String classNameRegexToCheckForLeaks() {
254 return
255 "\\Qjava.util.concurrent.locks.AbstractQueuedSynchronizer$\\E[A-Za-z]+";
256 }
257
258 public static void main(String[] args) throws Throwable {
259 // Synchronize with parent process, so that jps can find us
260 sendByte(System.out);
261 System.in.read();
262
263 final ReentrantLock lock = new ReentrantLock();
264 lock.lock();
265
266 final ReentrantReadWriteLock rwlock
267 = new ReentrantReadWriteLock();
268 final ReentrantReadWriteLock.ReadLock readLock
269 = rwlock.readLock();
270 final ReentrantReadWriteLock.WriteLock writeLock
271 = rwlock.writeLock();
272 rwlock.writeLock().lock();
273
274 final BlockingQueue<Object> q = new LinkedBlockingQueue<>();
275 final Semaphore fairSem = new Semaphore(0, true);
|