1*795d594fSAndroid Build Coastguard Worker /* 2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2013 The Android Open Source Project 3*795d594fSAndroid Build Coastguard Worker * 4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*795d594fSAndroid Build Coastguard Worker * 8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*795d594fSAndroid Build Coastguard Worker * 10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*795d594fSAndroid Build Coastguard Worker * limitations under the License. 15*795d594fSAndroid Build Coastguard Worker */ 16*795d594fSAndroid Build Coastguard Worker 17*795d594fSAndroid Build Coastguard Worker import java.lang.Runtime; 18*795d594fSAndroid Build Coastguard Worker import java.lang.ref.ReferenceQueue; 19*795d594fSAndroid Build Coastguard Worker import java.lang.ref.PhantomReference; 20*795d594fSAndroid Build Coastguard Worker import java.util.concurrent.atomic.AtomicInteger; 21*795d594fSAndroid Build Coastguard Worker import dalvik.system.VMRuntime; 22*795d594fSAndroid Build Coastguard Worker 23*795d594fSAndroid Build Coastguard Worker public class Main { 24*795d594fSAndroid Build Coastguard Worker static Object deadlockLock = new Object(); 25*795d594fSAndroid Build Coastguard Worker static VMRuntime runtime = VMRuntime.getRuntime(); 26*795d594fSAndroid Build Coastguard Worker static volatile boolean aboutToDeadlock = false; 27*795d594fSAndroid Build Coastguard Worker static final long MAX_EXPECTED_GC_DURATION_MS = 2000; 28*795d594fSAndroid Build Coastguard Worker 29*795d594fSAndroid Build Coastguard Worker // Save ref as a static field to ensure it doesn't get GC'd before the 30*795d594fSAndroid Build Coastguard Worker // referent is enqueued. 31*795d594fSAndroid Build Coastguard Worker static PhantomReference ref = null; 32*795d594fSAndroid Build Coastguard Worker 33*795d594fSAndroid Build Coastguard Worker static class DeadlockingFinalizer { finalize()34*795d594fSAndroid Build Coastguard Worker protected void finalize() throws Exception { 35*795d594fSAndroid Build Coastguard Worker aboutToDeadlock = true; 36*795d594fSAndroid Build Coastguard Worker synchronized (deadlockLock) { } 37*795d594fSAndroid Build Coastguard Worker } 38*795d594fSAndroid Build Coastguard Worker } 39*795d594fSAndroid Build Coastguard Worker $noinline$allocateDeadlockingFinalizer()40*795d594fSAndroid Build Coastguard Worker private static void $noinline$allocateDeadlockingFinalizer() { 41*795d594fSAndroid Build Coastguard Worker new DeadlockingFinalizer(); 42*795d594fSAndroid Build Coastguard Worker } 43*795d594fSAndroid Build Coastguard Worker 44*795d594fSAndroid Build Coastguard Worker static AtomicInteger finalizeCounter = new AtomicInteger(0); 45*795d594fSAndroid Build Coastguard Worker 46*795d594fSAndroid Build Coastguard Worker static class IncrementingFinalizer { finalize()47*795d594fSAndroid Build Coastguard Worker protected void finalize() throws Exception { 48*795d594fSAndroid Build Coastguard Worker finalizeCounter.incrementAndGet(); 49*795d594fSAndroid Build Coastguard Worker } 50*795d594fSAndroid Build Coastguard Worker } 51*795d594fSAndroid Build Coastguard Worker $noinline$allocateIncrementingFinalizer()52*795d594fSAndroid Build Coastguard Worker private static void $noinline$allocateIncrementingFinalizer() { 53*795d594fSAndroid Build Coastguard Worker new IncrementingFinalizer(); 54*795d594fSAndroid Build Coastguard Worker } 55*795d594fSAndroid Build Coastguard Worker $noinline$allocPhantom(ReferenceQueue<Object> queue)56*795d594fSAndroid Build Coastguard Worker public static PhantomReference $noinline$allocPhantom(ReferenceQueue<Object> queue) { 57*795d594fSAndroid Build Coastguard Worker return new PhantomReference(new Object(), queue); 58*795d594fSAndroid Build Coastguard Worker } 59*795d594fSAndroid Build Coastguard Worker 60*795d594fSAndroid Build Coastguard Worker // Test that calling registerNativeAllocation triggers a GC eventually 61*795d594fSAndroid Build Coastguard Worker // after a substantial number of registered native bytes. checkRegisterNativeAllocation()62*795d594fSAndroid Build Coastguard Worker private static void checkRegisterNativeAllocation() throws Exception { 63*795d594fSAndroid Build Coastguard Worker long maxMem = Runtime.getRuntime().maxMemory(); 64*795d594fSAndroid Build Coastguard Worker int size = (int)(maxMem / 32); 65*795d594fSAndroid Build Coastguard Worker int allocationCount = 256; 66*795d594fSAndroid Build Coastguard Worker final long startTime = System.currentTimeMillis(); 67*795d594fSAndroid Build Coastguard Worker 68*795d594fSAndroid Build Coastguard Worker int initialFinalizeCount = finalizeCounter.get(); 69*795d594fSAndroid Build Coastguard Worker ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); 70*795d594fSAndroid Build Coastguard Worker ref = $noinline$allocPhantom(queue); 71*795d594fSAndroid Build Coastguard Worker long total = 0; 72*795d594fSAndroid Build Coastguard Worker int i; 73*795d594fSAndroid Build Coastguard Worker for (i = 0; !ref.isEnqueued() && i < allocationCount; ++i) { 74*795d594fSAndroid Build Coastguard Worker runtime.registerNativeAllocation(size); 75*795d594fSAndroid Build Coastguard Worker total += size; 76*795d594fSAndroid Build Coastguard Worker 77*795d594fSAndroid Build Coastguard Worker // Allocate a new finalizable object each time, so that we can see if anything 78*795d594fSAndroid Build Coastguard Worker // was finalized while we were running. 79*795d594fSAndroid Build Coastguard Worker $noinline$allocateIncrementingFinalizer(); 80*795d594fSAndroid Build Coastguard Worker 81*795d594fSAndroid Build Coastguard Worker // Sleep a little bit to ensure not all of the calls to 82*795d594fSAndroid Build Coastguard Worker // registerNativeAllocation complete while GC is in the process of 83*795d594fSAndroid Build Coastguard Worker // running. 84*795d594fSAndroid Build Coastguard Worker Thread.sleep(MAX_EXPECTED_GC_DURATION_MS / allocationCount); 85*795d594fSAndroid Build Coastguard Worker } 86*795d594fSAndroid Build Coastguard Worker 87*795d594fSAndroid Build Coastguard Worker // Wait up to MAX_EXPECTED_GC_DURATION_MS to give GC a chance to finish 88*795d594fSAndroid Build Coastguard Worker // running. If the reference isn't enqueued after that, then it is 89*795d594fSAndroid Build Coastguard Worker // pretty unlikely (though technically still possible) that GC was 90*795d594fSAndroid Build Coastguard Worker // triggered as intended. 91*795d594fSAndroid Build Coastguard Worker if (queue.remove(MAX_EXPECTED_GC_DURATION_MS) == null) { 92*795d594fSAndroid Build Coastguard Worker System.out.println("GC failed to complete after " + i 93*795d594fSAndroid Build Coastguard Worker + " iterations, is_enqueued = " + ref.isEnqueued()); 94*795d594fSAndroid Build Coastguard Worker System.out.println(" size = " + size + ", elapsed msecs = " 95*795d594fSAndroid Build Coastguard Worker + (System.currentTimeMillis() - startTime)); 96*795d594fSAndroid Build Coastguard Worker System.out.println(" original maxMemory() = " + maxMem + " current maxMemory() = " 97*795d594fSAndroid Build Coastguard Worker + Runtime.getRuntime().maxMemory()); 98*795d594fSAndroid Build Coastguard Worker System.out.println(" Initial finalize count = " + initialFinalizeCount 99*795d594fSAndroid Build Coastguard Worker + " current finalize count = " + finalizeCounter.get()); 100*795d594fSAndroid Build Coastguard Worker Thread.sleep(2 * MAX_EXPECTED_GC_DURATION_MS); 101*795d594fSAndroid Build Coastguard Worker System.out.println(" After delay, queue.poll() = " + queue.poll() 102*795d594fSAndroid Build Coastguard Worker + " is_enqueued = " + ref.isEnqueued()); 103*795d594fSAndroid Build Coastguard Worker System.out.println(" elapsed msecs = " + (System.currentTimeMillis() - startTime)); 104*795d594fSAndroid Build Coastguard Worker Runtime.getRuntime().gc(); 105*795d594fSAndroid Build Coastguard Worker System.runFinalization(); 106*795d594fSAndroid Build Coastguard Worker System.out.println(" After GC, queue.poll() = " + queue.poll() 107*795d594fSAndroid Build Coastguard Worker + " is_enqueued = " + ref.isEnqueued()); 108*795d594fSAndroid Build Coastguard Worker } 109*795d594fSAndroid Build Coastguard Worker 110*795d594fSAndroid Build Coastguard Worker while (total > 0) { 111*795d594fSAndroid Build Coastguard Worker runtime.registerNativeFree(size); 112*795d594fSAndroid Build Coastguard Worker total -= size; 113*795d594fSAndroid Build Coastguard Worker } 114*795d594fSAndroid Build Coastguard Worker } 115*795d594fSAndroid Build Coastguard Worker 116*795d594fSAndroid Build Coastguard Worker // Call registerNativeAllocation repeatedly at a high rate to trigger the case of blocking 117*795d594fSAndroid Build Coastguard Worker // registerNativeAllocation. Stop before we risk exhausting the finalizer timeout. triggerBlockingRegisterNativeAllocation()118*795d594fSAndroid Build Coastguard Worker private static void triggerBlockingRegisterNativeAllocation() throws Exception { 119*795d594fSAndroid Build Coastguard Worker final long startTime = System.currentTimeMillis(); 120*795d594fSAndroid Build Coastguard Worker final long finalizerTimeoutMs = VMRuntime.getRuntime().getFinalizerTimeoutMs(); 121*795d594fSAndroid Build Coastguard Worker final long quittingTime = startTime + finalizerTimeoutMs - MAX_EXPECTED_GC_DURATION_MS; 122*795d594fSAndroid Build Coastguard Worker long maxMem = Runtime.getRuntime().maxMemory(); 123*795d594fSAndroid Build Coastguard Worker int size = (int)(maxMem / 5); 124*795d594fSAndroid Build Coastguard Worker int allocationCount = 10; 125*795d594fSAndroid Build Coastguard Worker 126*795d594fSAndroid Build Coastguard Worker long total = 0; 127*795d594fSAndroid Build Coastguard Worker for (int i = 0; i < allocationCount && System.currentTimeMillis() < quittingTime; ++i) { 128*795d594fSAndroid Build Coastguard Worker runtime.registerNativeAllocation(size); 129*795d594fSAndroid Build Coastguard Worker total += size; 130*795d594fSAndroid Build Coastguard Worker } 131*795d594fSAndroid Build Coastguard Worker 132*795d594fSAndroid Build Coastguard Worker while (total > 0) { 133*795d594fSAndroid Build Coastguard Worker runtime.registerNativeFree(size); 134*795d594fSAndroid Build Coastguard Worker total -= size; 135*795d594fSAndroid Build Coastguard Worker } 136*795d594fSAndroid Build Coastguard Worker } 137*795d594fSAndroid Build Coastguard Worker main(String[] args)138*795d594fSAndroid Build Coastguard Worker public static void main(String[] args) throws Exception { 139*795d594fSAndroid Build Coastguard Worker // Test that registerNativeAllocation triggers GC. 140*795d594fSAndroid Build Coastguard Worker // Run this a few times in a loop to reduce the chances that the test 141*795d594fSAndroid Build Coastguard Worker // is flaky and make sure registerNativeAllocation continues to work 142*795d594fSAndroid Build Coastguard Worker // after the first GC is triggered. 143*795d594fSAndroid Build Coastguard Worker for (int i = 0; i < 20; ++i) { 144*795d594fSAndroid Build Coastguard Worker checkRegisterNativeAllocation(); 145*795d594fSAndroid Build Coastguard Worker } 146*795d594fSAndroid Build Coastguard Worker 147*795d594fSAndroid Build Coastguard Worker // Test that we don't get a deadlock if we call 148*795d594fSAndroid Build Coastguard Worker // registerNativeAllocation with a blocked finalizer. 149*795d594fSAndroid Build Coastguard Worker synchronized (deadlockLock) { 150*795d594fSAndroid Build Coastguard Worker $noinline$allocateDeadlockingFinalizer(); 151*795d594fSAndroid Build Coastguard Worker while (!aboutToDeadlock) { 152*795d594fSAndroid Build Coastguard Worker Runtime.getRuntime().gc(); 153*795d594fSAndroid Build Coastguard Worker } 154*795d594fSAndroid Build Coastguard Worker 155*795d594fSAndroid Build Coastguard Worker // Do more allocations now that the finalizer thread is deadlocked so that we force 156*795d594fSAndroid Build Coastguard Worker // finalization and timeout. 157*795d594fSAndroid Build Coastguard Worker triggerBlockingRegisterNativeAllocation(); 158*795d594fSAndroid Build Coastguard Worker } 159*795d594fSAndroid Build Coastguard Worker System.out.println("Test complete"); 160*795d594fSAndroid Build Coastguard Worker } 161*795d594fSAndroid Build Coastguard Worker } 162*795d594fSAndroid Build Coastguard Worker 163