Avoid running Runtime.gc() until we need to run finalization.

This prevents excessive explicit GC which are called from apps to get
good GC behavior on Dalvik. Calling System.gc() does not help on ART
since GC for alloc is much rarer.

If running finalizers is requested following a System.gc we remember
that a GC was requested and perform it ahead of finalization.

based on commit 930e26f0f59c0ce1020524269c82492f3c4ea722

Bug: 12004934
Bug: 26807871
Change-Id: I1a1455b0f3f229d2c507f8330fe6d31d6940ea21
diff --git a/ojluni/src/main/java/java/lang/System.java b/ojluni/src/main/java/java/lang/System.java
index 4635ae5..f296bf8 100755
--- a/ojluni/src/main/java/java/lang/System.java
+++ b/ojluni/src/main/java/java/lang/System.java
@@ -106,6 +106,22 @@
     public final static PrintStream err;
 
     /**
+     * Dedicated lock for GC / Finalization logic.
+     */
+    private static final Object LOCK = new Object();
+
+    /**
+     * Whether or not we need to do a GC before running the finalizers.
+     */
+    private static boolean runGC;
+
+    /**
+     * If we just ran finalization, we might want to do a GC to free the finalized objects.
+     * This lets us do gc/runFinlization/gc sequences but prevents back to back System.gc().
+     */
+    private static boolean justRanFinalization;
+
+    /**
      * Reassigns the "standard" input stream.
      *
      * <p>First, if there is a security manager, its <code>checkPermission</code>
@@ -1497,7 +1513,18 @@
      * @see     java.lang.Runtime#gc()
      */
     public static void gc() {
-        Runtime.getRuntime().gc();
+        boolean shouldRunGC;
+        synchronized (LOCK) {
+            shouldRunGC = justRanFinalization;
+            if (shouldRunGC) {
+                justRanFinalization = false;
+            } else {
+                runGC = true;
+            }
+        }
+        if (shouldRunGC) {
+            Runtime.getRuntime().gc();
+        }
     }
 
     /**
@@ -1519,7 +1546,18 @@
      * @see     java.lang.Runtime#runFinalization()
      */
     public static void runFinalization() {
+        boolean shouldRunGC;
+        synchronized (LOCK) {
+            shouldRunGC = runGC;
+            runGC = false;
+        }
+        if (shouldRunGC) {
+            Runtime.getRuntime().gc();
+        }
         Runtime.getRuntime().runFinalization();
+        synchronized (LOCK) {
+            justRanFinalization = true;
+        }
     }
 
     /**