Remove UnifiedAST memory calculation and clean up instance-creation aspects of UnifiedAST.
This patch is in preparation for pluggable CompilePerms workers.

Patch by: scottb
Review by: bobv


git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@4129 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/PermutationCompiler.java b/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
index f89b102..3b6f85d 100644
--- a/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
@@ -19,9 +19,8 @@
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.dev.cfg.BindingProperty;
 import com.google.gwt.dev.cfg.StaticPropertyOracle;
-import com.google.gwt.dev.jjs.UnifiedAst;
 import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
-import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.UnifiedAst;
 import com.google.gwt.dev.util.PerfLogger;
 
 import java.util.concurrent.BlockingQueue;
@@ -191,14 +190,6 @@
         });
       }
 
-      if (!hasEnoughMemory()) {
-        /*
-         * Not enough memory to run, but if there are multiple threads, we can
-         * try again with fewer threads.
-         */
-        tryToExitNonFinalThread(outOfMemoryRetryAction);
-      }
-
       boolean definitelyFinalThread = (threadCount.get() == 1);
       try {
         String result = currentTask.call();
@@ -247,26 +238,6 @@
     }
 
     /**
-     * Returns <code>true</code> if there is enough estimated memory to run
-     * another permutation, or if this is the last live worker thread and we
-     * have no choice.
-     */
-    private boolean hasEnoughMemory() {
-      if (threadCount.get() == 1) {
-        // I'm the last thread, so I have to at least try.
-        return true;
-      }
-
-      if (astMemoryUsage >= getPotentialFreeMemory()) {
-        return true;
-      }
-
-      // Best effort memory reclaim.
-      System.gc();
-      return astMemoryUsage < getPotentialFreeMemory();
-    }
-
-    /**
      * Exits this thread if and only if it's not the last running thread,
      * performing the specified action before terminating.
      * 
@@ -294,21 +265,6 @@
   private static final Result FINISHED_RESULT = new Result(null, -1) {
   };
 
-  private static long getPotentialFreeMemory() {
-    long used = Runtime.getRuntime().totalMemory()
-        - Runtime.getRuntime().freeMemory();
-    assert (used > 0);
-    long potentialFree = Runtime.getRuntime().maxMemory() - used;
-    assert (potentialFree >= 0);
-    return potentialFree;
-  }
-
-  /**
-   * Holds an estimate of how many bytes of memory a new concurrent compilation
-   * will consume.
-   */
-  protected final long astMemoryUsage;
-
   /**
    * A queue of results being sent from worker threads to the main thread.
    */
