Merge "Add some extra DCHECKS for debugging artQuickProxyInvokeHandler" into dalvik-dev
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index 8321ff6..d7a4136 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -151,10 +151,10 @@
   AN_BRANCH,
 
   // 2B PACKED_SWITCH vAA, +BBBBBBBB
-  AN_NONE,
+  AN_SWITCH,
 
   // 2C SPARSE_SWITCH vAA, +BBBBBBBB
-  AN_NONE,
+  AN_SWITCH,
 
   // 2D CMPL_FLOAT vAA, vBB, vCC
   AN_MATH | AN_FP | AN_SINGLE,
@@ -841,6 +841,7 @@
   int branch_ops;
   int heavyweight_ops;
   bool has_computational_loop;
+  bool has_switch;
   float math_ratio;
   float fp_ratio;
   float array_ratio;
@@ -914,6 +915,9 @@
       if ((flags & AN_HEAVYWEIGHT) != 0) {
         stats->heavyweight_ops += loop_scale_factor;
       }
+      if ((flags & AN_SWITCH) != 0) {
+        stats->has_switch = true;
+      }
     }
     if (tbb == ending_bb) {
       done = true;
@@ -939,7 +943,7 @@
               << stats->math_ratio << ", fp:"
               << stats->fp_ratio << ", br:"
               << stats->branch_ratio << ", hw:"
-              << stats-> heavyweight_ratio << ", arr:"
+              << stats->heavyweight_ratio << ", arr:"
               << stats->array_ratio << ", hot:"
               << stats->has_computational_loop << ", "
               << PrettyMethod(cu_->method_idx, *cu_->dex_file);
@@ -971,8 +975,14 @@
     return false;
   }
 
-  // If high proportion of expensive operations, skip.
-  if (stats->heavyweight_ratio > 0.3) {
+  // Switch operations benefit greatly from compilation, so go ahead and spend the cycles.
+  if (stats->has_switch) {
+    return false;
+  }
+
+  // If significant in size and high proportion of expensive operations, skip.
+  if ((GetNumDalvikInsns() > Runtime::Current()->GetSmallMethodThreshold()) &&
+      (stats->heavyweight_ratio > 0.3)) {
     return true;
   }
 
@@ -984,8 +994,7 @@
   * Ultimate goal is to drive with profile data.
   */
 bool MIRGraph::SkipCompilation(Runtime::CompilerFilter compiler_filter) {
-  if (compiler_filter == Runtime::kSpeed) {
-    // If going for speed, compile everything.
+  if (compiler_filter == Runtime::kEverything) {
     return false;
   }
 
@@ -994,10 +1003,38 @@
     return true;
   }
 
-  // Filter 1: Skip huge methods (generally machine-generated initialization methods).
+  // Set up compilation cutoffs based on current filter mode.
+  size_t small_cutoff = 0;
+  size_t default_cutoff = 0;
+  switch (compiler_filter) {
+    case Runtime::kBalanced:
+      small_cutoff = Runtime::Current()->GetSmallMethodThreshold();
+      default_cutoff = Runtime::Current()->GetLargeMethodThreshold();
+      break;
+    case Runtime::kSpace:
+      small_cutoff = Runtime::Current()->GetTinyMethodThreshold();
+      default_cutoff = Runtime::Current()->GetSmallMethodThreshold();
+      break;
+    case Runtime::kSpeed:
+      small_cutoff = Runtime::Current()->GetHugeMethodThreshold();
+      default_cutoff = Runtime::Current()->GetHugeMethodThreshold();
+      break;
+    default:
+      LOG(FATAL) << "Unexpected compiler_filter_: " << compiler_filter;
+  }
+
+  // If size < cutoff, assume we'll compile - but allow removal.
+  bool skip_compilation = (GetNumDalvikInsns() >= default_cutoff);
+
+  /*
+   * Filter 1: Huge methods are likely to be machine generated, but some aren't.
+   * If huge, assume we won't compile, but allow futher analysis to turn it back on.
+   */
   if (GetNumDalvikInsns() > Runtime::Current()->GetHugeMethodThreshold()) {
-    // Ain't nobody got time for that.
-    return true;
+    skip_compilation = true;
+  } else if (compiler_filter == Runtime::kSpeed) {
+    // If not huge, compile.
+    return false;
   }
 
   // Filter 2: Skip class initializers.
@@ -1010,28 +1047,7 @@
     return false;
   }
 
-  /* In balanced mode, we generally assume that we'll be compiling, and then detect
-   * methods that won't benefit and remove them.  In space or deferred mode, we do the
-   * opposite: assume no compilation and then add back presumed hot methods.
-   */
-  bool skip_compilation = (compiler_filter == Runtime::kBalanced) ? false : true;
-
-
-  // Filter 4: go ahead and compile the small ones.
-  size_t small_cutoff = 0;
-  switch (compiler_filter) {
-    case Runtime::kBalanced:
-      small_cutoff = Runtime::Current()->GetSmallMethodThreshold();
-      break;
-    case Runtime::kSpace:
-      small_cutoff = Runtime::Current()->GetTinyMethodThreshold();
-      break;
-    case Runtime::kDeferCompilation:
-      small_cutoff = 0;
-      break;
-    default:
-      LOG(FATAL) << "Unexpected compiler_filter_: " << compiler_filter;
-  }
+  // Filter 4: if small, just compile.
   if (GetNumDalvikInsns() < small_cutoff) {
     return false;
   }
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index af1ae44..c02deab 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -38,7 +38,8 @@
   kArrayOp,
   kHeavyweightOp,
   kSimpleConstOp,
-  kMoveOp
+  kMoveOp,
+  kSwitch
 };
 
 #define AN_NONE (1 << kUninterestingOp)
@@ -55,6 +56,7 @@
 #define AN_HEAVYWEIGHT (1 << kHeavyweightOp)
 #define AN_SIMPLECONST (1 << kSimpleConstOp)
 #define AN_MOVE (1 << kMoveOp)
+#define AN_SWITCH (1 << kSwitch)
 #define AN_COMPUTATIONAL (AN_MATH | AN_ARRAYOP | AN_MOVE | AN_SIMPLECONST)
 
 enum DataFlowAttributePos {
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index bf541c6..d265ed1 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -758,11 +758,6 @@
   }
 }
 
