blob: 59e12669503ba26e972531fe7727b14b2514465a [file] [log] [blame]
Hans Boehm6031ec12021-05-25 22:08:59 +00001/*
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
17import dalvik.system.VMRuntime;
18import java.lang.ref.WeakReference;
19import java.nio.ByteBuffer;
20
21public class Main {
22
Hans Boehmd0020ba2021-06-07 15:44:45 -070023 static final int HOW_MANY_HUGE = 120; // > 1GB to trigger blocking in default config.
Hans Boehm6031ec12021-05-25 22:08:59 +000024 int allocated = 0;
25 int deallocated = 0;
26 static Object lock = new Object();
Hans Boehmdc00c3a2021-07-26 18:16:30 -070027 final static int MAX_TRIES = 4;
Hans Boehm6031ec12021-05-25 22:08:59 +000028 WeakReference<BufferHolder>[] references = new WeakReference[HOW_MANY_HUGE];
29
30 class BufferHolder {
31 private ByteBuffer buffer;
32 BufferHolder() {
33 ++allocated;
34 buffer = getHugeNativeBuffer();
35 }
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.
46 private static long timeNotifications() {
Hans Boehmd0020ba2021-06-07 15:44:45 -070047 final VMRuntime vmr = VMRuntime.getRuntime();
48 final long startNanos = System.nanoTime();
Hans Boehm6031ec12021-05-25 22:08:59 +000049 // 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
56 public static void main(String[] args) {
57 System.loadLibrary(args[0]);
58 System.out.println("Main Started");
Hans Boehmdc00c3a2021-07-26 18:16:30 -070059 for (int i = 1; i <= MAX_TRIES; ++i) {
Hans Boehm602da4f2021-06-14 17:51:41 -070060 Runtime.getRuntime().gc();
Hans Boehmdc00c3a2021-07-26 18:16:30 -070061 if (new Main().tryToRun(i == MAX_TRIES)) {
Hans Boehm602da4f2021-06-14 17:51:41 -070062 break;
63 }
64 // Clean up and try again.
65 Runtime.getRuntime().gc();
66 System.runFinalization();
67 }
Hans Boehm6031ec12021-05-25 22:08:59 +000068 System.out.println("Main Finished");
69 }
70
Hans Boehmdc00c3a2021-07-26 18:16:30 -070071 // Returns false on a failure that should be retried.
72 boolean tryToRun(boolean lastChance) {
Hans Boehm602da4f2021-06-14 17:51:41 -070073 final int startingGcNum = getGcNum();
Hans Boehm6031ec12021-05-25 22:08:59 +000074 timeNotifications(); // warm up.
Hans Boehmd0020ba2021-06-07 15:44:45 -070075 final long referenceTime1 = timeNotifications();
76 final long referenceTime2 = timeNotifications();
77 final long referenceTime3 = timeNotifications();
78 final long referenceTime = Math.min(referenceTime1, Math.min(referenceTime2, referenceTime3));
Hans Boehm6031ec12021-05-25 22:08:59 +000079
Hans Boehm602da4f2021-06-14 17:51:41 -070080 // Allocate a GB+ of native memory without informing the GC.
Hans Boehm6031ec12021-05-25 22:08:59 +000081 for (int i = 0; i < HOW_MANY_HUGE; ++i) {
82 new BufferHolder();
83 }
84
Hans Boehm602da4f2021-06-14 17:51:41 -070085 if (startingGcNum != getGcNum()) {
86 // Happens rarely, fail and retry.
87 return false;
88 }
Hans Boehm6031ec12021-05-25 22:08:59 +000089 // One of the notifications should block for GC to catch up.
90 long actualTime = timeNotifications();
Hans Boehmd0020ba2021-06-07 15:44:45 -070091 final long minBlockingTime = 2 * referenceTime + 2_000_000;
Hans Boehm6031ec12021-05-25 22:08:59 +000092
Hans Boehm602da4f2021-06-14 17:51:41 -070093 if (startingGcNum == getGcNum()) {
94 System.out.println("No gc completed");
95 }
Hans Boehm6031ec12021-05-25 22:08:59 +000096 if (actualTime > 500_000_000) {
97 System.out.println("Notifications ran too slowly; excessive blocking? msec = "
98 + (actualTime / 1_000_000));
Hans Boehmd0020ba2021-06-07 15:44:45 -070099 } else if (actualTime < minBlockingTime) {
Hans Boehmdc00c3a2021-07-26 18:16:30 -0700100 if (!lastChance) {
101 // We sometimes see this, maybe because a GC is triggered by other means?
102 // Try again before reporting.
103 return false;
Hans Boehmd0020ba2021-06-07 15:44:45 -0700104 }
Hans Boehmdc00c3a2021-07-26 18:16:30 -0700105 System.out.println("Notifications ran too quickly; no blocking GC? msec = "
106 + (actualTime / 1_000_000) + " reference(msec) = " + (referenceTime / 1_000_000));
Hans Boehm6031ec12021-05-25 22:08:59 +0000107 }
108
109 // Let finalizers run.
110 try {
111 Thread.sleep(3000);
112 } catch (InterruptedException e) {
113 System.out.println("Unexpected interrupt");
114 }
115
116 if (deallocated > allocated || deallocated < allocated - 5 /* slop for register references */) {
117 System.out.println("Unexpected number of deallocated objects:");
118 System.out.println("Allocated = " + allocated + " deallocated = " + deallocated);
119 }
Hans Boehm602da4f2021-06-14 17:51:41 -0700120 System.out.println("Succeeded");
121 return true;
Hans Boehm6031ec12021-05-25 22:08:59 +0000122 }
123
124 private static native ByteBuffer getHugeNativeBuffer();
125 private static native void deleteHugeNativeBuffer(ByteBuffer buf);
Hans Boehm602da4f2021-06-14 17:51:41 -0700126 private static native int getGcNum();
Hans Boehm6031ec12021-05-25 22:08:59 +0000127}