blob: 5165df7bd3edee51e4da37990917cd7abeb85d70 [file] [log] [blame]
jeffhao5d1ac922011-09-29 17:41:15 -07001/*
2 * Copyright (C) 2009 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.io.File;
18import java.lang.ref.WeakReference;
19import java.lang.reflect.Method;
20import java.lang.reflect.InvocationTargetException;
21
22public class Main {
23 public static volatile boolean quit = false;
24 public static final boolean DEBUG = false;
25
26 private static final boolean WRITE_HPROF_DATA = false;
27 private static final int TEST_TIME = 10;
28 private static final String OUTPUT_FILE = "gc-thrash.hprof";
29
30 public static void main(String[] args) {
31 // dump heap before
32
33 System.out.println("Running (" + TEST_TIME + " seconds) ...");
34 runTests();
35
36 Method dumpHprofDataMethod = null;
37 String dumpFile = null;
38
39 if (WRITE_HPROF_DATA) {
40 dumpHprofDataMethod = getDumpHprofDataMethod();
41 if (dumpHprofDataMethod != null) {
42 dumpFile = getDumpFileName();
43 System.out.println("Sending output to " + dumpFile);
44 }
45 }
46
47 System.gc();
48 System.runFinalization();
49 System.gc();
50
51 if (WRITE_HPROF_DATA && dumpHprofDataMethod != null) {
52 try {
53 dumpHprofDataMethod.invoke(null, dumpFile);
54 } catch (IllegalAccessException iae) {
Kevin Brodskyf6c66c32015-12-17 14:13:00 +000055 System.out.println(iae);
jeffhao5d1ac922011-09-29 17:41:15 -070056 } catch (InvocationTargetException ite) {
Kevin Brodskyf6c66c32015-12-17 14:13:00 +000057 System.out.println(ite);
jeffhao5d1ac922011-09-29 17:41:15 -070058 }
59 }
60
61 System.out.println("Done.");
62 }
63
64 /**
65 * Finds VMDebug.dumpHprofData() through reflection. In the reference
66 * implementation this will not be available.
67 *
68 * @return the reflection object, or null if the method can't be found
69 */
70 private static Method getDumpHprofDataMethod() {
71 ClassLoader myLoader = Main.class.getClassLoader();
Andreas Gampe166aaee2016-07-18 08:27:23 -070072 Class<?> vmdClass;
jeffhao5d1ac922011-09-29 17:41:15 -070073 try {
74 vmdClass = myLoader.loadClass("dalvik.system.VMDebug");
75 } catch (ClassNotFoundException cnfe) {
76 return null;
77 }
78
79 Method meth;
80 try {
Andreas Gampe166aaee2016-07-18 08:27:23 -070081 meth = vmdClass.getMethod("dumpHprofData", String.class);
jeffhao5d1ac922011-09-29 17:41:15 -070082 } catch (NoSuchMethodException nsme) {
Kevin Brodskyf6c66c32015-12-17 14:13:00 +000083 System.out.println("Found VMDebug but not dumpHprofData method");
jeffhao5d1ac922011-09-29 17:41:15 -070084 return null;
85 }
86
87 return meth;
88 }
89
90 private static String getDumpFileName() {
91 File tmpDir = new File("/tmp");
92 if (tmpDir.exists() && tmpDir.isDirectory()) {
93 return "/tmp/" + OUTPUT_FILE;
94 }
95
96 File sdcard = new File("/sdcard");
97 if (sdcard.exists() && sdcard.isDirectory()) {
98 return "/sdcard/" + OUTPUT_FILE;
99 }
100
101 return null;
102 }
103
104
105 /**
106 * Run the various tests for a set period.
107 */
108 public static void runTests() {
109 Robin robin = new Robin();
110 Deep deep = new Deep();
111 Large large = new Large();
112
113 /* start all threads */
114 robin.start();
115 deep.start();
116 large.start();
117
118 /* let everybody run for 10 seconds */
119 sleep(TEST_TIME * 1000);
120
121 quit = true;
122
123 try {
124 /* wait for all threads to stop */
125 robin.join();
126 deep.join();
127 large.join();
128 } catch (InterruptedException ie) {
Kevin Brodskyf6c66c32015-12-17 14:13:00 +0000129 System.out.println("join was interrupted");
jeffhao5d1ac922011-09-29 17:41:15 -0700130 }
131 }
132
133 /**
134 * Sleeps for the "ms" milliseconds.
135 */
136 public static void sleep(int ms) {
137 try {
138 Thread.sleep(ms);
139 } catch (InterruptedException ie) {
Kevin Brodskyf6c66c32015-12-17 14:13:00 +0000140 System.out.println("sleep was interrupted");
jeffhao5d1ac922011-09-29 17:41:15 -0700141 }
142 }
143
144 /**
145 * Sleeps briefly, allowing other threads some CPU time to get started.
146 */
147 public static void startupDelay() {
148 sleep(500);
149 }
150}
151
152
153/**
154 * Allocates useless objects and holds on to several of them.
155 *
156 * Uses a single large array of references, replaced repeatedly in round-robin
157 * order.
158 */
159class Robin extends Thread {
160 private static final int ARRAY_SIZE = 40960;
161 int sleepCount = 0;
162
163 public void run() {
164 Main.startupDelay();
165
166 String strings[] = new String[ARRAY_SIZE];
167 int idx = 0;
168
169 while (!Main.quit) {
170 strings[idx] = makeString(idx);
171
172 if (idx % (ARRAY_SIZE / 4) == 0) {
173 Main.sleep(400);
174 sleepCount++;
175 }
176
177 idx = (idx + 1) % ARRAY_SIZE;
178 }
179
180 if (Main.DEBUG)
181 System.out.println("Robin: sleepCount=" + sleepCount);
182 }
183
184 private String makeString(int val) {
Mathieu Chartiercecc2d92014-10-13 11:45:52 -0700185 try {
186 return new String("Robin" + val);
187 } catch (OutOfMemoryError e) {
188 return null;
189 }
jeffhao5d1ac922011-09-29 17:41:15 -0700190 }
191}
192
193
194/**
195 * Allocates useless objects in recursive calls.
196 */
197class Deep extends Thread {
198 private static final int MAX_DEPTH = 61;
199
200 private static String strong[] = new String[MAX_DEPTH];
201 private static WeakReference weak[] = new WeakReference[MAX_DEPTH];
202
203 public void run() {
204 int iter = 0;
205 boolean once = false;
206
207 Main.startupDelay();
208
209 while (!Main.quit) {
210 dive(0, iter);
211 once = true;
212 iter += MAX_DEPTH;
213 }
214
215 if (!once) {
Kevin Brodskyf6c66c32015-12-17 14:13:00 +0000216 System.out.println("not even once?");
jeffhao5d1ac922011-09-29 17:41:15 -0700217 return;
218 }
219
Sebastien Hertz19ac0272015-02-24 17:39:50 +0100220 checkStringReferences();
jeffhao5d1ac922011-09-29 17:41:15 -0700221
222 /*
223 * Wipe "strong", do a GC, see if "weak" got collected.
224 */
225 for (int i = 0; i < MAX_DEPTH; i++)
226 strong[i] = null;
227
Mathieu Chartier7befd0e2014-02-03 17:48:41 -0800228 Runtime.getRuntime().gc();
jeffhao5d1ac922011-09-29 17:41:15 -0700229
230 for (int i = 0; i < MAX_DEPTH; i++) {
231 if (weak[i].get() != null) {
Kevin Brodskyf6c66c32015-12-17 14:13:00 +0000232 System.out.println("Deep: weak still has " + i);
jeffhao5d1ac922011-09-29 17:41:15 -0700233 }
234 }
235
236 if (Main.DEBUG)
237 System.out.println("Deep: iters=" + iter / MAX_DEPTH);
238 }
239
Sebastien Hertz19ac0272015-02-24 17:39:50 +0100240
241 /**
242 * Check the results of the last trip through. Everything in
243 * "weak" should be matched in "strong", and the two should be
244 * equivalent (object-wise, not just string-equality-wise).
245 *
246 * We do that check in a separate method to avoid retaining these
247 * String references in local DEX registers. In interpreter mode,
248 * they would retain these references until the end of the method
249 * or until they are updated to another value.
250 */
251 private static void checkStringReferences() {
252 for (int i = 0; i < MAX_DEPTH; i++) {
253 if (strong[i] != weak[i].get()) {
Kevin Brodskyf6c66c32015-12-17 14:13:00 +0000254 System.out.println("Deep: " + i + " strong=" + strong[i] +
Sebastien Hertz19ac0272015-02-24 17:39:50 +0100255 ", weak=" + weak[i].get());
256 }
257 }
258 }
259
jeffhao5d1ac922011-09-29 17:41:15 -0700260 /**
261 * Recursively dive down, setting one or more local variables.
262 *
263 * We pad the stack out with locals, attempting to create a mix of
264 * valid and invalid references on the stack.
265 */
266 private String dive(int depth, int iteration) {
Mathieu Chartier9dc0ced2014-10-16 10:01:39 -0700267 try {
268 String str0;
269 String str1;
270 String str2;
271 String str3;
272 String str4;
273 String str5;
274 String str6;
275 String str7;
276 String funStr = "";
277 switch (iteration % 8) {
278 case 0:
279 funStr = str0 = makeString(iteration);
280 break;
281 case 1:
282 funStr = str1 = makeString(iteration);
283 break;
284 case 2:
285 funStr = str2 = makeString(iteration);
286 break;
287 case 3:
288 funStr = str3 = makeString(iteration);
289 break;
290 case 4:
291 funStr = str4 = makeString(iteration);
292 break;
293 case 5:
294 funStr = str5 = makeString(iteration);
295 break;
296 case 6:
297 funStr = str6 = makeString(iteration);
298 break;
299 case 7:
300 funStr = str7 = makeString(iteration);
301 break;
302 }
jeffhao5d1ac922011-09-29 17:41:15 -0700303
Nicolas Geoffrayed8b53d2015-02-03 08:48:27 +0000304 weak[depth] = new WeakReference(funStr);
Nicolas Geoffray67f65ea2015-02-03 10:10:52 +0000305 strong[depth] = funStr;
Mathieu Chartier9dc0ced2014-10-16 10:01:39 -0700306 if (depth+1 < MAX_DEPTH)
307 dive(depth+1, iteration+1);
308 else
309 Main.sleep(100);
310 return funStr;
311 } catch (OutOfMemoryError e) {
312 // Silently ignore OOME since gc stress mode causes them to occur but shouldn't be a
313 // test failure.
jeffhao5d1ac922011-09-29 17:41:15 -0700314 }
Mathieu Chartier9dc0ced2014-10-16 10:01:39 -0700315 return "";
jeffhao5d1ac922011-09-29 17:41:15 -0700316 }
317
318 private String makeString(int val) {
Mathieu Chartiercecc2d92014-10-13 11:45:52 -0700319 try {
320 return new String("Deep" + val);
321 } catch (OutOfMemoryError e) {
322 return null;
323 }
jeffhao5d1ac922011-09-29 17:41:15 -0700324 }
325}
326
327
328/**
329 * Allocates large useless objects.
330 */
331class Large extends Thread {
332 public void run() {
333 byte[] chunk;
334 int count = 0;
335 int sleepCount = 0;
336
337 Main.startupDelay();
338
339 while (!Main.quit) {
Mathieu Chartiercecc2d92014-10-13 11:45:52 -0700340 try {
341 chunk = new byte[100000];
342 pretendToUse(chunk);
jeffhao5d1ac922011-09-29 17:41:15 -0700343
Mathieu Chartiercecc2d92014-10-13 11:45:52 -0700344 count++;
345 if ((count % 500) == 0) {
346 Main.sleep(400);
347 sleepCount++;
348 }
349 } catch (OutOfMemoryError e) {
jeffhao5d1ac922011-09-29 17:41:15 -0700350 }
351 }
352
353 if (Main.DEBUG)
354 System.out.println("Large: sleepCount=" + sleepCount);
355 }
356
357 public void pretendToUse(byte[] chunk) {}
358}