xref: /aosp_15_r20/art/test/004-NativeAllocations/src-art/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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