Make 2040-huge-native-alloc more robust

Most importantly, call gc() before we start, so we start with a clean
slate.

Allocate a bit more to make sure we're over the threshold for
triggering blocking.

Get a third reference time. Try twice to trigger a blocking GC.

Bug: 190148586
Test: run-test --host 2040-huge-native-alloc
Change-Id: If7f8aa8baec715c6f295384ce70b0b349ea2c71d
diff --git a/test/2040-huge-native-alloc/src/Main.java b/test/2040-huge-native-alloc/src/Main.java
index 0366e5e..9579040 100644
--- a/test/2040-huge-native-alloc/src/Main.java
+++ b/test/2040-huge-native-alloc/src/Main.java
@@ -20,7 +20,7 @@
 
 public class Main {
 
-  static final int HOW_MANY_HUGE = 110;  // > 1GB to trigger blocking in default config.
+  static final int HOW_MANY_HUGE = 120;  // > 1GB to trigger blocking in default config.
   int allocated = 0;
   int deallocated = 0;
   static Object lock = new Object();
@@ -43,8 +43,8 @@
 
   // Repeatedly inform the GC of native allocations. Return the time (in nsecs) this takes.
   private static long timeNotifications() {
-    VMRuntime vmr = VMRuntime.getRuntime();
-    long startNanos = System.nanoTime();
+    final VMRuntime vmr = VMRuntime.getRuntime();
+    final long startNanos = System.nanoTime();
     // Iteration count must be >= Heap::kNotifyNativeInterval.
     for (int i = 0; i < 400; ++i) {
       vmr.notifyNativeAllocation();
@@ -55,15 +55,17 @@
   public static void main(String[] args) {
     System.loadLibrary(args[0]);
     System.out.println("Main Started");
+    Runtime.getRuntime().gc();
     new Main().run();
     System.out.println("Main Finished");
   }
 
   void run() {
     timeNotifications();  // warm up.
-    long referenceTime1 = timeNotifications();
-    long referenceTime2 = timeNotifications();
-    long referenceTime = Math.min(referenceTime1, referenceTime2);
+    final long referenceTime1 = timeNotifications();
+    final long referenceTime2 = timeNotifications();
+    final long referenceTime3 = timeNotifications();
+    final long referenceTime = Math.min(referenceTime1, Math.min(referenceTime2, referenceTime3));
 
     // Allocate half a GB of native memory without informing the GC.
     for (int i = 0; i < HOW_MANY_HUGE; ++i) {
@@ -72,13 +74,18 @@
 
     // One of the notifications should block for GC to catch up.
     long actualTime = timeNotifications();
+    final long minBlockingTime = 2 * referenceTime + 2_000_000;
 
     if (actualTime > 500_000_000) {
       System.out.println("Notifications ran too slowly; excessive blocking? msec = "
           + (actualTime / 1_000_000));
-    } else if (actualTime < 3 * referenceTime + 2_000_000) {
-      System.out.println("Notifications ran too quickly; no blocking GC? msec = "
-          + (actualTime / 1_000_000));
+    } else if (actualTime < minBlockingTime) {
+      // Try again before reporting.
+      actualTime = timeNotifications();
+      if (actualTime < minBlockingTime) {
+        System.out.println("Notifications ran too quickly; no blocking GC? msec = "
+            + (actualTime / 1_000_000) + " reference(msec) = " + (referenceTime / 1_000_000));
+      }
     }
 
     // Let finalizers run.