Home | History | Annotate | Download | only in src
      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 
     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      */
     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 
     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      */
    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      */
    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      */
    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 
    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 
    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 
    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      */
    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 
    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 {
    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 
    336     public void pretendToUse(byte[] chunk) {}
    337 }
    338