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 17package android.core; 18 19import android.test.suitebuilder.annotation.LargeTest; 20import android.test.suitebuilder.annotation.MediumTest; 21import android.test.suitebuilder.annotation.SmallTest; 22import android.util.Log; 23import android.test.suitebuilder.annotation.Suppress; 24import dalvik.system.VMRuntime; 25import junit.framework.TestCase; 26 27import java.lang.ref.PhantomReference; 28import java.lang.ref.ReferenceQueue; 29import java.lang.ref.SoftReference; 30import java.lang.ref.WeakReference; 31import java.util.LinkedList; 32import java.util.Random; 33 34 35public 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