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