1 /* 2 * Copyright (C) 2013 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.lang.Runtime; 18 import java.lang.ref.ReferenceQueue; 19 import java.lang.ref.PhantomReference; 20 import dalvik.system.VMRuntime; 21 22 public class Main { 23 static Object deadlockLock = new Object(); 24 static VMRuntime runtime = VMRuntime.getRuntime(); 25 static volatile boolean aboutToDeadlock = false; 26 27 // Save ref as a static field to ensure it doesn't get GC'd before the 28 // referent is enqueued. 29 static PhantomReference ref = null; 30 31 static class DeadlockingFinalizer { 32 protected void finalize() throws Exception { 33 aboutToDeadlock = true; 34 synchronized (deadlockLock) { } 35 } 36 } 37 38 private static void allocateDeadlockingFinalizer() { 39 new DeadlockingFinalizer(); 40 } 41 42 public static PhantomReference allocPhantom(ReferenceQueue<Object> queue) { 43 return new PhantomReference(new Object(), queue); 44 } 45 46 // Test that calling registerNativeAllocation triggers a GC eventually 47 // after a substantial number of registered native bytes. 48 private static void checkRegisterNativeAllocation() throws Exception { 49 long maxMem = Runtime.getRuntime().maxMemory(); 50 int size = (int)(maxMem / 32); 51 int allocationCount = 256; 52 int maxExpectedGcDurationMs = 2000; 53 54 ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); 55 ref = allocPhantom(queue); 56 long total = 0; 57 for (int i = 0; !ref.isEnqueued() && i < allocationCount; ++i) { 58 runtime.registerNativeAllocation(size); 59 total += size; 60 61 // Sleep a little bit to ensure not all of the calls to 62 // registerNativeAllocation complete while GC is in the process of 63 // running. 64 Thread.sleep(maxExpectedGcDurationMs / allocationCount); 65 } 66 67 // Wait up to maxExpectedGcDurationMs to give GC a chance to finish 68 // running. If the reference isn't enqueued after that, then it is 69 // pretty unlikely (though technically still possible) that GC was 70 // triggered as intended. 71 if (queue.remove(maxExpectedGcDurationMs) == null) { 72 throw new RuntimeException("GC failed to complete"); 73 } 74 75 while (total > 0) { 76 runtime.registerNativeFree(size); 77 total -= size; 78 } 79 } 80 81 // Call registerNativeAllocation repeatedly at a high rate to trigger the 82 // case of blocking registerNativeAllocation. 83 private static void triggerBlockingRegisterNativeAllocation() throws Exception { 84 long maxMem = Runtime.getRuntime().maxMemory(); 85 int size = (int)(maxMem / 5); 86 int allocationCount = 10; 87 88 long total = 0; 89 for (int i = 0; i < allocationCount; ++i) { 90 runtime.registerNativeAllocation(size); 91 total += size; 92 } 93 94 while (total > 0) { 95 runtime.registerNativeFree(size); 96 total -= size; 97 } 98 } 99 100 public static void main(String[] args) throws Exception { 101 // Test that registerNativeAllocation triggers GC. 102 // Run this a few times in a loop to reduce the chances that the test 103 // is flaky and make sure registerNativeAllocation continues to work 104 // after the first GC is triggered. 105 for (int i = 0; i < 20; ++i) { 106 checkRegisterNativeAllocation(); 107 } 108 109 // Test that we don't get a deadlock if we call 110 // registerNativeAllocation with a blocked finalizer. 111 synchronized (deadlockLock) { 112 allocateDeadlockingFinalizer(); 113 while (!aboutToDeadlock) { 114 Runtime.getRuntime().gc(); 115 } 116 117 // Do more allocations now that the finalizer thread is deadlocked so that we force 118 // finalization and timeout. 119 triggerBlockingRegisterNativeAllocation(); 120 } 121 System.out.println("Test complete"); 122 } 123 } 124 125