blob: 213b07f16c2816c10af2b67f42d5502eb0f14da2 [file] [log] [blame]
Brian Carlstrom55621742011-10-17 00:41:56 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import java.util.ArrayList;
18import java.util.List;
Andreas Gampe6c957c52015-01-20 21:26:43 -080019import java.util.concurrent.atomic.AtomicInteger;
Andreas Gampe93f3da12014-07-26 01:13:13 -070020import java.util.concurrent.CyclicBarrier;
Brian Carlstrom55621742011-10-17 00:41:56 -070021
Andreas Gampe1c83cbc2014-07-22 18:52:29 -070022public class Main implements Runnable {
Andreas Gampe93f3da12014-07-26 01:13:13 -070023
Andreas Gampeee576fa2015-01-15 08:02:22 -080024 // Timeout in minutes. Make it larger than the run-test timeout to get a native thread dump by
25 // ART on timeout when running on the host.
Andreas Gampe6c957c52015-01-20 21:26:43 -080026 private final static long TIMEOUT_VALUE = 7;
Andreas Gampeee576fa2015-01-15 08:02:22 -080027
Andreas Gampe6c957c52015-01-20 21:26:43 -080028 private final static long MAX_SIZE = 1000; // Maximum size of array-list to allocate.
29
30 private final static int THREAD_COUNT = 16;
31
32 // Use a couple of different forms of synchronizing to test some of these...
Sorin Bascaa0e10b12021-08-11 16:00:13 +000033 private final static AtomicInteger counter = new AtomicInteger(-1);
Andreas Gampe6c957c52015-01-20 21:26:43 -080034 private final static Object gate = new Object();
35 private volatile static int waitCount = 0;
Andreas Gampe93f3da12014-07-26 01:13:13 -070036
Sorin Bascaa0e10b12021-08-11 16:00:13 +000037 static {
38 // If we're using the interpreter for the boot class path, the VarHandle implementation of
39 // AtomicInteger.incrementAndGet() needs to resolve a MethodType which is then cached for
40 // subsequent uses. Make sure the MethodType is resolved early, otherwise the
41 // counter.incrementAndGet() call in work(), after we have tried to allocate all memory,
42 // could throw OOME.
43 counter.incrementAndGet();
44 }
45
Brian Carlstrom55621742011-10-17 00:41:56 -070046 public static void main(String[] args) throws Exception {
Andreas Gampe6c957c52015-01-20 21:26:43 -080047 Thread[] threads = new Thread[THREAD_COUNT];
Andreas Gampe93f3da12014-07-26 01:13:13 -070048
Andreas Gampe6c957c52015-01-20 21:26:43 -080049 // This barrier is used to synchronize the threads starting to allocate.
50 // Note: Even though a barrier is not allocation-free, this one is fine, as it will be used
51 // before filling the heap.
52 CyclicBarrier startBarrier = new CyclicBarrier(threads.length);
Andreas Gampe93f3da12014-07-26 01:13:13 -070053
54 for (int i = 0; i < threads.length; i++) {
Andreas Gampe6c957c52015-01-20 21:26:43 -080055 threads[i] = new Thread(new Main(startBarrier));
56 threads[i].start();
Brian Carlstrom55621742011-10-17 00:41:56 -070057 }
Andreas Gampe93f3da12014-07-26 01:13:13 -070058
59 // Wait for the threads to finish.
Brian Carlstrom55621742011-10-17 00:41:56 -070060 for (Thread thread : threads) {
61 thread.join();
62 }
Andreas Gampe93f3da12014-07-26 01:13:13 -070063
64 // Allocate objects to definitely run GC before quitting.
Sebastien Hertz19ac0272015-02-24 17:39:50 +010065 allocateObjectsToRunGc();
66
Andreas Gampe6c957c52015-01-20 21:26:43 -080067 new ArrayList<Object>(50);
Andreas Gampe93f3da12014-07-26 01:13:13 -070068 }
69
Sebastien Hertz19ac0272015-02-24 17:39:50 +010070 private static void allocateObjectsToRunGc() {
71 ArrayList<Object> l = new ArrayList<Object>();
72 try {
73 for (int i = 0; i < 100000; i++) {
74 l.add(new ArrayList<Object>(i));
75 }
76 } catch (OutOfMemoryError oom) {
77 }
78 }
79
Andreas Gampe6c957c52015-01-20 21:26:43 -080080 private Main(CyclicBarrier startBarrier) {
81 this.startBarrier = startBarrier;
Andreas Gampe93f3da12014-07-26 01:13:13 -070082 }
83
Andreas Gampe6c957c52015-01-20 21:26:43 -080084 private ArrayList<Object> store;
85 private CyclicBarrier startBarrier;
Brian Carlstrom55621742011-10-17 00:41:56 -070086
87 public void run() {
Andreas Gampe93f3da12014-07-26 01:13:13 -070088 try {
89 work();
Andreas Gampe6c957c52015-01-20 21:26:43 -080090 } catch (Throwable t) {
91 // Any exception or error getting here is bad.
92 try {
93 // May need allocations...
Kevin Brodskyf6c66c32015-12-17 14:13:00 +000094 t.printStackTrace(System.out);
Andreas Gampe6c957c52015-01-20 21:26:43 -080095 } catch (Throwable tInner) {
96 }
Andreas Gampe93f3da12014-07-26 01:13:13 -070097 System.exit(1);
Brian Carlstrom55621742011-10-17 00:41:56 -070098 }
99 }
Andreas Gampe21b4bf82014-07-25 16:37:09 -0700100
Andreas Gampe6c957c52015-01-20 21:26:43 -0800101 private void work() throws Exception {
102 // Any exceptions except an OOME in the allocation loop are bad and handed off to the
103 // caller which should abort the whole runtime.
104
Andreas Gampe93f3da12014-07-26 01:13:13 -0700105 ArrayList<Object> l = new ArrayList<Object>();
Andreas Gampe6c957c52015-01-20 21:26:43 -0800106 store = l; // Keep it alive.
Andreas Gampe93f3da12014-07-26 01:13:13 -0700107
Andreas Gampe6c957c52015-01-20 21:26:43 -0800108 // Wait for the start signal.
109 startBarrier.await(TIMEOUT_VALUE, java.util.concurrent.TimeUnit.MINUTES);
Andreas Gampe93f3da12014-07-26 01:13:13 -0700110
Andreas Gampe6c957c52015-01-20 21:26:43 -0800111 // Allocate.
112 try {
113 for (int i = 0; i < MAX_SIZE; i++) {
114 l.add(new ArrayList<Object>(i));
Andreas Gampe93f3da12014-07-26 01:13:13 -0700115 }
Andreas Gampe6c957c52015-01-20 21:26:43 -0800116 } catch (OutOfMemoryError oome) {
117 // Fine, we're done.
Andreas Gampe93f3da12014-07-26 01:13:13 -0700118 }
119
Andreas Gampe6c957c52015-01-20 21:26:43 -0800120 // Atomically increment the counter and check whether we were last.
121 int number = counter.incrementAndGet();
Andreas Gampe93f3da12014-07-26 01:13:13 -0700122
Andreas Gampe6c957c52015-01-20 21:26:43 -0800123 if (number < THREAD_COUNT) {
124 // Not last.
125 synchronized (gate) {
126 // Increment the wait counter.
127 waitCount++;
128 gate.wait(TIMEOUT_VALUE * 1000 * 60);
129 }
130 } else {
131 // Last. Wait until waitCount == THREAD_COUNT - 1.
132 for (int loops = 0; ; loops++) {
133 synchronized (gate) {
134 if (waitCount == THREAD_COUNT - 1) {
135 // OK, everyone's waiting. Notify and break out.
136 gate.notifyAll();
137 break;
138 } else if (loops > 40) {
139 // 1s wait, too many tries.
140 System.out.println("Waited too long for the last thread.");
141 System.exit(1);
142 }
143 }
144 // Wait a bit.
145 Thread.sleep(25);
146 }
147 }
148
149 store = null; // Allow GC to reclaim it.
Andreas Gampe93f3da12014-07-26 01:13:13 -0700150 }
Brian Carlstrom55621742011-10-17 00:41:56 -0700151}