• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 import java.io.File;
18 import java.lang.ref.WeakReference;
19 import java.lang.reflect.Method;
20 import java.lang.reflect.InvocationTargetException;
21 
22 public 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 
main(String[] args)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) {
55                 System.err.println(iae);
56             } catch (InvocationTargetException ite) {
57                 System.err.println(ite);
58             }
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      */
getDumpHprofDataMethod()70     private static Method getDumpHprofDataMethod() {
71         ClassLoader myLoader = Main.class.getClassLoader();
72         Class vmdClass;
73         try {
74             vmdClass = myLoader.loadClass("dalvik.system.VMDebug");
75         } catch (ClassNotFoundException cnfe) {
76             return null;
77         }
78 
79         Method meth;
80         try {
81             meth = vmdClass.getMethod("dumpHprofData",
82                     new Class[] { String.class });
83         } catch (NoSuchMethodException nsme) {
84             System.err.println("Found VMDebug but not dumpHprofData method");
85             return null;
86         }
87 
88         return meth;
89     }
90 
getDumpFileName()91     private static String getDumpFileName() {
92         File tmpDir = new File("/tmp");
93         if (tmpDir.exists() && tmpDir.isDirectory()) {
94             return "/tmp/" + OUTPUT_FILE;
95         }
96 
97         File sdcard = new File("/sdcard");
98         if (sdcard.exists() && sdcard.isDirectory()) {
99             return "/sdcard/" + OUTPUT_FILE;
100         }
101 
102         return null;
103     }
104 
105 
106     /**
107      * Run the various tests for a set period.
108      */
runTests()109     public static void runTests() {
110         Robin robin = new Robin();
111         Deep deep = new Deep();
112         Large large = new Large();
113 
114         /* start all threads */
115         robin.start();
116         deep.start();
117         large.start();
118 
119         /* let everybody run for 10 seconds */
120         sleep(TEST_TIME * 1000);
121 
122         quit = true;
123 
124         try {
125             /* wait for all threads to stop */
126             robin.join();
127             deep.join();
128             large.join();
129         } catch (InterruptedException ie) {
130             System.err.println("join was interrupted");
131         }
132     }
133 
134     /**
135      * Sleeps for the "ms" milliseconds.
136      */
sleep(int ms)137     public static void sleep(int ms) {
138         try {
139             Thread.sleep(ms);
140         } catch (InterruptedException ie) {
141             System.err.println("sleep was interrupted");
142         }
143     }
144 
145     /**
146      * Sleeps briefly, allowing other threads some CPU time to get started.
147      */
startupDelay()148     public static void startupDelay() {
149         sleep(500);
150     }
151 }
152 
153 
154 /**
155  * Allocates useless objects and holds on to several of them.
156  *
157  * Uses a single large array of references, replaced repeatedly in round-robin
158  * order.
159  */
160 class Robin extends Thread {
161     private static final int ARRAY_SIZE = 40960;
162     int sleepCount = 0;
163 
run()164     public void run() {
165         Main.startupDelay();
166 
167         String strings[] = new String[ARRAY_SIZE];
168         int idx = 0;
169 
170         while (!Main.quit) {
171             strings[idx] = makeString(idx);
172 
173             if (idx % (ARRAY_SIZE / 4) == 0) {
174                 Main.sleep(400);
175                 sleepCount++;
176             }
177 
178             idx = (idx + 1) % ARRAY_SIZE;
179         }
180 
181         if (Main.DEBUG)
182             System.out.println("Robin: sleepCount=" + sleepCount);
183     }
184 
makeString(int val)185     private String makeString(int val) {
186         return new String("Robin" + val);
187     }
188 }
189 
190 
191 /**
192  * Allocates useless objects in recursive calls.
193  */
194 class Deep extends Thread {
195     private static final int MAX_DEPTH = 61;
196 
197     private static String strong[] = new String[MAX_DEPTH];
198     private static WeakReference weak[] = new WeakReference[MAX_DEPTH];
199 
run()200     public void run() {
201         int iter = 0;
202         boolean once = false;
203 
204         Main.startupDelay();
205 
206         while (!Main.quit) {
207             dive(0, iter);
208             once = true;
209             iter += MAX_DEPTH;
210         }
211 
212         if (!once) {
213             System.err.println("not even once?");
214             return;
215         }
216 
217         /*
218          * Check the results of the last trip through.  Everything in
219          * "weak" should be matched in "strong", and the two should be
220          * equivalent (object-wise, not just string-equality-wise).
221          */
222         for (int i = 0; i < MAX_DEPTH; i++) {
223             if (strong[i] != weak[i].get()) {
224                 System.err.println("Deep: " + i + " strong=" + strong[i] +
225                     ", weak=" + weak[i].get());
226             }
227         }
228 
229         /*
230          * Wipe "strong", do a GC, see if "weak" got collected.
231          */
232         for (int i = 0; i < MAX_DEPTH; i++)
233             strong[i] = null;
234 
235         System.gc();
236 
237         for (int i = 0; i < MAX_DEPTH; i++) {
238             if (weak[i].get() != null) {
239                 System.err.println("Deep: weak still has " + i);
240             }
241         }
242 
243         if (Main.DEBUG)
244             System.out.println("Deep: iters=" + iter / MAX_DEPTH);
245     }
246 
247     /**
248      * Recursively dive down, setting one or more local variables.
249      *
250      * We pad the stack out with locals, attempting to create a mix of
251      * valid and invalid references on the stack.
252      */
dive(int depth, int iteration)253     private String dive(int depth, int iteration) {
254         String str0;
255         String str1;
256         String str2;
257         String str3;
258         String str4;
259         String str5;
260         String str6;
261         String str7;
262         String funStr;
263 
264         funStr = "";
265 
266         switch (iteration % 8) {
267             case 0:
268                 funStr = str0 = makeString(iteration);
269                 break;
270             case 1:
271                 funStr = str1 = makeString(iteration);
272                 break;
273             case 2:
274                 funStr = str2 = makeString(iteration);
275                 break;
276             case 3:
277                 funStr = str3 = makeString(iteration);
278                 break;
279             case 4:
280                 funStr = str4 = makeString(iteration);
281                 break;
282             case 5:
283                 funStr = str5 = makeString(iteration);
284                 break;
285             case 6:
286                 funStr = str6 = makeString(iteration);
287                 break;
288             case 7:
289                 funStr = str7 = makeString(iteration);
290                 break;
291         }
292 
293         strong[depth] = funStr;
294         weak[depth] = new WeakReference(funStr);
295 
296         if (depth+1 < MAX_DEPTH)
297             dive(depth+1, iteration+1);
298         else
299             Main.sleep(100);
300 
301         return funStr;
302     }
303 
makeString(int val)304     private String makeString(int val) {
305         return new String("Deep" + val);
306     }
307 }
308 
309 
310 /**
311  * Allocates large useless objects.
312  */
313 class Large extends Thread {
run()314     public void run() {
315         byte[] chunk;
316         int count = 0;
317         int sleepCount = 0;
318 
319         Main.startupDelay();
320 
321         while (!Main.quit) {
322             chunk = new byte[100000];
323             pretendToUse(chunk);
324 
325             count++;
326             if ((count % 500) == 0) {
327                 Main.sleep(400);
328                 sleepCount++;
329             }
330         }
331 
332         if (Main.DEBUG)
333             System.out.println("Large: sleepCount=" + sleepCount);
334     }
335 
pretendToUse(byte[] chunk)336     public void pretendToUse(byte[] chunk) {}
337 }
338