@@ -329,7 +285,6 @@
   public PermutationCompiler(TreeLogger logger, UnifiedAst unifiedAst,
       Permutation[] perms, int[] permsToRun) {
     this.logger = logger;
-    this.astMemoryUsage = unifiedAst.getAstMemoryUsage();
     for (int permToRun : permsToRun) {
       tasks.add(new PermutationTask(logger, unifiedAst, perms[permToRun],
           permToRun));
@@ -387,52 +342,10 @@
     result = Math.min(Runtime.getRuntime().availableProcessors(), result);
 
     /*
-     * Allow user-defined override as an escape valve.
+     * User-defined value caps.
      */
-    result = Math.min(result, Integer.getInteger("gwt.jjs.maxThreads", result));
+    result = Math.min(result, Integer.getInteger("gwt.jjs.maxThreads", 1));
 
-    if (result == 1) {
-      return 1;
-    }
-
-    // More than one thread would definitely be faster at this point.
-
-    if (JProgram.isTracingEnabled()) {
-      logger.log(TreeLogger.INFO,
-          "Parallel compilation disabled due to gwt.jjs.traceMethods being enabled");
-      return 1;
-    }
-
-    int desiredThreads = result;
-
-    /*
-     * Need to do some memory estimation to figure out how many concurrent
-     * threads we can safely run.
-     */
-    long potentialFreeMemory = getPotentialFreeMemory();
-    int extraMemUsageThreads = (int) (potentialFreeMemory / astMemoryUsage);
-    logger.log(TreeLogger.TRACE,
-        "Extra threads constrained by estimated memory usage: "
-            + extraMemUsageThreads + " = " + potentialFreeMemory + " / "
-            + astMemoryUsage);
-    int memUsageThreads = extraMemUsageThreads + 1;
-
-    if (memUsageThreads < desiredThreads) {
-      long currentMaxMemory = Runtime.getRuntime().maxMemory();
-      // Convert to megabytes.
-      currentMaxMemory /= 1024 * 1024;
-
-      long suggestedMaxMemory = currentMaxMemory * 2;
-
-      logger.log(TreeLogger.WARN, desiredThreads
-          + " threads could be run concurrently, but only " + memUsageThreads
-          + " threads will be run due to limited memory; "
-          + "increasing the amount of memory by using the -Xmx flag "
-          + "at startup (java -Xmx" + suggestedMaxMemory
-          + "M ...) may result in faster compiles");
-    }
-
-    result = Math.min(memUsageThreads, desiredThreads);
     return result;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index a78a2d1..96f2f59 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -255,8 +255,6 @@
     JsProgram jsProgram = new JsProgram();
 
     try {
-      long usedMemoryBefore = singlePermutation ? 0 : getUsedMemory();
-
       /*
        * (1) Build a flattened map of TypeDeclarations => JType. The resulting
        * map contains entries for all reference types. BuildTypeMap also parses
@@ -276,10 +274,6 @@
       GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram, jsProgram,
           options.isEnableAssertions());
 
-      long usedMemoryAfter = singlePermutation ? 0 : getUsedMemory();
-      long memoryDelta = usedMemoryAfter - usedMemoryBefore;
-      long astMemoryUsage = (long) (memoryDelta * 1.5);
-
       // GenerateJavaAST can uncover semantic JSNI errors; report & abort
       checkForErrors(logger, goldenCuds, true);
 
@@ -329,7 +323,7 @@
       RecordRebinds.exec(jprogram, rebindRequests);
 
       return new UnifiedAst(options, new AST(jprogram, jsProgram),
-          singlePermutation, astMemoryUsage, rebindRequests);
+          singlePermutation, rebindRequests);
     } catch (Throwable e) {
       throw logAndTranslateException(logger, e);
     } finally {
@@ -565,14 +559,6 @@
     return null;
   }
 
-  private static long getUsedMemory() {
-    System.gc();
-    long used = Runtime.getRuntime().totalMemory()
-        - Runtime.getRuntime().freeMemory();
-    assert (used > 0);
-    return used;
-  }
-
   private static UnableToCompleteException logAndTranslateException(
       TreeLogger logger, Throwable e) {
     if (e instanceof UnableToCompleteException) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java b/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java
index a5e85b0..6dfc5fe 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java
@@ -57,10 +57,42 @@
     }
   }
 
-  /**
-   * Estimated AST memory usage.
-   */
-  private long astMemoryUsage;
+  private static AST deserializeAst(byte[] serializedAst) {
+    try {
+      PerfLogger.start("deserialize");
+      ByteArrayInputStream bais = new ByteArrayInputStream(serializedAst);
+      ObjectInputStream is;
+      is = new ObjectInputStream(bais);
+      JProgram jprogram = (JProgram) is.readObject();
+      JsProgram jsProgram = (JsProgram) is.readObject();
+      return new AST(jprogram, jsProgram);
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Should be impossible for memory based streams", e);
+    } catch (ClassNotFoundException e) {
+      throw new RuntimeException(
+          "Should be impossible when deserializing in process", e);
+    } finally {
+      PerfLogger.end();
+    }
+  }
+
+  private static byte[] serializeAst(AST ast) {
+    try {
+      PerfLogger.start("serialize");
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      ObjectOutputStream os = new ObjectOutputStream(baos);
+      os.writeObject(ast.getJProgram());
+      os.writeObject(ast.getJsProgram());
+      os.close();
+      return baos.toByteArray();
+    } catch (IOException e) {
+      throw new RuntimeException(
+          "Should be impossible for memory based streams", e);
+    } finally {
+      PerfLogger.end();
+    }
+  }
 
   /**
    * The original AST; nulled out once consumed (by the first call to
@@ -84,31 +116,17 @@
   private final SortedSet<String> rebindRequests;
 
   /**
-   * The serialized form of savedAst.
+   * The serialized AST.
    */
   private byte[] serializedAst;
 
-  /**
-   * If <code>true</code>, only one permutation will be run, so we don't need
-   * to serialize our AST (unless this whole object is about to be serialized).
-   */
-  private transient boolean singlePermutation;
-
   public UnifiedAst(JJSOptions options, AST initialAst,
-      boolean singlePermutation, long astMemoryUsage, Set<String> rebindRequests) {
+      boolean singlePermutation, Set<String> rebindRequests) {
     this.options = new JJSOptionsImpl(options);
     this.initialAst = initialAst;
-    this.singlePermutation = singlePermutation;
-    this.astMemoryUsage = astMemoryUsage;
     this.rebindRequests = Collections.unmodifiableSortedSet(new TreeSet<String>(
         rebindRequests));
-  }
-
-  /**
-   * Returns a rough estimate of how much memory an AST will take up.
-   */
-  public long getAstMemoryUsage() {
-    return astMemoryUsage;
+    this.serializedAst = singlePermutation ? null : serializeAst(initialAst);
   }
 
   /**
@@ -128,42 +146,19 @@
   AST getFreshAst() {
     synchronized (myLockObject) {
       if (initialAst != null) {
-        if (!singlePermutation && serializedAst == null) {
-          // Must preserve a serialized copy for future calls.
-          serializeAst();
-        }
         AST result = initialAst;
         initialAst = null;
         return result;
       } else {
         if (serializedAst == null) {
-          throw new IllegalStateException("No serialized AST was cached.");
+          throw new IllegalStateException(
+              "No serialized AST was cached and AST was already consumed.");
         }
-        return deserializeAst();
+        return deserializeAst(serializedAst);
       }
     }
   }
 
-  private AST deserializeAst() {
-    try {
-      PerfLogger.start("deserialize");
-      ByteArrayInputStream bais = new ByteArrayInputStream(serializedAst);
-      ObjectInputStream is;
-      is = new ObjectInputStream(bais);
-      JProgram jprogram = (JProgram) is.readObject();
-      JsProgram jsProgram = (JsProgram) is.readObject();
-      return new AST(jprogram, jsProgram);
-    } catch (IOException e) {
-      throw new RuntimeException(
-          "Should be impossible for memory based streams", e);
-    } catch (ClassNotFoundException e) {
-      throw new RuntimeException(
-          "Should be impossible when deserializing in process", e);
-    } finally {
-      PerfLogger.end();
-    }
-  }
-
   /**
    * Re-initialize lock object.
    */
@@ -172,35 +167,17 @@
     return this;
   }
 
-  private void serializeAst() {
-    try {
-      assert (initialAst != null);
-      assert (serializedAst == null);
-      PerfLogger.start("serialize");
-      ByteArrayOutputStream baos = new ByteArrayOutputStream();
-      ObjectOutputStream os = new ObjectOutputStream(baos);
-      os.writeObject(initialAst.getJProgram());
-      os.writeObject(initialAst.getJsProgram());
-      os.close();
-      serializedAst = baos.toByteArray();
-
-      // Very rough heuristic.
-      astMemoryUsage = Math.max(astMemoryUsage, serializedAst.length * 4);
-    } catch (IOException e) {
-      throw new RuntimeException(
-          "Should be impossible for memory based streams", e);
-    } finally {
-      PerfLogger.end();
-    }
-  }
-
   /**
    * Force byte serialization of AST before writing.
    */
   private Object writeReplace() {
-    synchronized (myLockObject) {
-      if (serializedAst == null) {
-        serializeAst();
+    if (serializedAst == null) {
+      synchronized (myLockObject) {
+        if (initialAst == null) {
+          throw new IllegalStateException(
+              "No serialized AST was cached and AST was already consumed.");
+        }
+        serializedAst = serializeAst(initialAst);
       }
     }
     return this;