-void CompilerDriver::RecordClassStatus(ClassReference ref, CompiledClass* compiled_class) {
-  MutexLock mu(Thread::Current(), CompilerDriver::compiled_classes_lock_);
-  compiled_classes_.Put(ref, compiled_class);
-}
-
 bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file,
                                                       uint32_t type_idx) {
   if (IsImage() && IsImageClass(dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)))) {
@@ -1428,6 +1423,7 @@
 static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manager,
                                          size_t class_def_index)
     LOCKS_EXCLUDED(Locks::mutator_lock_) {
+  ATRACE_CALL();
   Thread* self = Thread::Current();
   jobject jclass_loader = manager->GetClassLoader();
   const DexFile& dex_file = *manager->GetDexFile();
@@ -2048,6 +2044,7 @@
 
 static void InitializeClass(const ParallelCompilationManager* manager, size_t class_def_index)
     LOCKS_EXCLUDED(Locks::mutator_lock_) {
+  ATRACE_CALL();
   const DexFile::ClassDef& class_def = manager->GetDexFile()->GetClassDef(class_def_index);
   ScopedObjectAccess soa(Thread::Current());
   mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader());
@@ -2056,48 +2053,54 @@
   if (klass != NULL) {
     // Only try to initialize classes that were successfully verified.
     if (klass->IsVerified()) {
-      // We don't want class initialization occurring on multiple threads due to deadlock problems.
-      // For example, a parent class is initialized (holding its lock) that refers to a sub-class
-      // in its static/class initializer causing it to try to acquire the sub-class' lock. While
-      // on a second thread the sub-class is initialized (holding its lock) after first initializing
-      // its parents, whose locks are acquired. This leads to a parent-to-child and a child-to-parent
-      // lock ordering and consequent potential deadlock.
-      // We need to use an ObjectLock due to potential suspension in the interpreting code. Rather
-      // than use a special Object for the purpose we use the Class of java.lang.Class.
-      ObjectLock lock(soa.Self(), klass->GetClass());
-      bool can_init_static_fields = manager->GetCompiler()->IsImage() &&
-          manager->GetCompiler()->IsImageClass(descriptor);
-      manager->GetClassLinker()->EnsureInitialized(klass, false, can_init_static_fields);
-      if (soa.Self()->IsExceptionPending()) {
-        soa.Self()->GetException(NULL)->Dump();
-      }
+      // Attempt to initialize the class but bail if we either need to initialize the super-class
+      // or static fields.
+      manager->GetClassLinker()->EnsureInitialized(klass, false, false);
       if (!klass->IsInitialized()) {
-        if (can_init_static_fields) {
-          // NoPreloadHolder inner class implies this should not be initialized early.
-          bool is_black_listed = StringPiece(descriptor).ends_with("$NoPreloadHolder;");
-          if (!is_black_listed) {
-            for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) {
-              if (StringPiece(descriptor) == class_initializer_black_list[i]) {
-                is_black_listed = true;
-                break;
+        // We don't want non-trivial class initialization occurring on multiple threads due to
+        // deadlock problems. For example, a parent class is initialized (holding its lock) that
+        // refers to a sub-class in its static/class initializer causing it to try to acquire the
+        // sub-class' lock. While on a second thread the sub-class is initialized (holding its lock)
+        // after first initializing its parents, whose locks are acquired. This leads to a
+        // parent-to-child and a child-to-parent lock ordering and consequent potential deadlock.
+        // We need to use an ObjectLock due to potential suspension in the interpreting code. Rather
+        // than use a special Object for the purpose we use the Class of java.lang.Class.
+        ObjectLock lock(soa.Self(), klass->GetClass());
+        // Attempt to initialize allowing initialization of parent classes but still not static
+        // fields.
+        manager->GetClassLinker()->EnsureInitialized(klass, false, true);
+        if (!klass->IsInitialized()) {
+          // We need to initialize static fields, we only do this for image classes that aren't
+          // black listed or marked with the $NoPreloadHolder.
+          bool can_init_static_fields = manager->GetCompiler()->IsImage() &&
+              manager->GetCompiler()->IsImageClass(descriptor);
+          if (can_init_static_fields) {
+            // NoPreloadHolder inner class implies this should not be initialized early.
+            bool is_black_listed = StringPiece(descriptor).ends_with("$NoPreloadHolder;");
+            if (!is_black_listed) {
+              for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) {
+                if (StringPiece(descriptor) == class_initializer_black_list[i]) {
+                  is_black_listed = true;
+                  break;
+                }
+              }
+            }
+            if (!is_black_listed) {
+              VLOG(compiler) << "Initializing: " << descriptor;
+              if (StringPiece(descriptor) == "Ljava/lang/Void;") {
+                // Hand initialize j.l.Void to avoid Dex file operations in un-started runtime.
+                ObjectLock lock(soa.Self(), klass);
+                mirror::ObjectArray<mirror::ArtField>* fields = klass->GetSFields();
+                CHECK_EQ(fields->GetLength(), 1);
+                fields->Get(0)->SetObj(klass, manager->GetClassLinker()->FindPrimitiveClass('V'));
+                klass->SetStatus(mirror::Class::kStatusInitialized);
+              } else {
+                manager->GetClassLinker()->EnsureInitialized(klass, true, true);
               }
             }
           }
-          if (!is_black_listed) {
-            VLOG(compiler) << "Initializing: " << descriptor;
-            if (StringPiece(descriptor) == "Ljava/lang/Void;") {
-              // Hand initialize j.l.Void to avoid Dex file operations in un-started runtime.
-              ObjectLock lock(soa.Self(), klass);
-              mirror::ObjectArray<mirror::ArtField>* fields = klass->GetSFields();
-              CHECK_EQ(fields->GetLength(), 1);
-              fields->Get(0)->SetObj(klass, manager->GetClassLinker()->FindPrimitiveClass('V'));
-              klass->SetStatus(mirror::Class::kStatusInitialized);
-            } else {
-              manager->GetClassLinker()->EnsureInitialized(klass, true, can_init_static_fields);
-            }
-            soa.Self()->AssertNoPendingException();
-          }
         }
+        soa.Self()->AssertNoPendingException();
       }
       // If successfully initialized place in SSB array.
       if (klass->IsInitialized()) {
@@ -2105,15 +2108,8 @@
       }
     }
     // Record the final class status if necessary.
-    mirror::Class::Status status = klass->GetStatus();
     ClassReference ref(manager->GetDexFile(), class_def_index);
-    CompiledClass* compiled_class = manager->GetCompiler()->GetCompiledClass(ref);
-    if (compiled_class == NULL) {
-      compiled_class = new CompiledClass(status);
-      manager->GetCompiler()->RecordClassStatus(ref, compiled_class);
-    } else {
-      DCHECK_GE(status, compiled_class->GetStatus()) << descriptor;
-    }
+    manager->GetCompiler()->RecordClassStatus(ref, klass->GetStatus());
   }
   // Clear any class not found or verification exceptions.
   soa.Self()->ClearException();
@@ -2288,7 +2284,7 @@
   Thread* self = Thread::Current();
   if (compiled_method != NULL) {
     MethodReference ref(&dex_file, method_idx);
-    CHECK(GetCompiledMethod(ref) == NULL) << PrettyMethod(method_idx, dex_file);
+    DCHECK(GetCompiledMethod(ref) == NULL) << PrettyMethod(method_idx, dex_file);
     {
       MutexLock mu(self, compiled_methods_lock_);
       compiled_methods_.Put(ref, compiled_method);
@@ -2313,6 +2309,32 @@
   return it->second;
 }
 
+void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) {
+  MutexLock mu(Thread::Current(), compiled_classes_lock_);
+  auto it = compiled_classes_.find(ref);
+  if (it == compiled_classes_.end() || it->second->GetStatus() != status) {
+    // An entry doesn't exist or the status is lower than the new status.
+    if (it != compiled_classes_.end()) {
+      CHECK_GT(status, it->second->GetStatus());
+      delete it->second;
+    }
+    switch (status) {
+      case mirror::Class::kStatusNotReady:
+      case mirror::Class::kStatusError:
+      case mirror::Class::kStatusRetryVerificationAtRuntime:
+      case mirror::Class::kStatusVerified:
+      case mirror::Class::kStatusInitialized:
+        break;  // Expected states.
+      default:
+        LOG(FATAL) << "Unexpected class status for class "
+            << PrettyDescriptor(ref.first->GetClassDescriptor(ref.first->GetClassDef(ref.second)))
+            << " of " << status;
+    }
+    CompiledClass* compiled_class = new CompiledClass(status);
+    compiled_classes_.Overwrite(ref, compiled_class);
+  }
+}
+
 CompiledMethod* CompilerDriver::GetCompiledMethod(MethodReference ref) const {
   MutexLock mu(Thread::Current(), compiled_methods_lock_);
   MethodTable::const_iterator it = compiled_methods_.find(ref);
@@ -2335,13 +2357,13 @@
 
 void CompilerDriver::AddRequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
                                              size_t class_def_index) {
-  MutexLock mu(self, freezing_constructor_lock_);
+  WriterMutexLock mu(self, freezing_constructor_lock_);
   freezing_constructor_classes_.insert(ClassReference(dex_file, class_def_index));
 }
 
 bool CompilerDriver::RequiresConstructorBarrier(Thread* self, const DexFile* dex_file,
                                           size_t class_def_index) {
-  MutexLock mu(self, freezing_constructor_lock_);
+  ReaderMutexLock mu(self, freezing_constructor_lock_);
   return freezing_constructor_classes_.count(ClassReference(dex_file, class_def_index)) != 0;
 }
 
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index bcde178..22a510b 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -296,7 +296,8 @@
   // Checks if class specified by type_idx is one of the image_classes_
   bool IsImageClass(const char* descriptor) const;
 
-  void RecordClassStatus(ClassReference ref, CompiledClass* compiled_class);
+  void RecordClassStatus(ClassReference ref, mirror::Class::Status status)
+      LOCKS_EXCLUDED(compiled_classes_lock_);
 
  private:
   // Compute constant code and method pointers when possible
@@ -362,7 +363,7 @@
   InstructionSet instruction_set_;
 
   // All class references that require
-  mutable Mutex freezing_constructor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  mutable ReaderWriterMutex freezing_constructor_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   std::set<ClassReference> freezing_constructor_classes_ GUARDED_BY(freezing_constructor_lock_);
 
   typedef SafeMap<const ClassReference, CompiledClass*> ClassTable;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 511788b..1d7f68d 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -867,9 +867,10 @@
   // give it away now and then switch to a more managable ScopedObjectAccess.
   Thread::Current()->TransitionFromRunnableToSuspended(kNative);
   // If we're doing the image, override the compiler filter to force full compilation. Must be
-  // done ahead of WellKnownClasses::Init that causes verification.
-  if (image && Runtime::Current()->GetCompilerFilter() == Runtime::kInterpretOnly) {
-    Runtime::Current()->SetCompilerFilter(Runtime::kSpeed);
+  // done ahead of WellKnownClasses::Init that causes verification.  Note: doesn't force
+  // compilation of class initializers.
+  if (image) {
+    Runtime::Current()->SetCompilerFilter(Runtime::kEverything);
   }
   // Whilst we're in native take the opportunity to initialize well known classes.
   WellKnownClasses::Init(Thread::Current()->GetJniEnv());
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 297a63d..cec06cf 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -51,8 +51,8 @@
 
 // Record Log contention information, dumpable via SIGQUIT.
 #ifdef ART_USE_FUTEXES
-// To enable lock contention logging, flip this to true.
-const bool kLogLockContentions = false;
+// To disable lock contention logging, flip this to false.
+const bool kLogLockContentions = true;
 #else
 // Keep this false as lock contention logging is supported only with
 // futex.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0110b36..f44e8c3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -575,9 +575,6 @@
     case Runtime::kInterpretOnly:
       oat_compiler_filter_string += "interpret-only";
       break;
-    case Runtime::kDeferCompilation:
-      oat_compiler_filter_string += "defer-compilation";
-      break;
     case Runtime::kSpace:
       oat_compiler_filter_string += "space";
       break;
@@ -587,6 +584,9 @@
     case Runtime::kSpeed:
       oat_compiler_filter_string += "speed";
       break;
+    case Runtime::kEverything:
+      oat_compiler_filter_string += "everything";
+      break;
     default:
       LOG(FATAL) << "Unexpected case.";
   }
@@ -782,8 +782,10 @@
   }
 
   ~ScopedFlock() {
-    int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN));
-    CHECK_EQ(0, flock_result);
+    if (file_.get() != NULL) {
+      int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN));
+      CHECK_EQ(0, flock_result);
+    }
   }
 
  private:
@@ -2284,18 +2286,18 @@
   }
 
   // Verify super class.
