Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright (C) 2007 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 package android.core;
     18 
     19 import android.test.suitebuilder.annotation.LargeTest;
     20 import android.test.suitebuilder.annotation.MediumTest;
     21 import android.test.suitebuilder.annotation.SmallTest;
     22 import android.util.Log;
     23 import android.test.suitebuilder.annotation.Suppress;
     24 import dalvik.system.VMRuntime;
     25 import junit.framework.TestCase;
     26 
     27 import java.lang.ref.PhantomReference;
     28 import java.lang.ref.ReferenceQueue;
     29 import java.lang.ref.SoftReference;
     30 import java.lang.ref.WeakReference;
     31 import java.util.LinkedList;
     32 import java.util.Random;
     33 
     34 
     35 public class HeapTest extends TestCase {
     36 
     37     private static final String TAG = "HeapTest";
     38 
     39     /**
     40      * Returns a WeakReference to an object that has no
     41      * other references.  This is done in a separate method
     42      * to ensure that the Object's address isn't sitting in
     43      * a stale local register.
     44      */
     45     private WeakReference<Object> newRef() {
     46         return new WeakReference<Object>(new Object());
     47     }
     48 
     49     private static void makeRefs(Object objects[], SoftReference<Object> refs[]) {
     50         for (int i = 0; i < objects.length; i++) {
     51             objects[i] = (Object) new byte[8 * 1024];
     52             refs[i] = new SoftReference<Object>(objects[i]);
     53         }
     54     }
     55 
     56     private static <T> int checkRefs(SoftReference<T> refs[], int last) {
     57         int i;
     58         int numCleared = 0;
     59         for (i = 0; i < refs.length; i++) {
     60             Object o = refs[i].get();
     61             if (o == null) {
     62                 numCleared++;
     63             }
     64         }
     65         if (numCleared != last) {
     66             Log.i(TAG, "****** " + numCleared + "/" + i + " cleared ******");
     67         }
     68         return numCleared;
     69     }
     70 
     71     private static void clearRefs(Object objects[], int skip) {
     72         for (int i = 0; i < objects.length; i += skip) {
     73             objects[i] = null;
     74         }
     75     }
     76 
     77     private static void clearRefs(Object objects[]) {
     78         clearRefs(objects, 1);
     79     }
     80 
     81     private static <T> void checkRefs(T objects[], SoftReference<T> refs[]) {
     82         boolean ok = true;
     83 
     84         for (int i = 0; i < objects.length; i++) {
     85             if (refs[i].get() != objects[i]) {
     86                 ok = false;
     87             }
     88         }
     89         if (!ok) {
     90             throw new RuntimeException("Test failed: soft refs not cleared");
     91         }
     92     }
     93 
     94     @MediumTest
     95     public void testGcSoftRefs() throws Exception {
     96         final int NUM_REFS = 128;
     97 
     98         Object objects[] = new Object[NUM_REFS];
     99         SoftReference<Object> refs[] = new SoftReference[objects.length];
    100 
    101         /* Create a bunch of objects and a parallel array
    102          * of SoftReferences.
    103          */
    104         makeRefs(objects, refs);
    105         Runtime.getRuntime().gc();
    106 
    107         /* Let go of some of the hard references to the objects so that
    108          * the references can be cleared.
    109          */
    110         clearRefs(objects, 3);
    111 
    112         /* Collect all softly-reachable objects.
    113          */
    114         VMRuntime.getRuntime().gcSoftReferences();
    115         Runtime.getRuntime().runFinalization();
    116 
    117         /* Make sure that the objects were collected.
    118          */
    119         checkRefs(objects, refs);
    120 
    121         /* Remove more hard references and re-check.
    122          */
    123         clearRefs(objects, 2);
    124         VMRuntime.getRuntime().gcSoftReferences();
    125         Runtime.getRuntime().runFinalization();
    126         checkRefs(objects, refs);
    127 
    128         /* Remove the rest of the references and re-check.
    129          */
    130         /* Remove more hard references and re-check.
    131          */
    132         clearRefs(objects);
    133         VMRuntime.getRuntime().gcSoftReferences();
    134         Runtime.getRuntime().runFinalization();
    135         checkRefs(objects, refs);
    136     }
    137 
    138     public void xxtestSoftRefPartialClean() throws Exception {
    139         final int NUM_REFS = 128;
    140 
    141         Object objects[] = new Object[NUM_REFS];
    142         SoftReference<Object> refs[] = new SoftReference[objects.length];
    143 
    144         /* Create a bunch of objects and a parallel array
    145         * of SoftReferences.
    146         */
    147         makeRefs(objects, refs);
    148         Runtime.getRuntime().gc();
    149 
    150         /* Let go of the hard references to the objects so that
    151         * the references can be cleared.
    152         */
    153         clearRefs(objects);
    154 
    155         /* Start creating a bunch of temporary and permanent objects
    156         * to drive GC.
    157         */
    158         final int NUM_OBJECTS = 64 * 1024;
    159         Object junk[] = new Object[NUM_OBJECTS];
    160         Random random = new Random();
    161 
    162         int i = 0;
    163         int mod = 0;
    164         int totalSize = 0;
    165         int cleared = -1;
    166         while (i < junk.length && totalSize < 8 * 1024 * 1024) {
    167             int r = random.nextInt(64 * 1024) + 128;
    168             Object o = (Object) new byte[r];
    169             if (++mod % 16 == 0) {
    170                 junk[i++] = o;
    171                 totalSize += r * 4;
    172             }
    173             cleared = checkRefs(refs, cleared);
    174         }
    175     }
    176 
    177     private static void makeRefs(Object objects[], WeakReference<Object> refs[]) {
    178         for (int i = 0; i < objects.length; i++) {
    179             objects[i] = new Object();
    180             refs[i] = new WeakReference<Object>(objects[i]);
    181         }
    182     }
    183 
    184     private static <T> void checkRefs(T objects[], WeakReference<T> refs[]) {
    185         boolean ok = true;
    186 
    187         for (int i = 0; i < objects.length; i++) {
    188             if (refs[i].get() != objects[i]) {
    189                 ok = false;
    190             }
    191         }
    192         if (!ok) {
    193             throw new RuntimeException("Test failed: " +
    194                     "weak refs not cleared");
    195         }
    196     }
    197 
    198     @MediumTest
    199     public void testWeakRefs() throws Exception {
    200         final int NUM_REFS = 16;
    201 
    202         Object objects[] = new Object[NUM_REFS];
    203         WeakReference<Object> refs[] = new WeakReference[objects.length];
    204 
    205         /* Create a bunch of objects and a parallel array
    206         * of WeakReferences.
    207         */
    208         makeRefs(objects, refs);
    209         Runtime.getRuntime().gc();
    210         checkRefs(objects, refs);
    211 
    212         /* Clear out every other strong reference.
    213         */
    214         for (int i = 0; i < objects.length; i += 2) {
    215             objects[i] = null;
    216         }
    217         Runtime.getRuntime().gc();
    218         checkRefs(objects, refs);
    219 
    220         /* Clear out the rest of them.
    221         */
    222         for (int i = 0; i < objects.length; i++) {
    223             objects[i] = null;
    224         }
    225         Runtime.getRuntime().gc();
    226         checkRefs(objects, refs);
    227     }
    228 
    229     private static void makeRefs(Object objects[], PhantomReference<Object> refs[],
    230             ReferenceQueue<Object> queue) {
    231         for (int i = 0; i < objects.length; i++) {
    232             objects[i] = new Object();
    233             refs[i] = new PhantomReference<Object>(objects[i], queue);
    234         }
    235     }
    236 
    237     static <T> void checkRefs(T objects[], PhantomReference<T> refs[],
    238             ReferenceQueue<T> queue) {
    239         boolean ok = true;
    240 
    241         /* Make sure that the reference that should be on
    242         * the queue are marked as enqueued.  Once we
    243         * pull them off the queue, they will no longer
    244         * be marked as enqueued.
    245         */
    246         for (int i = 0; i < objects.length; i++) {
    247             if (objects[i] == null && refs[i] != null) {
    248                 if (!refs[i].isEnqueued()) {
    249                     ok = false;
    250                 }
    251             }
    252         }
    253         if (!ok) {
    254             throw new RuntimeException("Test failed: " +
    255                     "phantom refs not marked as enqueued");
    256         }
    257 
    258         /* Make sure that all of the references on the queue
    259         * are supposed to be there.
    260         */
    261         PhantomReference<T> ref;
    262         while ((ref = (PhantomReference<T>) queue.poll()) != null) {
    263             /* Find the list index that corresponds to this reference.
    264             */
    265             int i;
    266             for (i = 0; i < objects.length; i++) {
    267                 if (refs[i] == ref) {
    268                     break;
    269                 }
    270             }
    271             if (i == objects.length) {
    272                 throw new RuntimeException("Test failed: " +
    273                         "unexpected ref on queue");
    274             }
    275             if (objects[i] != null) {
    276                 throw new RuntimeException("Test failed: " +
    277                         "reference enqueued for strongly-reachable " +
    278                         "object");
    279             }
    280             refs[i] = null;
    281 
    282             /* TODO: clear doesn't do much, since we're losing the
    283             * strong ref to the ref object anyway.  move the ref
    284             * into another list.
    285             */
    286             ref.clear();
    287         }
    288 
    289         /* We've visited all of the enqueued references.
    290         * Make sure that there aren't any other references
    291         * that should have been enqueued.
    292         *
    293         * NOTE: there is a race condition here;  this assumes
    294         * that the VM has serviced all outstanding reference
    295         * enqueue() calls.
    296         */
    297         for (int i = 0; i < objects.length; i++) {
    298             if (objects[i] == null && refs[i] != null) {
    299 //                System.out.println("HeapTest/PhantomRefs: refs[" + i +
    300 //                        "] should be enqueued");
    301                 ok = false;
    302             }
    303         }
    304         if (!ok) {
    305             throw new RuntimeException("Test failed: " +
    306                     "phantom refs not enqueued");
    307         }
    308     }
    309 
    310     @MediumTest
    311     public void testPhantomRefs() throws Exception {
    312         final int NUM_REFS = 16;
    313 
    314         Object objects[] = new Object[NUM_REFS];
    315         PhantomReference<Object> refs[] = new PhantomReference[objects.length];
    316         ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
    317 
    318         /* Create a bunch of objects and a parallel array
    319         * of PhantomReferences.
    320         */
    321         makeRefs(objects, refs, queue);
    322         Runtime.getRuntime().gc();
    323         checkRefs(objects, refs, queue);
    324 
    325         /* Clear out every other strong reference.
    326         */
    327         for (int i = 0; i < objects.length; i += 2) {
    328             objects[i] = null;
    329         }
    330         // System.out.println("HeapTest/PhantomRefs: cleared evens");
    331         Runtime.getRuntime().gc();
    332         Runtime.getRuntime().runFinalization();
    333         checkRefs(objects, refs, queue);
    334 
    335         /* Clear out the rest of them.
    336         */
    337         for (int i = 0; i < objects.length; i++) {
    338             objects[i] = null;
    339         }
    340         // System.out.println("HeapTest/PhantomRefs: cleared all");
    341         Runtime.getRuntime().gc();
    342         Runtime.getRuntime().runFinalization();
    343         checkRefs(objects, refs, queue);
    344     }
    345 
    346     private static int sNumFinalized = 0;
    347     private static final Object sLock = new Object();
    348 
    349     private static class FinalizableObject {
    350         protected void finalize() {
    351             // System.out.println("gc from finalize()");
    352             Runtime.getRuntime().gc();
    353             synchronized (sLock) {
    354                 sNumFinalized++;
    355             }
    356         }
    357     }
    358 
    359     private static void makeRefs(FinalizableObject objects[],
    360             WeakReference<FinalizableObject> refs[]) {
    361         for (int i = 0; i < objects.length; i++) {
    362             objects[i] = new FinalizableObject();
    363             refs[i] = new WeakReference<FinalizableObject>(objects[i]);
    364         }
    365     }
    366 
    367     @LargeTest
    368     public void testWeakRefsAndFinalizers() throws Exception {
    369         final int NUM_REFS = 16;
    370 
    371         FinalizableObject objects[] = new FinalizableObject[NUM_REFS];
    372         WeakReference<FinalizableObject> refs[] = new WeakReference[objects.length];
    373         int numCleared;
    374 
    375         /* Create a bunch of objects and a parallel array
    376         * of WeakReferences.
    377         */
    378         makeRefs(objects, refs);
    379         Runtime.getRuntime().gc();
    380         checkRefs(objects, refs);
    381 
    382         /* Clear out every other strong reference.
    383         */
    384         sNumFinalized = 0;
    385         numCleared = 0;
    386         for (int i = 0; i < objects.length; i += 2) {
    387             objects[i] = null;
    388             numCleared++;
    389         }
    390         // System.out.println("HeapTest/WeakRefsAndFinalizers: cleared evens");
    391         Runtime.getRuntime().gc();
    392         Runtime.getRuntime().runFinalization();
    393         checkRefs(objects, refs);
    394         if (sNumFinalized != numCleared) {
    395             throw new RuntimeException("Test failed: " +
    396                     "expected " + numCleared + " finalizations, saw " +
    397                     sNumFinalized);
    398         }
    399 
    400         /* Clear out the rest of them.
    401         */
    402         sNumFinalized = 0;
    403         numCleared = 0;
    404         for (int i = 0; i < objects.length; i++) {
    405             if (objects[i] != null) {
    406                 objects[i] = null;
    407                 numCleared++;
    408             }
    409         }
    410         // System.out.println("HeapTest/WeakRefsAndFinalizers: cleared all");
    411         Runtime.getRuntime().gc();
    412         Runtime.getRuntime().runFinalization();
    413         checkRefs(objects, refs);
    414         if (sNumFinalized != numCleared) {
    415             throw new RuntimeException("Test failed: " +
    416                     "expected " + numCleared + " finalizations, saw " +
    417                     sNumFinalized);
    418         }
    419     }
    420 
    421     // TODO: flaky test
    422     //@MediumTest
    423     public void testOomeLarge() throws Exception {
    424         /* Just shy of the typical max heap size so that it will actually
    425          * try to allocate it instead of short-circuiting.
    426          */
    427         final int SIXTEEN_MB = (16 * 1024 * 1024 - 32);
    428 
    429         Boolean sawEx = false;
    430         byte a[];
    431 
    432         try {
    433             a = new byte[SIXTEEN_MB];
    434         } catch (OutOfMemoryError oom) {
    435             //Log.i(TAG, "HeapTest/OomeLarge caught " + oom);
    436             sawEx = true;
    437         }
    438 
    439         if (!sawEx) {
    440             throw new RuntimeException("Test failed: " +
    441                     "OutOfMemoryError not thrown");
    442         }
    443     }
    444 
    445     //See bug 1308253 for reasons.
    446     @Suppress
    447     public void disableTestOomeSmall() throws Exception {
    448         final int SIXTEEN_MB = (16 * 1024 * 1024);
    449         final int LINK_SIZE = 6 * 4; // estimated size of a LinkedList's node
    450 
    451         Boolean sawEx = false;
    452 
    453         LinkedList<Object> list = new LinkedList<Object>();
    454 
    455         /* Allocate progressively smaller objects to fill up the entire heap.
    456          */
    457         int objSize = 1 * 1024 * 1024;
    458         while (objSize >= LINK_SIZE) {
    459             try {
    460                 for (int i = 0; i < SIXTEEN_MB / objSize; i++) {
    461                     list.add((Object)new byte[objSize]);
    462                 }
    463             } catch (OutOfMemoryError oom) {
    464                 sawEx = true;
    465             }
    466 
    467             if (!sawEx) {
    468                 throw new RuntimeException("Test failed: " +
    469                         "OutOfMemoryError not thrown while filling heap");
    470             }
    471             sawEx = false;
    472 
    473             objSize = (objSize * 4) / 5;
    474         }
    475     }
    476 }
    477