xref: /aosp_15_r20/art/test/2040-huge-native-alloc/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2021 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 dalvik.system.VMRuntime;
18*795d594fSAndroid Build Coastguard Worker import java.lang.ref.WeakReference;
19*795d594fSAndroid Build Coastguard Worker import java.nio.ByteBuffer;
20*795d594fSAndroid Build Coastguard Worker 
21*795d594fSAndroid Build Coastguard Worker public class Main {
22*795d594fSAndroid Build Coastguard Worker 
23*795d594fSAndroid Build Coastguard Worker   static final int HOW_MANY_HUGE = 120;  // > 1GB to trigger blocking in default config.
24*795d594fSAndroid Build Coastguard Worker   int allocated = 0;
25*795d594fSAndroid Build Coastguard Worker   int deallocated = 0;
26*795d594fSAndroid Build Coastguard Worker   static Object lock = new Object();
27*795d594fSAndroid Build Coastguard Worker   final static int MAX_TRIES = 10;
28*795d594fSAndroid Build Coastguard Worker   WeakReference<BufferHolder>[] references = new WeakReference[HOW_MANY_HUGE];
29*795d594fSAndroid Build Coastguard Worker 
30*795d594fSAndroid Build Coastguard Worker   class BufferHolder {
31*795d594fSAndroid Build Coastguard Worker     private ByteBuffer buffer;
BufferHolder()32*795d594fSAndroid Build Coastguard Worker     BufferHolder() {
33*795d594fSAndroid Build Coastguard Worker       ++allocated;
34*795d594fSAndroid Build Coastguard Worker       buffer = getHugeNativeBuffer();
35*795d594fSAndroid Build Coastguard Worker     }
finalize()36*795d594fSAndroid Build Coastguard Worker     protected void finalize() {
37*795d594fSAndroid Build Coastguard Worker       synchronized(lock) {
38*795d594fSAndroid Build Coastguard Worker         ++deallocated;
39*795d594fSAndroid Build Coastguard Worker       }
40*795d594fSAndroid Build Coastguard Worker       deleteHugeNativeBuffer(buffer);
41*795d594fSAndroid Build Coastguard Worker       buffer = null;
42*795d594fSAndroid Build Coastguard Worker     }
43*795d594fSAndroid Build Coastguard Worker   }
44*795d594fSAndroid Build Coastguard Worker 
45*795d594fSAndroid Build Coastguard Worker   // Repeatedly inform the GC of native allocations. Return the time (in nsecs) this takes.
timeNotifications()46*795d594fSAndroid Build Coastguard Worker   private static long timeNotifications() {
47*795d594fSAndroid Build Coastguard Worker     final VMRuntime vmr = VMRuntime.getRuntime();
48*795d594fSAndroid Build Coastguard Worker     final long startNanos = System.nanoTime();
49*795d594fSAndroid Build Coastguard Worker     // Iteration count must be >= Heap::kNotifyNativeInterval.
50*795d594fSAndroid Build Coastguard Worker     for (int i = 0; i < 400; ++i) {
51*795d594fSAndroid Build Coastguard Worker       vmr.notifyNativeAllocation();
52*795d594fSAndroid Build Coastguard Worker     }
53*795d594fSAndroid Build Coastguard Worker     return System.nanoTime() - startNanos;
54*795d594fSAndroid Build Coastguard Worker   }
55*795d594fSAndroid Build Coastguard Worker 
main(String[] args)56*795d594fSAndroid Build Coastguard Worker   public static void main(String[] args) {
57*795d594fSAndroid Build Coastguard Worker     System.loadLibrary(args[0]);
58*795d594fSAndroid Build Coastguard Worker     System.out.println("Main Started");
59*795d594fSAndroid Build Coastguard Worker     for (int i = 1; i <= MAX_TRIES; ++i) {
60*795d594fSAndroid Build Coastguard Worker       Runtime.getRuntime().gc();
61*795d594fSAndroid Build Coastguard Worker       if (new Main().tryToRun(i == MAX_TRIES)) {
62*795d594fSAndroid Build Coastguard Worker         break;
63*795d594fSAndroid Build Coastguard Worker       }
64*795d594fSAndroid Build Coastguard Worker       if (i == MAX_TRIES / 2) {
65*795d594fSAndroid Build Coastguard Worker         // Maybe some transient CPU load is causing issues here?
66*795d594fSAndroid Build Coastguard Worker         try {
67*795d594fSAndroid Build Coastguard Worker           Thread.sleep(3000);
68*795d594fSAndroid Build Coastguard Worker         } catch (InterruptedException ignored) {
69*795d594fSAndroid Build Coastguard Worker           System.out.println("Unexpected interrupt");
70*795d594fSAndroid Build Coastguard Worker         }
71*795d594fSAndroid Build Coastguard Worker       }
72*795d594fSAndroid Build Coastguard Worker       // Clean up and try again.
73*795d594fSAndroid Build Coastguard Worker       Runtime.getRuntime().gc();
74*795d594fSAndroid Build Coastguard Worker       System.runFinalization();
75*795d594fSAndroid Build Coastguard Worker     }
76*795d594fSAndroid Build Coastguard Worker     System.out.println("Main Finished");
77*795d594fSAndroid Build Coastguard Worker   }
78*795d594fSAndroid Build Coastguard Worker 
79*795d594fSAndroid Build Coastguard Worker   // Returns false on a failure that should be retried.
tryToRun(boolean lastChance)80*795d594fSAndroid Build Coastguard Worker   boolean tryToRun(boolean lastChance) {
81*795d594fSAndroid Build Coastguard Worker     final int startingGcNum = getGcNum();
82*795d594fSAndroid Build Coastguard Worker     timeNotifications();  // warm up.
83*795d594fSAndroid Build Coastguard Worker     final long referenceTime1 = timeNotifications();
84*795d594fSAndroid Build Coastguard Worker     final long referenceTime2 = timeNotifications();
85*795d594fSAndroid Build Coastguard Worker     final long referenceTime3 = timeNotifications();
86*795d594fSAndroid Build Coastguard Worker     final long referenceTime = Math.min(referenceTime1, Math.min(referenceTime2, referenceTime3));
87*795d594fSAndroid Build Coastguard Worker 
88*795d594fSAndroid Build Coastguard Worker     // Allocate a GB+ of native memory without informing the GC.
89*795d594fSAndroid Build Coastguard Worker     for (int i = 0; i < HOW_MANY_HUGE; ++i) {
90*795d594fSAndroid Build Coastguard Worker       new BufferHolder();
91*795d594fSAndroid Build Coastguard Worker     }
92*795d594fSAndroid Build Coastguard Worker 
93*795d594fSAndroid Build Coastguard Worker     if (startingGcNum != getGcNum()) {
94*795d594fSAndroid Build Coastguard Worker       // Happens rarely, fail and retry.
95*795d594fSAndroid Build Coastguard Worker       if (!lastChance) {
96*795d594fSAndroid Build Coastguard Worker         return false;
97*795d594fSAndroid Build Coastguard Worker       }
98*795d594fSAndroid Build Coastguard Worker       System.out.println("Triggered early GC");
99*795d594fSAndroid Build Coastguard Worker     }
100*795d594fSAndroid Build Coastguard Worker     // One of the notifications should block for GC to catch up.
101*795d594fSAndroid Build Coastguard Worker     long actualTime = timeNotifications();
102*795d594fSAndroid Build Coastguard Worker     final long minBlockingTime = 2 * referenceTime + 2_000_000;
103*795d594fSAndroid Build Coastguard Worker 
104*795d594fSAndroid Build Coastguard Worker     if (startingGcNum == getGcNum()) {
105*795d594fSAndroid Build Coastguard Worker       System.out.println("No gc completed");
106*795d594fSAndroid Build Coastguard Worker     }
107*795d594fSAndroid Build Coastguard Worker     if (actualTime > 500_000_000) {
108*795d594fSAndroid Build Coastguard Worker       System.out.println("Notifications ran too slowly; excessive blocking? msec = "
109*795d594fSAndroid Build Coastguard Worker           + (actualTime / 1_000_000));
110*795d594fSAndroid Build Coastguard Worker     } else if (actualTime < minBlockingTime) {
111*795d594fSAndroid Build Coastguard Worker       if (!lastChance) {
112*795d594fSAndroid Build Coastguard Worker         // We sometimes see this, maybe because a GC is triggered by other means?
113*795d594fSAndroid Build Coastguard Worker         // Try again before reporting.
114*795d594fSAndroid Build Coastguard Worker         return false;
115*795d594fSAndroid Build Coastguard Worker       }
116*795d594fSAndroid Build Coastguard Worker       System.out.println("Notifications ran too quickly; no blocking GC? msec = "
117*795d594fSAndroid Build Coastguard Worker           + (actualTime / 1_000_000) + " reference(msec) = " + (referenceTime / 1_000_000));
118*795d594fSAndroid Build Coastguard Worker     }
119*795d594fSAndroid Build Coastguard Worker 
120*795d594fSAndroid Build Coastguard Worker     // Let finalizers run.
121*795d594fSAndroid Build Coastguard Worker     try {
122*795d594fSAndroid Build Coastguard Worker       Thread.sleep(3000);
123*795d594fSAndroid Build Coastguard Worker     } catch (InterruptedException e) {
124*795d594fSAndroid Build Coastguard Worker       System.out.println("Unexpected interrupt");
125*795d594fSAndroid Build Coastguard Worker     }
126*795d594fSAndroid Build Coastguard Worker 
127*795d594fSAndroid Build Coastguard Worker     if (deallocated > allocated || deallocated < allocated - 5 /* slop for register references */) {
128*795d594fSAndroid Build Coastguard Worker       System.out.println("Unexpected number of deallocated objects:");
129*795d594fSAndroid Build Coastguard Worker       System.out.println("Allocated = " + allocated + " deallocated = " + deallocated);
130*795d594fSAndroid Build Coastguard Worker     }
131*795d594fSAndroid Build Coastguard Worker     System.out.println("Succeeded");
132*795d594fSAndroid Build Coastguard Worker     return true;
133*795d594fSAndroid Build Coastguard Worker   }
134*795d594fSAndroid Build Coastguard Worker 
getHugeNativeBuffer()135*795d594fSAndroid Build Coastguard Worker   private static native ByteBuffer getHugeNativeBuffer();
deleteHugeNativeBuffer(ByteBuffer buf)136*795d594fSAndroid Build Coastguard Worker   private static native void deleteHugeNativeBuffer(ByteBuffer buf);
getGcNum()137*795d594fSAndroid Build Coastguard Worker   private static native int getGcNum();
138*795d594fSAndroid Build Coastguard Worker }
139