-  mirror::Class* super = klass->GetSuperClass();
-  if (super != NULL) {
+  SirtRef<mirror::Class> super(self, klass->GetSuperClass());
+  if (super.get() != NULL) {
     // Acquire lock to prevent races on verifying the super class.
-    ObjectLock lock(self, super);
+    ObjectLock lock(self, super.get());
 
     if (!super->IsVerified() && !super->IsErroneous()) {
-      Runtime::Current()->GetClassLinker()->VerifyClass(super);
+      VerifyClass(super.get());
     }
     if (!super->IsCompileTimeVerified()) {
       std::string error_msg(StringPrintf("Rejecting class %s that attempts to sub-class erroneous class %s",
                                          PrettyDescriptor(klass).c_str(),
-                                         PrettyDescriptor(super).c_str()));
+                                         PrettyDescriptor(super.get()).c_str()));
       LOG(ERROR) << error_msg  << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
       SirtRef<mirror::Throwable> cause(self, self->GetException(NULL));
       if (cause.get() != NULL) {
@@ -2339,7 +2341,14 @@
     // Make sure all classes referenced by catch blocks are resolved.
     ResolveClassExceptionHandlerTypes(dex_file, klass);
     if (verifier_failure == verifier::MethodVerifier::kNoFailure) {
-      klass->SetStatus(mirror::Class::kStatusVerified);
+      // Even though there were no verifier failures we need to respect whether the super-class
+      // was verified or requiring runtime reverification.
+      if (super.get() == NULL || super->IsVerified()) {
+        klass->SetStatus(mirror::Class::kStatusVerified);
+      } else {
+        CHECK_EQ(super->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
+        klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime);
+      }
     } else {
       CHECK_EQ(verifier_failure, verifier::MethodVerifier::kSoftFailure);
       // Soft failures at compile time should be retried at runtime. Soft
@@ -2695,31 +2704,85 @@
   CHECK_EQ(mh.GetReturnType(), mh2.GetReturnType());
 }
 
-bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_run_clinit, bool can_init_statics) {
-  CHECK(klass->IsResolved() || klass->IsErroneous())
-      << PrettyClass(klass) << ": state=" << klass->GetStatus();
+static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics,
+                                 bool can_init_parents)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (can_init_statics && can_init_statics) {
+    return true;
+  }
+  if (!can_init_statics) {
+    // Check if there's a class initializer.
+    mirror::ArtMethod* clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
+    if (clinit != NULL) {
+      return false;
+    }
+    // Check if there are encoded static values needing initialization.
+    if (klass->NumStaticFields() != 0) {
+      ClassHelper kh(klass);
+      const DexFile::ClassDef* dex_class_def = kh.GetClassDef();
+      DCHECK(dex_class_def != NULL);
+      if (dex_class_def->static_values_off_ != 0) {
+        return false;
+      }
+    }
+  }
+  if (!klass->IsInterface() && klass->HasSuperClass()) {
+    mirror::Class* super_class = klass->GetSuperClass();
+    if (!can_init_parents && !super_class->IsInitialized()) {
+      return false;
+    } else {
+      if (!CanWeInitializeClass(super_class, can_init_statics, true)) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics,
+                                  bool can_init_parents) {
+  // see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol
+
+  // Are we already initialized and therefore done?
+  // Note: we differ from the JLS here as we don't do this under the lock, this is benign as
+  // an initialized class will never change its state.
+  if (klass->IsInitialized()) {
+    return true;
+  }
+
+  // Fast fail if initialization requires a full runtime. Not part of the JLS.
+  if (!CanWeInitializeClass(klass, can_init_statics, can_init_parents)) {
+    return false;
+  }
 
   Thread* self = Thread::Current();
-
+  uint64_t t0;
   {
-    // see JLS 3rd edition, 12.4.2 "Detailed Initialization Procedure" for the locking protocol
     ObjectLock lock(self, klass);
 
-    if (klass->GetStatus() == mirror::Class::kStatusInitialized) {
+    // Re-check under the lock in case another thread initialized ahead of us.
+    if (klass->IsInitialized()) {
       return true;
     }
 
+    // Was the class already found to be erroneous? Done under the lock to match the JLS.
     if (klass->IsErroneous()) {
       ThrowEarlierClassFailure(klass);
       return false;
     }
 
-    if (klass->GetStatus() == mirror::Class::kStatusResolved ||
-        klass->GetStatus() == mirror::Class::kStatusRetryVerificationAtRuntime) {
+    CHECK(klass->IsResolved()) << PrettyClass(klass) << ": state=" << klass->GetStatus();
+
+    if (!klass->IsVerified()) {
       VerifyClass(klass);
-      if (klass->GetStatus() != mirror::Class::kStatusVerified) {
-        if (klass->GetStatus() == mirror::Class::kStatusError) {
+      if (!klass->IsVerified()) {
+        // We failed to verify, expect either the klass to be erroneous or verification failed at
+        // compile time.
+        if (klass->IsErroneous()) {
           CHECK(self->IsExceptionPending());
+        } else {
+          CHECK(Runtime::Current()->IsCompiler());
+          CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
         }
         return false;
       }
@@ -2742,38 +2805,65 @@
 
     if (!ValidateSuperClassDescriptors(klass)) {
       klass->SetStatus(mirror::Class::kStatusError);
-      lock.NotifyAll();
       return false;
     }
 
-    DCHECK_EQ(klass->GetStatus(), mirror::Class::kStatusVerified) << PrettyClass(klass);
+    CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusVerified) << PrettyClass(klass);
 
+    // From here out other threads may observe that we're initializing and so changes of state
+    // require the a notification.
     klass->SetClinitThreadId(self->GetTid());
     klass->SetStatus(mirror::Class::kStatusInitializing);
+
+    t0 = NanoTime();
   }
 
-  uint64_t t0 = NanoTime();
-
-  if (!InitializeSuperClass(klass, can_run_clinit, can_init_statics)) {
-    // Super class initialization failed, this can be because we can't run
-    // super-class class initializers in which case we'll be verified.
-    // Otherwise this class is erroneous.
-    if (!can_run_clinit) {
-      CHECK(klass->IsVerified());
-    } else {
-      CHECK(klass->IsErroneous());
+  // Initialize super classes, must be done will initializing for the JLS.
+  if (!klass->IsInterface() && klass->HasSuperClass()) {
+    mirror::Class* super_class = klass->GetSuperClass();
+    if (!super_class->IsInitialized()) {
+      CHECK(!super_class->IsInterface());
+      CHECK(can_init_parents);
+      bool super_initialized = InitializeClass(super_class, can_init_statics, true);
+      if (!super_initialized) {
+        // The super class was verified ahead of entering initializing, we should only be here if
+        // the super class became erroneous due to initialization.
+        CHECK(super_class->IsErroneous() && self->IsExceptionPending())
+            << "Super class initialization failed for " << PrettyDescriptor(super_class)
+            << " that has unexpected status " << super_class->GetStatus()
+            << "\nPending exception:\n"
+            << (self->GetException(NULL) != NULL ? self->GetException(NULL)->Dump() : "");
+        ObjectLock lock(self, klass);
+        // Initialization failed because the super-class is erroneous.
+        klass->SetStatus(mirror::Class::kStatusError);
+        lock.NotifyAll();
+        return false;
+      }
     }
-    // Signal to any waiting threads that saw this class as initializing.
-    ObjectLock lock(self, klass);
-    lock.NotifyAll();
-    return false;
   }
 
-  bool has_static_field_initializers = InitializeStaticFields(klass, can_init_statics);
+  if (klass->NumStaticFields() > 0) {
+    ClassHelper kh(klass);
+    const DexFile::ClassDef* dex_class_def = kh.GetClassDef();
+    CHECK(dex_class_def != NULL);
+    const DexFile& dex_file = kh.GetDexFile();
+    EncodedStaticFieldValueIterator it(dex_file, kh.GetDexCache(), klass->GetClassLoader(),
+                                       this, *dex_class_def);
+    if (it.HasNext()) {
+      CHECK(can_init_statics);
+      // We reordered the fields, so we need to be able to map the field indexes to the right fields.
+      SafeMap<uint32_t, mirror::ArtField*> field_map;
+      ConstructFieldMap(dex_file, *dex_class_def, klass, field_map);
+      for (size_t i = 0; it.HasNext(); i++, it.Next()) {
+        it.ReadValueToField(field_map.Get(i));
+      }
+    }
+  }
 
   mirror::ArtMethod* clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
-  if (clinit != NULL && can_run_clinit) {
-    if (Runtime::Current()->IsStarted()) {
+  if (clinit != NULL) {
+    CHECK(can_init_statics);
+    if (LIKELY(Runtime::Current()->IsStarted())) {
       JValue result;
       clinit->Invoke(self, NULL, 0, &result, 'V');
     } else {
@@ -2781,11 +2871,8 @@
     }
   }
 
-  // Opportunistically set static method trampolines to their destination. Unless initialization
-  // is being hindered at compile time.
-  if (can_init_statics || can_run_clinit || (!has_static_field_initializers && clinit == NULL)) {
-    FixupStaticTrampolines(klass);
-  }
+  // Opportunistically set static method trampolines to their destination.
+  FixupStaticTrampolines(klass);
 
   uint64_t t1 = NanoTime();
 
@@ -2805,14 +2892,8 @@
       global_stats->class_init_time_ns += (t1 - t0);
       thread_stats->class_init_time_ns += (t1 - t0);
       // Set the class as initialized except if failed to initialize static fields.
-      if ((!can_init_statics && has_static_field_initializers) ||
-          (!can_run_clinit && clinit != NULL)) {
-        klass->SetStatus(mirror::Class::kStatusVerified);
-        success = false;
-      } else {
-        klass->SetStatus(mirror::Class::kStatusInitialized);
-      }
-      if (success && VLOG_IS_ON(class_linker)) {
+      klass->SetStatus(mirror::Class::kStatusInitialized);
+      if (VLOG_IS_ON(class_linker)) {
         ClassHelper kh(klass);
         LOG(INFO) << "Initialized class " << kh.GetDescriptor() << " from " << kh.GetLocation();
       }
@@ -2826,6 +2907,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   while (true) {
     self->AssertNoPendingException();
+    CHECK(!klass->IsInitialized());
     lock.WaitIgnoringInterrupts();
 
     // When we wake up, repeat the test for init-in-progress.  If
@@ -2952,43 +3034,16 @@
   return found1 == found2;
 }
 
-bool ClassLinker::InitializeSuperClass(mirror::Class* klass, bool can_run_clinit, bool can_init_fields) {
-  CHECK(klass != NULL);
-  if (!klass->IsInterface() && klass->HasSuperClass()) {
-    mirror::Class* super_class = klass->GetSuperClass();
-    if (!super_class->IsInitialized()) {
-      CHECK(!super_class->IsInterface());
-      // Must hold lock on object when initializing and setting status.
-      Thread* self = Thread::Current();
-      ObjectLock lock(self, klass);
-      bool super_initialized = InitializeClass(super_class, can_run_clinit, can_init_fields);
-      // TODO: check for a pending exception
-      if (!super_initialized) {
-        if (!can_run_clinit) {
-          // Don't set status to error when we can't run <clinit>.
-          CHECK_EQ(klass->GetStatus(), mirror::Class::kStatusInitializing) << PrettyClass(klass);
-          klass->SetStatus(mirror::Class::kStatusVerified);
-          return false;
-        }
-        klass->SetStatus(mirror::Class::kStatusError);
-        klass->NotifyAll(self);
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
-bool ClassLinker::EnsureInitialized(mirror::Class* c, bool can_run_clinit, bool can_init_fields) {
+bool ClassLinker::EnsureInitialized(mirror::Class* c, bool can_init_fields, bool can_init_parents) {
   DCHECK(c != NULL);
   if (c->IsInitialized()) {
     return true;
   }
 
-  bool success = InitializeClass(c, can_run_clinit, can_init_fields);
+  bool success = InitializeClass(c, can_init_fields, can_init_parents);
   if (!success) {
     Thread* self = Thread::Current();
-    CHECK(self->IsExceptionPending() || !can_run_clinit) << PrettyClass(c);
+    CHECK(self->IsExceptionPending() || !can_init_fields || !can_init_parents) << PrettyClass(c);
   }
   return success;
 }
@@ -3003,38 +3058,6 @@
   }
 }
 
-bool ClassLinker::InitializeStaticFields(mirror::Class* klass, bool can_init_statics) {
-  size_t num_static_fields = klass->NumStaticFields();
-  if (num_static_fields == 0) {
-    return false;
-  }
-  mirror::DexCache* dex_cache = klass->GetDexCache();
-  // TODO: this seems like the wrong check. do we really want !IsPrimitive && !IsArray?
-  if (dex_cache == NULL) {
-    return false;
-  }
-  ClassHelper kh(klass);
-  const DexFile::ClassDef* dex_class_def = kh.GetClassDef();
-  CHECK(dex_class_def != NULL);
-  const DexFile& dex_file = kh.GetDexFile();
-  EncodedStaticFieldValueIterator it(dex_file, dex_cache, klass->GetClassLoader(),
-                                     this, *dex_class_def);
-
-  if (!it.HasNext()) {
-    return false;
-  } else {
-    if (can_init_statics) {
-      // We reordered the fields, so we need to be able to map the field indexes to the right fields.
-      SafeMap<uint32_t, mirror::ArtField*> field_map;
-      ConstructFieldMap(dex_file, *dex_class_def, klass, field_map);
-      for (size_t i = 0; it.HasNext(); i++, it.Next()) {
-        it.ReadValueToField(field_map.Get(i));
-      }
-    }
-    return true;
-  }
-}
-
 bool ClassLinker::LinkClass(SirtRef<mirror::Class>& klass,
                             mirror::ObjectArray<mirror::Class>* interfaces) {
   CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus());
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 624b7ce..5ef6d8f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -437,16 +437,11 @@
   void RegisterOatFileLocked(const OatFile& oat_file) EXCLUSIVE_LOCKS_REQUIRED(dex_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(dex_lock_);
 
-  bool InitializeClass(mirror::Class* klass, bool can_run_clinit, bool can_init_statics)
+  bool InitializeClass(mirror::Class* klass, bool can_run_clinit, bool can_init_parents)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   bool WaitForInitializeClass(mirror::Class* klass, Thread* self, ObjectLock& lock);
   bool ValidateSuperClassDescriptors(const mirror::Class* klass)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  bool InitializeSuperClass(mirror::Class* klass, bool can_run_clinit, bool can_init_fields)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  // Initialize static fields, returns true if fields were initialized.
-  bool InitializeStaticFields(mirror::Class* klass, bool can_init_statics)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool IsSameDescriptorInDifferentClassContexts(const char* descriptor,
                                                 const mirror::Class* klass1,
diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc
index e48208d..2e6b0a8 100644
--- a/runtime/exception_test.cc
+++ b/runtime/exception_test.cc
@@ -41,7 +41,7 @@
                                       soa.Decode<mirror::ClassLoader*>(LoadDex("ExceptionHandle")));
     my_klass_ = class_linker_->FindClass("LExceptionHandle;", class_loader.get());
     ASSERT_TRUE(my_klass_ != NULL);
-    class_linker_->EnsureInitialized(my_klass_, false, true);
+    class_linker_->EnsureInitialized(my_klass_, true, true);
 
     dex_ = my_klass_->GetDexCache()->GetDexFile();
 
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index a854971..cedea61 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -742,11 +742,23 @@
   }
 };
 
+size_t MarkSweep::GetThreadCount(bool paused) const {
+  if (heap_->GetThreadPool() == nullptr || !heap_->CareAboutPauseTimes()) {
+    return 0;
+  }
+  if (paused) {
+    return heap_->GetParallelGCThreadCount() + 1;
+  } else {
+    return heap_->GetConcGCThreadCount() + 1;
+  }
+}
+
 void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) {
   accounting::CardTable* card_table = GetHeap()->GetCardTable();
   ThreadPool* thread_pool = GetHeap()->GetThreadPool();
-  const bool parallel = kParallelCardScan && thread_pool != nullptr;
-  if (parallel) {
+  size_t thread_count = GetThreadCount(paused);
+  // The parallel version with only one thread is faster for card scanning, TODO: fix.
+  if (kParallelCardScan && thread_count > 0) {
     Thread* self = Thread::Current();
     // Can't have a different split for each space since multiple spaces can have their cards being
     // scanned at the same time.
@@ -755,7 +767,6 @@
     const Object** mark_stack_begin = const_cast<const Object**>(mark_stack_->Begin());
     const Object** mark_stack_end = const_cast<const Object**>(mark_stack_->End());
     const size_t mark_stack_size = mark_stack_end - mark_stack_begin;
-    const size_t thread_count = thread_pool->GetThreadCount() + 1;
     // Estimated number of work tasks we will create.
     const size_t mark_stack_tasks = GetHeap()->GetContinuousSpaces().size() * thread_count;
     DCHECK_NE(mark_stack_tasks, 0U);
@@ -788,8 +799,9 @@
         card_begin += card_increment;
       }
     }
+    thread_pool->SetMaxActiveWorkers(thread_count - 1);
     thread_pool->StartWorkers(self);
-    thread_pool->Wait(self, paused, true);  // Only do work in the main thread if we are paused.
+    thread_pool->Wait(self, true, true);
     thread_pool->StopWorkers(self);
     timings_.EndSplit();
   } else {
@@ -885,7 +897,8 @@
     ScanObjectVisitor scan_visitor(this);
     auto* self = Thread::Current();
     ThreadPool* thread_pool = heap_->GetThreadPool();
-    const bool parallel = kParallelRecursiveMark && thread_pool != NULL;
+    size_t thread_count = GetThreadCount(false);
+    const bool parallel = kParallelRecursiveMark && thread_count > 1;
     mark_stack_->Reset();
     for (const auto& space : GetHeap()->GetContinuousSpaces()) {
       if ((space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) ||
@@ -904,7 +917,7 @@
           atomic_finger_ = static_cast<int32_t>(0xFFFFFFFF);
 
           // Create a few worker tasks.
-          size_t n = (thread_pool->GetThreadCount() + 1) * 2;
+          const size_t n = thread_count * 2;
           while (begin != end) {
             uintptr_t start = begin;
             uintptr_t delta = (end - begin) / n;
@@ -915,8 +928,9 @@
                                                begin);
             thread_pool->AddTask(self, task);
           }
+          thread_pool->SetMaxActiveWorkers(thread_count - 1);
           thread_pool->StartWorkers(self);
-          thread_pool->Wait(self, false, true);
+          thread_pool->Wait(self, true, true);
           thread_pool->StopWorkers(self);
         } else {
           // This function does not handle heap end increasing, so we must use the space end.
@@ -949,7 +963,7 @@
 
 void MarkSweep::SweepJniWeakGlobals(IsMarkedTester is_marked, void* arg) {
   JavaVMExt* vm = Runtime::Current()->GetJavaVM();
-  MutexLock mu(Thread::Current(), vm->weak_globals_lock);
+  WriterMutexLock mu(Thread::Current(), vm->weak_globals_lock);
   for (const Object** entry : vm->weak_globals) {
     if (!is_marked(*entry, arg)) {
       *entry = kClearedJniWeakGlobal;
@@ -1032,7 +1046,7 @@
   runtime->GetMonitorList()->SweepMonitorList(VerifyIsLiveCallback, this);
 
   JavaVMExt* vm = runtime->GetJavaVM();
-  MutexLock mu(Thread::Current(), vm->weak_globals_lock);
+  ReaderMutexLock mu(Thread::Current(), vm->weak_globals_lock);
   for (const Object** entry : vm->weak_globals) {
     VerifyIsLive(*entry);
   }
@@ -1369,13 +1383,11 @@
   ScanObjectVisit(obj, visitor);
 }
 
-void MarkSweep::ProcessMarkStackParallel(bool paused) {
+void MarkSweep::ProcessMarkStackParallel(size_t thread_count) {
   Thread* self = Thread::Current();
   ThreadPool* thread_pool = GetHeap()->GetThreadPool();
-  const size_t num_threads = thread_pool->GetThreadCount();
-  const size_t chunk_size =
-      std::min(mark_stack_->Size() / num_threads + 1,
-               static_cast<size_t>(MarkStackTask<false>::kMaxSize));
+  const size_t chunk_size = std::min(mark_stack_->Size() / thread_count + 1,
+                                     static_cast<size_t>(MarkStackTask<false>::kMaxSize));
   CHECK_GT(chunk_size, 0U);
   // Split the current mark stack up into work tasks.
   for (mirror::Object **it = mark_stack_->Begin(), **end = mark_stack_->End(); it < end; ) {
@@ -1384,10 +1396,9 @@
                                                         const_cast<const mirror::Object**>(it)));
     it += delta;
   }
+  thread_pool->SetMaxActiveWorkers(thread_count - 1);
   thread_pool->StartWorkers(self);
-  // Don't do work in the main thread since it assumed at least one other thread will require CPU
-  // time during the GC.
-  thread_pool->Wait(self, paused, true);
+  thread_pool->Wait(self, true, true);
   thread_pool->StopWorkers(self);
   mark_stack_->Reset();
   CHECK_EQ(work_chunks_created_, work_chunks_deleted_) << " some of the work chunks were leaked";
@@ -1396,10 +1407,10 @@
 // Scan anything that's on the mark stack.
 void MarkSweep::ProcessMarkStack(bool paused) {
   timings_.StartSplit("ProcessMarkStack");
-  const bool parallel = kParallelProcessMarkStack && GetHeap()->GetThreadPool() &&
-      mark_stack_->Size() >= kMinimumParallelMarkStackSize;
-  if (parallel) {
-    ProcessMarkStackParallel(paused);
+  size_t thread_count = GetThreadCount(paused);
+  if (kParallelProcessMarkStack && thread_count > 1 &&
+      mark_stack_->Size() >= kMinimumParallelMarkStackSize) {
+    ProcessMarkStackParallel(thread_count);
   } else {
     // TODO: Tune this.
     static const size_t kFifoSize = 4;
@@ -1610,8 +1621,8 @@
   total_time_ns_ += GetDurationNs();
   total_paused_time_ns_ += std::accumulate(GetPauseTimes().begin(), GetPauseTimes().end(), 0,
                                            std::plus<uint64_t>());
-  total_freed_objects_ += GetFreedObjects();
-  total_freed_bytes_ += GetFreedBytes();
+  total_freed_objects_ += GetFreedObjects() + GetFreedLargeObjects();
+  total_freed_bytes_ += GetFreedBytes() + GetFreedLargeObjectBytes();
 
   // Ensure that the mark stack is empty.
   CHECK(mark_stack_->IsEmpty());
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 8430839..dbec3e9 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -308,6 +308,10 @@
   // Expand mark stack to 2x its current size. Thread safe.
   void ExpandMarkStack();
 
+  // Returns how many threads we should use for the current GC phase based on if we are paused,
+  // whether or not we care about pauses.
+  size_t GetThreadCount(bool paused) const;
+
   // Returns true if an object is inside of the immune region (assumed to be marked).
   bool IsImmune(const mirror::Object* obj) const {
     return obj >= immune_begin_ && obj < immune_end_;
@@ -367,7 +371,7 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void ProcessMarkStackParallel(bool paused)
+  void ProcessMarkStackParallel(size_t thread_count)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 800159a..e20c2c5 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -58,10 +58,6 @@
 namespace art {
 namespace gc {
 
-// When to create a log message about a slow GC, 100ms.
-static constexpr uint64_t kSlowGcThreshold = MsToNs(100);
-// When to create a log message about a long pause, 5ms.
-static constexpr uint64_t kLongGcPauseThreshold = MsToNs(5);
 static constexpr bool kGCALotMode = false;
 static constexpr size_t kGcAlotInterval = KB;
 static constexpr bool kDumpGcPerformanceOnShutdown = false;
@@ -72,12 +68,18 @@
 
 Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free,
            double target_utilization, size_t capacity, const std::string& original_image_file_name,
-           bool concurrent_gc, size_t num_gc_threads, bool low_memory_mode)
+           bool concurrent_gc, size_t parallel_gc_threads, size_t conc_gc_threads,
+           bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold,
+           bool ignore_max_footprint)
     : alloc_space_(NULL),
       card_table_(NULL),
       concurrent_gc_(concurrent_gc),
-      num_gc_threads_(num_gc_threads),
+      parallel_gc_threads_(parallel_gc_threads),
+      conc_gc_threads_(conc_gc_threads),
       low_memory_mode_(low_memory_mode),
+      long_pause_log_threshold_(long_pause_log_threshold),
+      long_gc_log_threshold_(long_gc_log_threshold),
+      ignore_max_footprint_(ignore_max_footprint),
       have_zygote_space_(false),
       soft_ref_queue_lock_(NULL),
       weak_ref_queue_lock_(NULL),
@@ -230,6 +232,11 @@
   last_gc_time_ns_ = NanoTime();
   last_gc_size_ = GetBytesAllocated();
 
+  if (ignore_max_footprint_) {
+    SetIdealFootprint(std::numeric_limits<size_t>::max());
+    concurrent_start_bytes_ = max_allowed_footprint_;
+  }
+
   // Create our garbage collectors.
   for (size_t i = 0; i < 2; ++i) {
     const bool concurrent = i != 0;
@@ -245,13 +252,14 @@
 }
 
 void Heap::CreateThreadPool() {
-  if (num_gc_threads_ != 0) {
-    thread_pool_.reset(new ThreadPool(num_gc_threads_));
+  const size_t num_threads = std::max(parallel_gc_threads_, conc_gc_threads_);
+  if (num_threads != 0) {
+    thread_pool_.reset(new ThreadPool(num_threads));
   }
 }
 
 void Heap::DeleteThreadPool() {
-  thread_pool_.reset(NULL);
+  thread_pool_.reset(nullptr);
 }
 
 static bool ReadStaticInt(JNIEnvExt* env, jclass clz, const char* name, int* out_value) {
@@ -1249,11 +1257,11 @@
     const size_t duration = collector->GetDurationNs();
     std::vector<uint64_t> pauses = collector->GetPauseTimes();
     // GC for alloc pauses the allocating thread, so consider it as a pause.
-    bool was_slow = duration > kSlowGcThreshold ||
-            (gc_cause == kGcCauseForAlloc && duration > kLongGcPauseThreshold);
+    bool was_slow = duration > long_gc_log_threshold_ ||
+            (gc_cause == kGcCauseForAlloc && duration > long_pause_log_threshold_);
     if (!was_slow) {
       for (uint64_t pause : pauses) {
-        was_slow = was_slow || pause > kLongGcPauseThreshold;
+        was_slow = was_slow || pause > long_pause_log_threshold_;
       }
     }
 
@@ -1702,7 +1710,7 @@
         wait_time = NanoTime() - wait_start;
         total_wait_time_ += wait_time;
       }
-      if (wait_time > kLongGcPauseThreshold) {
+      if (wait_time > long_pause_log_threshold_) {
         LOG(INFO) << "WaitForConcurrentGcToComplete blocked for " << PrettyDuration(wait_time);
       }
     }
@@ -1776,28 +1784,32 @@
       target_size = std::max(bytes_allocated, max_allowed_footprint_);
     }
   }
-  SetIdealFootprint(target_size);
 
-  // Calculate when to perform the next ConcurrentGC.
-  if (concurrent_gc_) {
-    // Calculate the estimated GC duration.
-    double gc_duration_seconds = NsToMs(gc_duration) / 1000.0;
-    // Estimate how many remaining bytes we will have when we need to start the next GC.
-    size_t remaining_bytes = allocation_rate_ * gc_duration_seconds;
-    remaining_bytes = std::max(remaining_bytes, kMinConcurrentRemainingBytes);
-    if (UNLIKELY(remaining_bytes > max_allowed_footprint_)) {
-      // A never going to happen situation that from the estimated allocation rate we will exceed
-      // the applications entire footprint with the given estimated allocation rate. Schedule
-      // another GC straight away.
-      concurrent_start_bytes_ = bytes_allocated;
-    } else {
-      // Start a concurrent GC when we get close to the estimated remaining bytes. When the
-      // allocation rate is very high, remaining_bytes could tell us that we should start a GC
-      // right away.
-      concurrent_start_bytes_ = std::max(max_allowed_footprint_ - remaining_bytes, bytes_allocated);
+  if (!ignore_max_footprint_) {
+    SetIdealFootprint(target_size);
+
+    if (concurrent_gc_) {
+      // Calculate when to perform the next ConcurrentGC.
+
+      // Calculate the estimated GC duration.
+      double gc_duration_seconds = NsToMs(gc_duration) / 1000.0;
+      // Estimate how many remaining bytes we will have when we need to start the next GC.
+      size_t remaining_bytes = allocation_rate_ * gc_duration_seconds;
+      remaining_bytes = std::max(remaining_bytes, kMinConcurrentRemainingBytes);
+      if (UNLIKELY(remaining_bytes > max_allowed_footprint_)) {
+        // A never going to happen situation that from the estimated allocation rate we will exceed
+        // the applications entire footprint with the given estimated allocation rate. Schedule
+        // another GC straight away.
+        concurrent_start_bytes_ = bytes_allocated;
+      } else {
+        // Start a concurrent GC when we get close to the estimated remaining bytes. When the
+        // allocation rate is very high, remaining_bytes could tell us that we should start a GC
+        // right away.
+        concurrent_start_bytes_ = std::max(max_allowed_footprint_ - remaining_bytes, bytes_allocated);
+      }
+      DCHECK_LE(concurrent_start_bytes_, max_allowed_footprint_);
+      DCHECK_LE(max_allowed_footprint_, growth_limit_);
     }
-    DCHECK_LE(concurrent_start_bytes_, max_allowed_footprint_);
-    DCHECK_LE(max_allowed_footprint_, growth_limit_);
   }
 
   UpdateMaxNativeFootprint();
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index cda252e..c93dacb 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -107,6 +107,8 @@
   static constexpr size_t kDefaultMaximumSize = 32 * MB;
   static constexpr size_t kDefaultMaxFree = 2 * MB;
   static constexpr size_t kDefaultMinFree = kDefaultMaxFree / 4;
+  static constexpr size_t kDefaultLongPauseLogThreshold = MsToNs(5);
+  static constexpr size_t kDefaultLongGCLogThreshold = MsToNs(100);
 
   // Default target utilization.
   static constexpr double kDefaultTargetUtilization = 0.5;
@@ -120,7 +122,8 @@
   explicit Heap(size_t initial_size, size_t growth_limit, size_t min_free,
                 size_t max_free, double target_utilization, size_t capacity,
                 const std::string& original_image_file_name, bool concurrent_gc,
-                size_t num_gc_threads, bool low_memory_mode);
+                size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode,
+                size_t long_pause_threshold, size_t long_gc_threshold, bool ignore_max_footprint);
 
   ~Heap();
 
@@ -401,12 +404,23 @@
   // GC performance measuring
   void DumpGcPerformanceInfo(std::ostream& os);
 
+  // Returns true if we currently care about pause times.
+  bool CareAboutPauseTimes() const {
+    return care_about_pause_times_;
+  }
+
   // Thread pool.
   void CreateThreadPool();
   void DeleteThreadPool();
   ThreadPool* GetThreadPool() {
     return thread_pool_.get();
   }
+  size_t GetParallelGCThreadCount() const {
+    return parallel_gc_threads_;
+  }
+  size_t GetConcGCThreadCount() const {
+    return conc_gc_threads_;
+  }
 
  private:
   // Allocates uninitialized storage. Passing in a null space tries to place the object in the
@@ -514,12 +528,26 @@
   // false for stop-the-world mark sweep.
   const bool concurrent_gc_;
 
-  // How many GC threads we may use for garbage collection.
-  const size_t num_gc_threads_;
+  // How many GC threads we may use for paused parts of garbage collection.
+  const size_t parallel_gc_threads_;
+
+  // How many GC threads we may use for unpaused parts of garbage collection.
+  const size_t conc_gc_threads_;
 
   // Boolean for if we are in low memory mode.
   const bool low_memory_mode_;
 
+  // If we get a pause longer than long pause log threshold, then we print out the GC after it
+  // finishes.
+  const size_t long_pause_log_threshold_;
+
+  // If we get a GC longer than long GC log threshold, then we print out the GC after it finishes.
+  const size_t long_gc_log_threshold_;
+
+  // If we ignore the max footprint it lets the heap grow until it hits the heap capacity, this is
+  // useful for benchmarking since it reduces time spent in GC to a low %.
+  const bool ignore_max_footprint_;
+
   // If we have a zygote space.
   bool have_zygote_space_;
 
@@ -544,14 +572,18 @@
 
   // Maximum size that the heap can reach.
   const size_t capacity_;
+
   // The size the heap is limited to. This is initially smaller than capacity, but for largeHeap
   // programs it is "cleared" making it the same as capacity.
   size_t growth_limit_;
+
   // When the number of bytes allocated exceeds the footprint TryAllocate returns NULL indicating
   // a GC should be triggered.
   size_t max_allowed_footprint_;
+
   // The watermark at which a concurrent GC is requested by registerNativeAllocation.
   size_t native_footprint_gc_watermark_;
+
   // The watermark at which a GC is performed inside of registerNativeAllocation.
   size_t native_footprint_limit_;
 
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 55c0765..ceed866 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -91,7 +91,7 @@
   }
   JavaVMExt* vm = soa.Vm();
   IndirectReferenceTable& weak_globals = vm->weak_globals;
-  MutexLock mu(soa.Self(), vm->weak_globals_lock);
+  WriterMutexLock mu(soa.Self(), vm->weak_globals_lock);
   IndirectRef ref = weak_globals.Add(IRT_FIRST_SEGMENT, obj);
   return reinterpret_cast<jweak>(ref);
 }
@@ -611,6 +611,11 @@
     JniAbortF(#fn, #value " == null"); \
   }
 
+#define CHECK_NON_NULL_MEMCPY_ARGUMENT(fn, length, value) \
+  if (UNLIKELY(length != 0 && value == NULL)) { \
+    JniAbortF(#fn, #value " == null"); \
+  }
+
 class JNI {
  public:
   static jint GetVersion(JNIEnv*) {
@@ -823,7 +828,7 @@
     JavaVMExt* vm = soa.Vm();
     IndirectReferenceTable& globals = vm->globals;
     Object* decoded_obj = soa.Decode<Object*>(obj);
-    MutexLock mu(soa.Self(), vm->globals_lock);
+    WriterMutexLock mu(soa.Self(), vm->globals_lock);
     IndirectRef ref = globals.Add(IRT_FIRST_SEGMENT, decoded_obj);
     return reinterpret_cast<jobject>(ref);
   }
@@ -835,7 +840,7 @@
     JavaVMExt* vm = reinterpret_cast<JNIEnvExt*>(env)->vm;
     IndirectReferenceTable& globals = vm->globals;
     Thread* self = reinterpret_cast<JNIEnvExt*>(env)->self;
-    MutexLock mu(self, vm->globals_lock);
+    WriterMutexLock mu(self, vm->globals_lock);
 
     if (!globals.Remove(IRT_FIRST_SEGMENT, obj)) {
       LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
@@ -855,7 +860,7 @@
     ScopedObjectAccess soa(env);
     JavaVMExt* vm = soa.Vm();
     IndirectReferenceTable& weak_globals = vm->weak_globals;
-    MutexLock mu(soa.Self(), vm->weak_globals_lock);
+    WriterMutexLock mu(soa.Self(), vm->weak_globals_lock);
 
     if (!weak_globals.Remove(IRT_FIRST_SEGMENT, obj)) {
       LOG(WARNING) << "JNI WARNING: DeleteWeakGlobalRef(" << obj << ") "
@@ -1968,7 +1973,7 @@
     if (start < 0 || length < 0 || start + length > s->GetLength()) {
       ThrowSIOOBE(soa, start, length, s->GetLength());
     } else {
-      CHECK_NON_NULL_ARGUMENT(GetStringRegion, buf);
+      CHECK_NON_NULL_MEMCPY_ARGUMENT(GetStringRegion, length, buf);
       const jchar* chars = s->GetCharArray()->GetData() + s->GetOffset();
       memcpy(buf, chars + start, length * sizeof(jchar));
     }
@@ -1982,7 +1987,7 @@
     if (start < 0 || length < 0 || start + length > s->GetLength()) {
       ThrowSIOOBE(soa, start, length, s->GetLength());
     } else {
-      CHECK_NON_NULL_ARGUMENT(GetStringUTFRegion, buf);
+      CHECK_NON_NULL_MEMCPY_ARGUMENT(GetStringUTFRegion, length, buf);
       const jchar* chars = s->GetCharArray()->GetData() + s->GetOffset();
       ConvertUtf16ToModifiedUtf8(buf, chars + start, length);
     }
@@ -2333,7 +2338,7 @@
   static jint RegisterNativeMethods(JNIEnv* env, jclass java_class, const JNINativeMethod* methods,
                                     jint method_count, bool return_errors) {
     if (UNLIKELY(method_count < 0)) {
-      JniAbortF("RegisterNatives", "method_cound == %d", method_count);
+      JniAbortF("RegisterNatives", "negative method count: %d", method_count);
       return JNI_ERR;  // Not reached.
     }
     CHECK_NON_NULL_ARGUMENT(RegisterNatives, java_class);
@@ -2512,7 +2517,7 @@
   static jint EnsureLocalCapacity(JNIEnv* env, jint desired_capacity,
                                   const char* caller) {
     // TODO: we should try to expand the table if necessary.
-    if (desired_capacity < 1 || desired_capacity > static_cast<jint>(kLocalsMax)) {
+    if (desired_capacity < 0 || desired_capacity > static_cast<jint>(kLocalsMax)) {
       LOG(ERROR) << "Invalid capacity given to " << caller << ": " << desired_capacity;
       return JNI_ERR;
     }
@@ -2566,7 +2571,7 @@
     if (start < 0 || length < 0 || start + length > array->GetLength()) {
       ThrowAIOOBE(soa, array, start, length, "src");
     } else {
-      CHECK_NON_NULL_ARGUMENT(GetPrimitiveArrayRegion, buf);
+      CHECK_NON_NULL_MEMCPY_ARGUMENT(GetStringRegion, length, buf);
       JavaT* data = array->GetData();
       memcpy(buf, data + start, length * sizeof(JavaT));
     }
@@ -2581,7 +2586,7 @@
     if (start < 0 || length < 0 || start + length > array->GetLength()) {
       ThrowAIOOBE(soa, array, start, length, "dst");
     } else {
-      CHECK_NON_NULL_ARGUMENT(SetPrimitiveArrayRegion, buf);
+      CHECK_NON_NULL_MEMCPY_ARGUMENT(GetStringRegion, length, buf);
       JavaT* data = array->GetData();
       memcpy(data + start, buf, length * sizeof(JavaT));
     }
@@ -3019,11 +3024,11 @@
     os << "; pins=" << pin_table.Size();
   }
   {
-    MutexLock mu(self, globals_lock);
+    ReaderMutexLock mu(self, globals_lock);
     os << "; globals=" << globals.Capacity();
   }
   {
-    MutexLock mu(self, weak_globals_lock);
+    ReaderMutexLock mu(self, weak_globals_lock);
     if (weak_globals.Capacity() > 0) {
       os << " (plus " << weak_globals.Capacity() << " weak)";
     }
@@ -3039,11 +3044,11 @@
 void JavaVMExt::DumpReferenceTables(std::ostream& os) {
   Thread* self = Thread::Current();
   {
-    MutexLock mu(self, globals_lock);
+    ReaderMutexLock mu(self, globals_lock);
     globals.Dump(os);
   }
   {
-    MutexLock mu(self, weak_globals_lock);
+    ReaderMutexLock mu(self, weak_globals_lock);
     weak_globals.Dump(os);
   }
   {
@@ -3191,7 +3196,7 @@
       return NULL;
     }
   } else {
-    CHECK(c->GetStatus() >= Class::kStatusInitializing) << c->GetStatus() << " " << PrettyMethod(m);
+    CHECK(c->IsInitializing()) << c->GetStatus() << " " << PrettyMethod(m);
   }
 
   std::string detail;
@@ -3212,7 +3217,7 @@
 void JavaVMExt::VisitRoots(RootVisitor* visitor, void* arg) {
   Thread* self = Thread::Current();
   {
-    MutexLock mu(self, globals_lock);
+    ReaderMutexLock mu(self, globals_lock);
     globals.VisitRoots(visitor, arg);
   }
   {
diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h
index f7caa0f..bad3841 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni_internal.h
@@ -112,11 +112,11 @@
   ReferenceTable pin_table GUARDED_BY(pins_lock);
 
   // JNI global references.
-  Mutex globals_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
+  ReaderWriterMutex globals_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
   IndirectReferenceTable globals GUARDED_BY(globals_lock);
 
   // JNI weak global references.
-  Mutex weak_globals_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
+  ReaderWriterMutex weak_globals_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
   IndirectReferenceTable weak_globals GUARDED_BY(weak_globals_lock);
 
   Mutex libraries_lock DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 234e40a..aea2ed3 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -1488,6 +1488,21 @@
   env_->DeleteLocalRef(o);
 }
 
+TEST_F(JniInternalTest, PushLocalFrame_10395422) {
+  // The JNI specification is ambiguous about whether the given capacity is to be interpreted as a
+  // maximum or as a minimum, but it seems like it's supposed to be a minimum, and that's how
+  // Android historically treated it, and it's how the RI treats it. It's also the more useful
+  // interpretation!
+  ASSERT_EQ(JNI_OK, env_->PushLocalFrame(0));
+  env_->PopLocalFrame(NULL);
+
+  // Negative capacities are not allowed.
+  ASSERT_EQ(JNI_ERR, env_->PushLocalFrame(-1));
+
+  // And it's okay to have an upper limit. Ours is currently 512.
+  ASSERT_EQ(JNI_ERR, env_->PushLocalFrame(8192));
+}
+
 TEST_F(JniInternalTest, PushLocalFrame_PopLocalFrame) {
   jobject original = env_->NewStringUTF("");
   ASSERT_TRUE(original != NULL);
@@ -1497,11 +1512,11 @@
   ScopedObjectAccess soa(env_);
   mirror::Object* inner2_direct_pointer;
   {
-    env_->PushLocalFrame(4);
+    ASSERT_EQ(JNI_OK, env_->PushLocalFrame(4));
     outer = env_->NewLocalRef(original);
 
     {
-      env_->PushLocalFrame(4);
+      ASSERT_EQ(JNI_OK, env_->PushLocalFrame(4));
       inner1 = env_->NewLocalRef(outer);
       inner2 = env_->NewStringUTF("survivor");
       inner2_direct_pointer = soa.Decode<mirror::Object*>(inner2);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 29025f2..19e134f 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -51,14 +51,20 @@
 }
 
 void Class::SetStatus(Status new_status) {
-  CHECK(new_status > GetStatus() || new_status == kStatusError || !Runtime::Current()->IsStarted())
-      << PrettyClass(this) << " " << GetStatus() << " -> " << new_status;
-  CHECK(sizeof(Status) == sizeof(uint32_t)) << PrettyClass(this);
+  if (UNLIKELY(new_status <= GetStatus() && new_status != kStatusError)) {
+    bool class_linker_initialized = Runtime::Current()->GetClassLinker() != nullptr;
+    if (class_linker_initialized) {
+      LOG(FATAL) << "Unexpected change back of class status for " << PrettyClass(this) << " "
+          << GetStatus() << " -> " << new_status;
+    }
+  }
   if (new_status > kStatusResolved) {
-    CHECK_EQ(GetThinLockId(), Thread::Current()->GetThinLockId()) << PrettyClass(this);
+    CHECK_EQ(GetThinLockId(), Thread::Current()->GetThinLockId())
+        << "Attempt to change status of class while not holding its lock " << PrettyClass(this);
   }
   if (new_status == kStatusError) {
-    CHECK_NE(GetStatus(), kStatusError) << PrettyClass(this);
+    CHECK_NE(GetStatus(), kStatusError)
+        << "Attempt to set as erroneous an already erroneous class " << PrettyClass(this);
 
     // Stash current exception.
     Thread* self = Thread::Current();
@@ -96,6 +102,7 @@
 
     self->SetException(gc_safe_throw_location, old_exception.get());
   }
+  CHECK(sizeof(Status) == sizeof(uint32_t)) << PrettyClass(this);
   return SetField32(OFFSET_OF_OBJECT_MEMBER(Class, status_), new_status, false);
 }
 
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index c4a9503..51a67c1 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -339,7 +339,9 @@
   parsed->heap_target_utilization_ = gc::Heap::kDefaultTargetUtilization;
   parsed->heap_growth_limit_ = 0;  // 0 means no growth limit.
   // Default to number of processors minus one since the main GC thread also does work.
-  parsed->heap_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1;
+  parsed->parallel_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1;
+  // Only the main GC thread, no workers.
+  parsed->conc_gc_threads_ = 0;
   parsed->stack_size_ = 0;  // 0 means default.
   parsed->low_memory_mode_ = false;
 
@@ -349,6 +351,10 @@
   parsed->is_concurrent_gc_enabled_ = true;
   parsed->is_explicit_gc_disabled_ = false;
 
+  parsed->long_pause_log_threshold_ = gc::Heap::kDefaultLongPauseLogThreshold;
+  parsed->long_gc_log_threshold_ = gc::Heap::kDefaultLongGCLogThreshold;
+  parsed->ignore_max_footprint_ = false;
+
   parsed->lock_profiling_threshold_ = 0;
   parsed->hook_is_sensitive_thread_ = NULL;
 
@@ -480,9 +486,12 @@
         return NULL;
       }
       parsed->heap_target_utilization_ = value;
-    } else if (StartsWith(option, "-XX:HeapGCThreads=")) {
-      parsed->heap_gc_threads_ =
-          ParseMemoryOption(option.substr(strlen("-XX:HeapGCThreads=")).c_str(), 1024);
+    } else if (StartsWith(option, "-XX:ParallelGCThreads=")) {
+      parsed->parallel_gc_threads_ =
+          ParseMemoryOption(option.substr(strlen("-XX:ParallelGCThreads=")).c_str(), 1024);
+    } else if (StartsWith(option, "-XX:ConcGCThreads=")) {
+      parsed->conc_gc_threads_ =
+          ParseMemoryOption(option.substr(strlen("-XX:ConcGCThreads=")).c_str(), 1024);
     } else if (StartsWith(option, "-Xss")) {
       size_t size = ParseMemoryOption(option.substr(strlen("-Xss")).c_str(), 1);
       if (size == 0) {
@@ -494,6 +503,14 @@
         return NULL;
       }
       parsed->stack_size_ = size;
+    } else if (option == "-XX:LongPauseLogThreshold") {
+      parsed->long_pause_log_threshold_ =
+          ParseMemoryOption(option.substr(strlen("-XX:LongPauseLogThreshold=")).c_str(), 1024);
+    } else if (option == "-XX:LongGCLogThreshold") {
+          parsed->long_gc_log_threshold_ =
+              ParseMemoryOption(option.substr(strlen("-XX:LongGCLogThreshold")).c_str(), 1024);
+    } else if (option == "-XX:IgnoreMaxFootprint") {
+      parsed->ignore_max_footprint_ = true;
     } else if (option == "-XX:LowMemoryMode") {
       parsed->low_memory_mode_ = true;
     } else if (StartsWith(option, "-D")) {
@@ -583,14 +600,14 @@
       Trace::SetDefaultClockSource(kProfilerClockSourceDual);
     } else if (option == "-compiler-filter:interpret-only") {
       parsed->compiler_filter_ = kInterpretOnly;
-    } else if (option == "-compiler-filter:defer-compilation") {
-      parsed->compiler_filter_ = kDeferCompilation;
     } else if (option == "-compiler-filter:space") {
       parsed->compiler_filter_ = kSpace;
     } else if (option == "-compiler-filter:balanced") {
       parsed->compiler_filter_ = kBalanced;
     } else if (option == "-compiler-filter:speed") {
       parsed->compiler_filter_ = kSpeed;
+    } else if (option == "-compiler-filter:everything") {
+      parsed->compiler_filter_ = kEverything;
     } else if (option == "-sea_ir") {
       parsed->sea_ir_mode_ = true;
     } else if (StartsWith(option, "-huge-method-max:")) {
@@ -865,8 +882,12 @@
                        options->heap_maximum_size_,
                        options->image_,
                        options->is_concurrent_gc_enabled_,
-                       options->heap_gc_threads_,
-                       options->low_memory_mode_);
+                       options->parallel_gc_threads_,
+                       options->conc_gc_threads_,
+                       options->low_memory_mode_,
+                       options->long_pause_log_threshold_,
+                       options->long_gc_log_threshold_,
+                       options->ignore_max_footprint_);
 
   BlockSignals();
   InitPlatformSignalHandlers();
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 8aba762..50108ac 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -65,10 +65,10 @@
 
   enum CompilerFilter {
     kInterpretOnly,       // Compile nothing.
-    kDeferCompilation,    // Temporary minimal compilation, will redo during device idle time.
     kSpace,               // Maximize space savings.
     kBalanced,            // Try to get the best performance return on compilation investment.
-    kSpeed                // Compile all methods.
+    kSpeed,               // Maximize runtime performance.
+    kEverything           // Force compilation (Note: excludes compilaton of class initializers).
   };
 
   // Guide heuristics to determine whether to compile method if profile data not available.
@@ -77,10 +77,10 @@
 #else
   static const CompilerFilter kDefaultCompilerFilter = kSpeed;
 #endif
-  static const size_t kDefaultHugeMethodThreshold = 6000;
-  static const size_t kDefaultLargeMethodThreshold = 1000;
-  static const size_t kDefaultSmallMethodThreshold = 200;
-  static const size_t kDefaultTinyMethodThreshold = 10;
+  static const size_t kDefaultHugeMethodThreshold = 10000;
+  static const size_t kDefaultLargeMethodThreshold = 600;
+  static const size_t kDefaultSmallMethodThreshold = 60;
+  static const size_t kDefaultTinyMethodThreshold = 20;
   static const size_t kDefaultNumDexMethodsThreshold = 900;
 
   class ParsedOptions {
@@ -100,13 +100,17 @@
     bool interpreter_only_;
     bool is_concurrent_gc_enabled_;
     bool is_explicit_gc_disabled_;
+    size_t long_pause_log_threshold_;
+    size_t long_gc_log_threshold_;
+    bool ignore_max_footprint_;
     size_t heap_initial_size_;
     size_t heap_maximum_size_;
     size_t heap_growth_limit_;
-    size_t heap_gc_threads_;
     size_t heap_min_free_;
     size_t heap_max_free_;
     double heap_target_utilization_;
+    size_t parallel_gc_threads_;
+    size_t conc_gc_threads_;
     size_t stack_size_;
     bool low_memory_mode_;
     size_t lock_profiling_threshold_;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 505e368..3178bf1 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1184,13 +1184,13 @@
   } else if (kind == kGlobal) {
     JavaVMExt* vm = Runtime::Current()->GetJavaVM();
     IndirectReferenceTable& globals = vm->globals;
-    MutexLock mu(const_cast<Thread*>(this), vm->globals_lock);
+    ReaderMutexLock mu(const_cast<Thread*>(this), vm->globals_lock);
     result = const_cast<mirror::Object*>(globals.Get(ref));
   } else {
     DCHECK_EQ(kind, kWeakGlobal);
     JavaVMExt* vm = Runtime::Current()->GetJavaVM();
     IndirectReferenceTable& weak_globals = vm->weak_globals;
-    MutexLock mu(const_cast<Thread*>(this), vm->weak_globals_lock);
+    ReaderMutexLock mu(const_cast<Thread*>(this), vm->weak_globals_lock);
     result = const_cast<mirror::Object*>(weak_globals.Get(ref));
     if (result == kClearedJniWeakGlobal) {
       // This is a special case where it's okay to return NULL.
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 39d30bb2..674ab9d 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -81,7 +81,8 @@
     start_time_(0),
     total_wait_time_(0),
     // Add one since the caller of constructor waits on the barrier too.
-    creation_barier_(num_threads + 1) {
+    creation_barier_(num_threads + 1),
+    max_active_workers_(num_threads) {
   Thread* self = Thread::Current();
   while (GetThreadCount() < num_threads) {
     const std::string name = StringPrintf("Thread pool worker %zu", GetThreadCount());
@@ -91,6 +92,12 @@
   creation_barier_.Wait(self);
 }
 
+void ThreadPool::SetMaxActiveWorkers(size_t threads) {
+  MutexLock mu(Thread::Current(), task_queue_lock_);
+  CHECK_LE(threads, GetThreadCount());
+  max_active_workers_ = threads;
+}
+
 ThreadPool::~ThreadPool() {
   {
     Thread* self = Thread::Current();
@@ -121,12 +128,18 @@
 Task* ThreadPool::GetTask(Thread* self) {
   MutexLock mu(self, task_queue_lock_);
   while (!IsShuttingDown()) {
-    Task* task = TryGetTaskLocked(self);
-    if (task != NULL) {
-      return task;
+    const size_t thread_count = GetThreadCount();
+    // Ensure that we don't use more threads than the maximum active workers.
+    const size_t active_threads = thread_count - waiting_count_;
+    // <= since self is considered an active worker.
+    if (active_threads <= max_active_workers_) {
+      Task* task = TryGetTaskLocked(self);
+      if (task != NULL) {
+        return task;
+      }
     }
 
-    waiting_count_++;
+    ++waiting_count_;
     if (waiting_count_ == GetThreadCount() && tasks_.empty()) {
       // We may be done, lets broadcast to the completion condition.
       completion_condition_.Broadcast(self);
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index 9c6d47b..b9a97a1 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -90,6 +90,10 @@
     return total_wait_time_;
   }
 
+  // Provides a way to bound the maximum number of worker threads, threads must be less the the
+  // thread count of the thread pool.
+  void SetMaxActiveWorkers(size_t threads);
+
  protected:
   // Get a task to run, blocks if there are no tasks left
   virtual Task* GetTask(Thread* self);
@@ -117,6 +121,7 @@
   uint64_t start_time_ GUARDED_BY(task_queue_lock_);
   uint64_t total_wait_time_;
   Barrier creation_barier_;
+  size_t max_active_workers_ GUARDED_BY(task_queue_lock_);
 
  private:
   friend class ThreadPoolWorker;
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 4d2f36f..34a0f73 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4202,7 +4202,7 @@
 ReaderWriterMutex* MethodVerifier::devirt_maps_lock_ = NULL;
 MethodVerifier::DevirtualizationMapTable* MethodVerifier::devirt_maps_ = NULL;
 
-Mutex* MethodVerifier::rejected_classes_lock_ = NULL;
+ReaderWriterMutex* MethodVerifier::rejected_classes_lock_ = NULL;
 MethodVerifier::RejectedClassesTable* MethodVerifier::rejected_classes_ = NULL;
 
 void MethodVerifier::Init() {
@@ -4227,9 +4227,9 @@
       devirt_maps_ = new MethodVerifier::DevirtualizationMapTable();
     }
 
-    rejected_classes_lock_ = new Mutex("verifier rejected classes lock");
+    rejected_classes_lock_ = new ReaderWriterMutex("verifier rejected classes lock");
     {
-      MutexLock mu(self, *rejected_classes_lock_);
+      WriterMutexLock mu(self, *rejected_classes_lock_);
       rejected_classes_ = new MethodVerifier::RejectedClassesTable;
     }
   }
@@ -4267,7 +4267,7 @@
     devirt_maps_lock_ = NULL;
 
     {
-      MutexLock mu(self, *rejected_classes_lock_);
+      WriterMutexLock mu(self, *rejected_classes_lock_);
       delete rejected_classes_;
       rejected_classes_ = NULL;
     }
@@ -4280,7 +4280,7 @@
 void MethodVerifier::AddRejectedClass(ClassReference ref) {
   DCHECK(Runtime::Current()->IsCompiler());
   {
-    MutexLock mu(Thread::Current(), *rejected_classes_lock_);
+    WriterMutexLock mu(Thread::Current(), *rejected_classes_lock_);
     rejected_classes_->insert(ref);
   }
   CHECK(IsClassRejected(ref));
@@ -4288,7 +4288,7 @@
 
 bool MethodVerifier::IsClassRejected(ClassReference ref) {
   DCHECK(Runtime::Current()->IsCompiler());
-  MutexLock mu(Thread::Current(), *rejected_classes_lock_);
+  ReaderMutexLock mu(Thread::Current(), *rejected_classes_lock_);
   return (rejected_classes_->find(ref) != rejected_classes_->end());
 }
 
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index d6bebc6..70442fb 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -661,7 +661,7 @@
                            const PcToConcreteMethodMap* pc_method_map)
         LOCKS_EXCLUDED(devirt_maps_lock_);
   typedef std::set<ClassReference> RejectedClassesTable;
-  static Mutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+  static ReaderWriterMutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
   static RejectedClassesTable* rejected_classes_ GUARDED_BY(rejected_classes_lock_);
 
   static void AddRejectedClass(ClassReference ref)
diff --git a/test/run-test b/test/run-test
index 4744f19..11dcfc5 100755
--- a/test/run-test
+++ b/test/run-test
@@ -268,6 +268,8 @@
             echo "BUILD FAILED For ${TEST_NAME}"
         fi
     fi
+    # Clean up extraneous files that are not used by tests.
+    find $tmp_dir -mindepth 1  ! -regex ".*/\(.*jar\|$build_output\|$expected\)" | xargs rm -rf
     exit 0
 else
     "./${build}" >"$build_output" 2>&1