• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.concurrent.atomic.AtomicInteger;
20 import java.util.concurrent.CyclicBarrier;
21 
22 public class Main implements Runnable {
23 
24     // 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.
26     private final static long TIMEOUT_VALUE = 7;
27 
28     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...
33     private final static AtomicInteger counter = new AtomicInteger(-1);
34     private final static Object gate = new Object();
35     private volatile static int waitCount = 0;
36 
37     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.
counter.incrementAndGet()43         counter.incrementAndGet();
44     }
45 
main(String[] args)46     public static void main(String[] args) throws Exception {
47         Thread[] threads = new Thread[THREAD_COUNT];
48 
49         // 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);
53 
54         for (int i = 0; i < threads.length; i++) {
55             threads[i] = new Thread(new Main(startBarrier));
56             threads[i].start();
57         }
58 
59         // Wait for the threads to finish.
60         for (Thread thread : threads) {
61             thread.join();
62         }
63 
64         // Allocate objects to definitely run GC before quitting.
65         allocateObjectsToRunGc();
66 
67         new ArrayList<Object>(50);
68     }
69 
allocateObjectsToRunGc()70     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 
Main(CyclicBarrier startBarrier)80     private Main(CyclicBarrier startBarrier) {
81         this.startBarrier = startBarrier;
82     }
83 
84     private ArrayList<Object> store;
85     private CyclicBarrier startBarrier;
86 
run()87     public void run() {
88         try {
89             work();
90         } catch (Throwable t) {
91             // Any exception or error getting here is bad.
92             try {
93                 // May need allocations...
94                 t.printStackTrace(System.out);
95             } catch (Throwable tInner) {
96             }
97             System.exit(1);
98         }
99     }
100 
work()101     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 
105         ArrayList<Object> l = new ArrayList<Object>();
106         store = l;  // Keep it alive.
107 
108         // Wait for the start signal.
109         startBarrier.await(TIMEOUT_VALUE, java.util.concurrent.TimeUnit.MINUTES);
110 
111         // Allocate.
112         try {
113             for (int i = 0; i < MAX_SIZE; i++) {
114                 l.add(new ArrayList<Object>(i));
115             }
116         } catch (OutOfMemoryError oome) {
117             // Fine, we're done.
118         }
119 
120         // Atomically increment the counter and check whether we were last.
121         int number = counter.incrementAndGet();
122 
123         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.
150     }
151 }
152