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", String.class);
     82         } catch (NoSuchMethodException nsme) {
     83             System.err.println("Found VMDebug but not dumpHprofData method");
     84             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) {
    129             System.err.println("join was interrupted");
    130         }
    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) {
    140             System.err.println("sleep was interrupted");
    141         }
    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  */
    159 class 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) {
    185         try {
    186             return new String("Robin" + val);
    187         } catch (OutOfMemoryError e) {
    188             return null;
    189         }
    190     }
    191 }
    192 
    193 
    194 /**
    195  * Allocates useless objects in recursive calls.
    196  */
    197 class 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) {
    216             System.err.println("not even once?");
    217             return;
    218         }
    219 
    220         checkStringReferences();
    221 
    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 
    228         Runtime.getRuntime().gc();
    229 
    230         for (int i = 0; i < MAX_DEPTH; i++) {
    231             if (weak[i].get() != null) {
    232                 System.err.println("Deep: weak still has " + i);
    233             }
    234         }
    235 
    236         if (Main.DEBUG)
    237             System.out.println("Deep: iters=" + iter / MAX_DEPTH);
    238     }
    239 
    240 
    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()) {
    254               System.err.println("Deep: " + i + " strong=" + strong[i] +
    255                   ", weak=" + weak[i].get());
    256           }
    257       }
    258     }
    259 
    260     /**
    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) {
    267         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             }
    303 
    304             weak[depth] = new WeakReference(funStr);
    305             strong[depth] = funStr;
    306             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.
    314         }
    315         return "";
    316     }
    317 
    318     private String makeString(int val) {
    319         try {
    320             return new String("Deep" + val);
    321         } catch (OutOfMemoryError e) {
    322             return null;
    323         }
    324     }
    325 }
    326 
    327 
    328 /**
    329  * Allocates large useless objects.
    330  */
    331 class 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) {
    340             try {
    341                 chunk = new byte[100000];
    342                 pretendToUse(chunk);
    343 
    344                 count++;
    345                 if ((count % 500) == 0) {
    346                     Main.sleep(400);
    347                     sleepCount++;
    348                 }
    349             } catch (OutOfMemoryError e) {
    350             }
    351         }
    352 
    353         if (Main.DEBUG)
    354             System.out.println("Large: sleepCount=" + sleepCount);
    355     }
    356 
    357     public void pretendToUse(byte[] chunk) {}
    358 }
    359