Merge "Update art/tests/089-many-methods."
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 8da1520..d1f7f3e 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -181,9 +181,11 @@
   cu->mir_graph->InlineMethod(code_item, access_flags, invoke_type, class_def_idx, method_idx,
                               class_loader, dex_file);
 
+#if !defined(ART_USE_PORTABLE_COMPILER)
   if (cu->mir_graph->SkipCompilation(Runtime::Current()->GetCompilerFilter())) {
     return NULL;
   }
+#endif
 
   /* Do a code layout pass */
   cu->mir_graph->CodeLayout();
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 7b83e40..c079e52 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -582,10 +582,11 @@
 
 bool CompilerDriver::IsImageClass(const char* descriptor) const {
   DCHECK(descriptor != NULL);
-  if (image_classes_.get() == NULL) {
+  if (!IsImage()) {
     return true;
+  } else {
+    return image_classes_->find(descriptor) != image_classes_->end();
   }
-  return image_classes_->find(descriptor) != image_classes_->end();
 }
 
 static void ResolveExceptionsForMethod(MethodHelper* mh,
@@ -655,7 +656,7 @@
 // Make a list of descriptors for classes to include in the image
 void CompilerDriver::LoadImageClasses(base::TimingLogger& timings)
       LOCKS_EXCLUDED(Locks::mutator_lock_) {
-  if (image_classes_.get() == NULL) {
+  if (!IsImage()) {
     return;
   }
 
@@ -669,7 +670,7 @@
     SirtRef<mirror::Class> klass(self, class_linker->FindSystemClass(descriptor.c_str()));
     if (klass.get() == NULL) {
       image_classes_->erase(it++);
-      LOG(WARNING) << "Failed to find class " << descriptor;
+      VLOG(compiler) << "Failed to find class " << descriptor;
       Thread::Current()->ClearException();
     } else {
       ++it;
@@ -742,26 +743,19 @@
 }
 
 void CompilerDriver::UpdateImageClasses(base::TimingLogger& timings) {
-  if (image_classes_.get() == NULL) {
-    return;
+  if (IsImage()) {
+    timings.NewSplit("UpdateImageClasses");
+
+    // Update image_classes_ with classes for objects created by <clinit> methods.
+    Thread* self = Thread::Current();
+    const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
+    gc::Heap* heap = Runtime::Current()->GetHeap();
+    // TODO: Image spaces only?
+    WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+    heap->FlushAllocStack();
+    heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this);
+    self->EndAssertNoThreadSuspension(old_cause);
   }
-
-  timings.NewSplit("UpdateImageClasses");
-
-  // Update image_classes_ with classes for objects created by <clinit> methods.
-  Thread* self = Thread::Current();
-  const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter");
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  // TODO: Image spaces only?
-  WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
-  heap->FlushAllocStack();
-  heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this);
-  self->EndAssertNoThreadSuspension(old_cause);
-}
-
-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,
@@ -1417,30 +1411,28 @@
 // classes found in the boot classpath. Since at runtime we will
 // select the class from the boot classpath, do not attempt to resolve
 // or compile it now.
-static bool SkipClass(mirror::ClassLoader* class_loader,
-                      const DexFile& dex_file,
-                      const DexFile::ClassDef& class_def)
-    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static bool SkipClass(ClassLinker* class_linker, jobject class_loader, const DexFile& dex_file,
+                      const DexFile::ClassDef& class_def) {
   if (class_loader == NULL) {
     return false;
   }
   const char* descriptor = dex_file.GetClassDescriptor(class_def);
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  mirror::Class* klass = class_linker->FindClass(descriptor, NULL);
-  if (klass == NULL) {
-    Thread* self = Thread::Current();
-    CHECK(self->IsExceptionPending());
-    self->ClearException();
-    return false;
-  }
-  return true;
+  return class_linker->IsInBootClassPath(descriptor);
 }
 
-static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manager, size_t class_def_index)
+static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manager,
+                                         size_t class_def_index)
     LOCKS_EXCLUDED(Locks::mutator_lock_) {
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader());
+  ATRACE_CALL();
+  Thread* self = Thread::Current();
+  jobject jclass_loader = manager->GetClassLoader();
   const DexFile& dex_file = *manager->GetDexFile();
+  ClassLinker* class_linker = manager->GetClassLinker();
+
+  // If an instance field is final then we need to have a barrier on the return, static final
+  // fields are assigned within the lock held for class initialization. Conservatively assume
+  // constructor barriers are always required.
+  bool requires_constructor_barrier = true;
 
   // Method and Field are the worst. We can't resolve without either
   // context from the code use (to disambiguate virtual vs direct
@@ -1450,72 +1442,89 @@
   // definitions, since many of them many never be referenced by
   // generated code.
   const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
-  if (SkipClass(class_loader, dex_file, class_def)) {
-    return;
-  }
+  if (!SkipClass(class_linker, jclass_loader, dex_file, class_def)) {
+    // Note the class_data pointer advances through the headers,
+    // static fields, instance fields, direct methods, and virtual
+    // methods.
+    const byte* class_data = dex_file.GetClassData(class_def);
+    if (class_data == NULL) {
+      // Empty class such as a marker interface.
+      requires_constructor_barrier = false;
+    } else {
+      ScopedObjectAccess soa(self);
+      mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
+      mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file);
 
-  // Note the class_data pointer advances through the headers,
-  // static fields, instance fields, direct methods, and virtual
-  // methods.
-  const byte* class_data = dex_file.GetClassData(class_def);
-  if (class_data == NULL) {
-    // empty class such as a marker interface
-    return;
-  }
-  Thread* self = Thread::Current();
-  ClassLinker* class_linker = manager->GetClassLinker();
-  mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file);
-  ClassDataItemIterator it(dex_file, class_data);
-  while (it.HasNextStaticField()) {
-    mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
-                                                      class_loader, true);
-    if (field == NULL) {
-      CHECK(self->IsExceptionPending());
-      self->ClearException();
-    }
-    it.Next();
-  }
-  // If an instance field is final then we need to have a barrier on the return, static final
-  // fields are assigned within the lock held for class initialization.
-  bool requires_constructor_barrier = false;
-  while (it.HasNextInstanceField()) {
-    if ((it.GetMemberAccessFlags() & kAccFinal) != 0) {
-      requires_constructor_barrier = true;
-    }
+      // Resolve the class.
+      mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
+                                                       class_loader);
 
-    mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache,
-                                                      class_loader, false);
-    if (field == NULL) {
-      CHECK(self->IsExceptionPending());
-      self->ClearException();
+      bool resolve_fields_and_methods;
+      if (klass == NULL) {
+        // Class couldn't be resolved, for example, super-class is in a different dex file. Don't
+        // attempt to resolve methods and fields when there is no declaring class.
+        CHECK(soa.Self()->IsExceptionPending());
+        Thread::Current()->ClearException();
+        resolve_fields_and_methods = false;
+      } else {
+        resolve_fields_and_methods = manager->GetCompiler()->IsImage();
+      }
+      ClassDataItemIterator it(dex_file, class_data);
+      while (it.HasNextStaticField()) {
+        if (resolve_fields_and_methods) {
+          mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+                                                               dex_cache, class_loader, true);
+          if (field == NULL) {
+            CHECK(soa.Self()->IsExceptionPending());
+            soa.Self()->ClearException();
+          }
+        }
+        it.Next();
+      }
+      // We require a constructor barrier if there are final instance fields.
+      requires_constructor_barrier = false;
+      while (it.HasNextInstanceField()) {
+        if ((it.GetMemberAccessFlags() & kAccFinal) != 0) {
+          requires_constructor_barrier = true;
+        }
+        if (resolve_fields_and_methods) {
+          mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+                                                               dex_cache, class_loader, false);
+          if (field == NULL) {
+            CHECK(soa.Self()->IsExceptionPending());
+            soa.Self()->ClearException();
+          }
+        }
+        it.Next();
+      }
+      if (resolve_fields_and_methods) {
+        while (it.HasNextDirectMethod()) {
+          mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
+                                                                  dex_cache, class_loader, NULL,
+                                                                  it.GetMethodInvokeType(class_def));
+          if (method == NULL) {
+            CHECK(soa.Self()->IsExceptionPending());
+            soa.Self()->ClearException();
+          }
+          it.Next();
+        }
+        while (it.HasNextVirtualMethod()) {
+          mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
+                                                                  dex_cache, class_loader, NULL,
+                                                                  it.GetMethodInvokeType(class_def));
+          if (method == NULL) {
+            CHECK(soa.Self()->IsExceptionPending());
+            soa.Self()->ClearException();
+          }
+          it.Next();
+        }
+        DCHECK(!it.HasNext());
+      }
     }
-    it.Next();
   }
   if (requires_constructor_barrier) {
-    manager->GetCompiler()->AddRequiresConstructorBarrier(soa.Self(), manager->GetDexFile(),
-                                                          class_def_index);
+    manager->GetCompiler()->AddRequiresConstructorBarrier(self, &dex_file, class_def_index);
   }
-  while (it.HasNextDirectMethod()) {
-    mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
-                                                                 dex_cache, class_loader, NULL,
-                                                                 it.GetMethodInvokeType(class_def));
-    if (method == NULL) {
-      CHECK(self->IsExceptionPending());
-      self->ClearException();
-    }
-    it.Next();
-  }
-  while (it.HasNextVirtualMethod()) {
-    mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
-                                                                 dex_cache, class_loader, NULL,
-                                                                 it.GetMethodInvokeType(class_def));
-    if (method == NULL) {
-      CHECK(self->IsExceptionPending());
-      self->ClearException();
-    }
-    it.Next();
-  }
-  DCHECK(!it.HasNext());
 }
 
 static void ResolveType(const ParallelCompilationManager* manager, size_t type_idx)
@@ -1541,10 +1550,16 @@
   // TODO: we could resolve strings here, although the string table is largely filled with class
   //       and method names.
 
-  timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " Types").c_str()));
   ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool);
-  context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_);
+  if (IsImage()) {
+    // For images we resolve all types, such as array, whereas for applications just those with
+    // classdefs are resolved by ResolveClassFieldsAndMethods.
+    // TODO: strdup memory leak.
+    timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " Types").c_str()));
+    context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_);
+  }
 
+  // TODO: strdup memory leak.
   timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " MethodsAndFields").c_str()));
   context.ForAll(0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods, thread_count_);
 }
@@ -1567,7 +1582,8 @@
   mirror::Class* klass =
       manager->GetClassLinker()->FindClass(descriptor,
                                            soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader()));
-  if (klass == NULL) {    CHECK(soa.Self()->IsExceptionPending());
+  if (klass == NULL) {
+    CHECK(soa.Self()->IsExceptionPending());
     soa.Self()->ClearException();
 
     /*
@@ -1587,25 +1603,25 @@
                  << PrettyDescriptor(manager->GetDexFile()->GetClassDescriptor(class_def))
                  << " because: " << error_msg;
     }
-    return;
+  } else {
+    CHECK(klass->IsResolved()) << PrettyClass(klass);
+    manager->GetClassLinker()->VerifyClass(klass);
+
+    if (klass->IsErroneous()) {
+      // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
+      CHECK(soa.Self()->IsExceptionPending());
+      soa.Self()->ClearException();
+    }
+
+    CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous())
+        << PrettyDescriptor(klass) << ": state=" << klass->GetStatus();
   }
-  CHECK(klass->IsResolved()) << PrettyClass(klass);
-  manager->GetClassLinker()->VerifyClass(klass);
-
-  if (klass->IsErroneous()) {
-    // ClassLinker::VerifyClass throws, which isn't useful in the compiler.
-    CHECK(soa.Self()->IsExceptionPending());
-    soa.Self()->ClearException();
-  }
-
-
-  CHECK(klass->IsCompileTimeVerified() || klass->IsErroneous())
-      << PrettyDescriptor(klass) << ": state=" << klass->GetStatus();
   soa.Self()->AssertNoPendingException();
 }
 
 void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file,
                                    ThreadPool& thread_pool, base::TimingLogger& timings) {
+  // TODO: strdup memory leak.
   timings.NewSplit(strdup(("Verify " + dex_file.GetLocation()).c_str()));
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool);
@@ -1734,6 +1750,7 @@
   "Landroid/webkit/JniUtil;",  // Calls System.loadLibrary.
   "Landroid/webkit/PluginManager;",  // // Calls OsConstants.initConstants.
   "Landroid/webkit/WebViewCore;",  // Calls System.loadLibrary.
+  "Landroid/webkit/WebViewFactory;",  // Calls -..-> android.os.SystemProperties.native_get.
   "Landroid/webkit/WebViewFactory$Preloader;",  // Calls to Class.forName.
   "Landroid/webkit/WebViewInputDispatcher;",  // Calls Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl.
   "Landroid/webkit/URLUtil;",  // Calls Calls regex.Pattern.compile -..-> regex.Pattern.compileImpl.
@@ -2028,58 +2045,63 @@
 
 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());
   const char* descriptor = manager->GetDexFile()->GetClassDescriptor(class_def);
   mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader);
-  bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1;
-  bool can_init_static_fields = compiling_boot &&
-      manager->GetCompiler()->IsImageClass(descriptor);
   if (klass != NULL) {
-    // 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 lock1(soa.Self(), klass->GetClass());
-    // The lock required to initialize the class.
-    ObjectLock lock2(soa.Self(), klass);
     // Only try to initialize classes that were successfully verified.
     if (klass->IsVerified()) {
-      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.
-              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()) {
@@ -2087,15 +2109,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();
@@ -2103,11 +2118,15 @@
 
 void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
                                        ThreadPool& thread_pool, base::TimingLogger& timings) {
+  // TODO: strdup memory leak.
   timings.NewSplit(strdup(("InitializeNoClinit " + dex_file.GetLocation()).c_str()));
 #ifndef NDEBUG
-  for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) {
-    const char* descriptor = class_initializer_black_list[i];
-    CHECK(IsValidDescriptor(descriptor)) << descriptor;
+  // Sanity check blacklist descriptors.
+  if (IsImage()) {
+    for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) {
+      const char* descriptor = class_initializer_black_list[i];
+      CHECK(IsValidDescriptor(descriptor)) << descriptor;
+    }
   }
 #endif
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -2139,12 +2158,9 @@
   jobject jclass_loader = manager->GetClassLoader();
   const DexFile& dex_file = *manager->GetDexFile();
   const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
-  {
-    ScopedObjectAccess soa(Thread::Current());
-    mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
-    if (SkipClass(class_loader, dex_file, class_def)) {
-      return;
-    }
+  ClassLinker* class_linker = manager->GetClassLinker();
+  if (SkipClass(class_linker, jclass_loader, dex_file, class_def)) {
+    return;
   }
   ClassReference ref(&dex_file, class_def_index);
   // Skip compiling classes with generic verifier failures since they will still fail at runtime
@@ -2172,6 +2188,7 @@
   while (it.HasNextInstanceField()) {
     it.Next();
   }
+  CompilerDriver* driver = manager->GetCompiler();
   // Compile direct methods
   int64_t previous_direct_method_idx = -1;
   while (it.HasNextDirectMethod()) {
@@ -2183,9 +2200,9 @@
       continue;
     }
     previous_direct_method_idx = method_idx;
-    manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
-                                          it.GetMethodInvokeType(class_def), class_def_index,
-                                          method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level);
+    driver->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
+                          it.GetMethodInvokeType(class_def), class_def_index,
+                          method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level);
     it.Next();
   }
   // Compile virtual methods
@@ -2199,9 +2216,9 @@
       continue;
     }
     previous_virtual_method_idx = method_idx;
-    manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
-                                          it.GetMethodInvokeType(class_def), class_def_index,
-                                          method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level);
+    driver->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
+                          it.GetMethodInvokeType(class_def), class_def_index,
+                          method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level);
     it.Next();
   }
   DCHECK(!it.HasNext());
@@ -2209,8 +2226,10 @@
 
 void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file,
                                     ThreadPool& thread_pool, base::TimingLogger& timings) {
+  // TODO: strdup memory leak.
   timings.NewSplit(strdup(("Compile " + dex_file.GetLocation()).c_str()));
-  ParallelCompilationManager context(NULL, class_loader, this, &dex_file, thread_pool);
+  ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
+                                     &dex_file, thread_pool);
   context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_);
 }
 
@@ -2266,7 +2285,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);
@@ -2291,6 +2310,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);
@@ -2313,13 +2358,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 21a44ea..22a510b 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -110,6 +110,7 @@
     return compiler_backend_;
   }
 
+  // Are we compiling and creating an image file?
   bool IsImage() const {
     return image_;
   }
@@ -295,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
@@ -361,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 ceb6bf6..1d7f68d 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -137,6 +137,8 @@
   UsageError("");
   UsageError("  --host: used with Portable backend to link against host runtime libraries");
   UsageError("");
+  UsageError("  --dump-timing: display a breakdown of where time was spent");
+  UsageError("");
   UsageError("  --runtime-arg <argument>: used to specify various arguments for the runtime,");
   UsageError("      such as initial heap size, maximum heap size, and verbose output.");
   UsageError("      Use a separate --runtime-arg switch for each argument.");
@@ -607,7 +609,8 @@
 #endif
   bool is_host = false;
   bool dump_stats = kIsDebugBuild;
-  bool dump_timings = kIsDebugBuild;
+  bool dump_timing = false;
+  bool dump_slow_timing = kIsDebugBuild;
   bool watch_dog_enabled = !kIsTargetBuild;
 
 
@@ -695,6 +698,8 @@
         LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
       }
       runtime_args.push_back(argv[i]);
+    } else if (option == "--dump-timing") {
+      dump_timing = true;
     } else {
       Usage("unknown argument %s", option.data());
     }
@@ -825,6 +830,7 @@
     return EXIT_FAILURE;
   }
 
+  timings.StartSplit("dex2oat Setup");
   LOG(INFO) << "dex2oat: " << oat_location;
 
   Runtime::Options options;
@@ -861,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());
@@ -926,7 +933,6 @@
     }
   }
 
-  timings.StartSplit("dex2oat Setup");
   UniquePtr<const CompilerDriver> compiler(dex2oat->CreateOatFile(boot_image_option,
                                                                   host_prefix.get(),
                                                                   android_root,
@@ -1012,7 +1018,7 @@
   }
 
   if (is_host) {
-    if (dump_timings && timings.GetTotalNs() > MsToNs(1000)) {
+    if (dump_timing || (dump_slow_timing && timings.GetTotalNs() > MsToNs(1000))) {
       LOG(INFO) << Dumpable<base::TimingLogger>(timings);
     }
     return EXIT_SUCCESS;
@@ -1053,7 +1059,7 @@
 
   timings.EndSplit();
 
-  if (dump_timings && timings.GetTotalNs() > MsToNs(1000)) {
+  if (dump_timing && timings.GetTotalNs() > MsToNs(1000)) {
     LOG(INFO) << Dumpable<base::TimingLogger>(timings);
   }
   return EXIT_SUCCESS;
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 d0b8bbd..3387a70 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:
@@ -1290,6 +1292,11 @@
   return klass;
 }
 
+bool ClassLinker::IsInBootClassPath(const char* descriptor) {
+  DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
+  return pair.second != NULL;
+}
+
 mirror::Class* ClassLinker::FindSystemClass(const char* descriptor) {
   return FindClass(descriptor, NULL);
 }
@@ -1320,18 +1327,17 @@
     }
 
   } else if (Runtime::Current()->UseCompileTimeClassPath()) {
-    // first try the boot class path
-    mirror::Class* system_class = FindSystemClass(descriptor);
-    if (system_class != NULL) {
+    // First try the boot class path, we check the descriptor first to avoid an unnecessary
+    // throw of a NoClassDefFoundError.
+    if (IsInBootClassPath(descriptor)) {
+      mirror::Class* system_class = FindSystemClass(descriptor);
+      CHECK(system_class != NULL);
       return system_class;
     }
-    CHECK(self->IsExceptionPending());
-    self->ClearException();
-
-    // next try the compile time class path
+    // Next try the compile time class path.
     const std::vector<const DexFile*>* class_path;
     {
-      ScopedObjectAccessUnchecked soa(Thread::Current());
+      ScopedObjectAccessUnchecked soa(self);
       ScopedLocalRef<jobject> jclass_loader(soa.Env(), soa.AddLocalReference<jobject>(class_loader));
       class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
     }
@@ -2280,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) {
@@ -2311,8 +2317,9 @@
   mirror::Class::Status oat_file_class_status(mirror::Class::kStatusNotReady);
   bool preverified = VerifyClassUsingOatFile(dex_file, klass, oat_file_class_status);
   if (oat_file_class_status == mirror::Class::kStatusError) {
-    LOG(WARNING) << "Skipping runtime verification of erroneous class " << PrettyDescriptor(klass)
-                 << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
+    VLOG(class_linker) << "Skipping runtime verification of erroneous class "
+        << PrettyDescriptor(klass) << " in "
+        << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
     ThrowVerifyError(klass, "Rejecting class %s because it failed compile-time verification",
                      PrettyDescriptor(klass).c_str());
     klass->SetStatus(mirror::Class::kStatusError);
@@ -2326,7 +2333,7 @@
   }
   if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) {
     if (!preverified && verifier_failure != verifier::MethodVerifier::kNoFailure) {
-      LOG(WARNING) << "Soft verification failure in class " << PrettyDescriptor(klass)
+      VLOG(class_linker) << "Soft verification failure in class " << PrettyDescriptor(klass)
           << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
           << " because: " << error_msg;
     }
@@ -2334,7 +2341,16 @@
     // 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);
+        // Pretend a soft failure occured so that we don't consider the class verified below.
+        verifier_failure = verifier::MethodVerifier::kSoftFailure;
+      }
     } else {
       CHECK_EQ(verifier_failure, verifier::MethodVerifier::kSoftFailure);
       // Soft failures at compile time should be retried at runtime. Soft
@@ -2355,7 +2371,7 @@
     klass->SetStatus(mirror::Class::kStatusError);
   }
   if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) {
-    // Class is verified so we don't need to do any access check in its methods.
+    // Class is verified so we don't need to do any access check on its methods.
     // Let the interpreter know it by setting the kAccPreverified flag onto each
     // method.
     // Note: we're going here during compilation and at runtime. When we set the
@@ -2690,47 +2706,90 @@
   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();
-
-  mirror::ArtMethod* clinit = NULL;
+  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;
       }
     }
 
-    clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
-    if (clinit != NULL && !can_run_clinit) {
-      // if the class has a <clinit> but we can't run it during compilation,
-      // don't bother going to kStatusInitializing. We return false so that
-      // sub-classes don't believe this class is initialized.
-      // Opportunistically link non-static methods, TODO: don't initialize and dirty pages
-      // in second pass.
-      return false;
-    }
-
     // If the class is kStatusInitializing, either this thread is
     // initializing higher up the stack or another thread has beat us
     // to initializing and we need to wait. Either way, this
@@ -2748,37 +2807,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 while 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);
+  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) {
-    if (Runtime::Current()->IsStarted()) {
+    CHECK(can_init_statics);
+    if (LIKELY(Runtime::Current()->IsStarted())) {
       JValue result;
       clinit->Invoke(self, NULL, 0, &result, 'V');
     } else {
@@ -2786,6 +2873,7 @@
     }
   }
 
+  // Opportunistically set static method trampolines to their destination.
   FixupStaticTrampolines(klass);
 
   uint64_t t1 = NanoTime();
@@ -2805,14 +2893,8 @@
       ++thread_stats->class_init_count;
       global_stats->class_init_time_ns += (t1 - t0);
       thread_stats->class_init_time_ns += (t1 - t0);
-      // Set the class as initialized except if we can't initialize static fields and static field
-      // initialization is necessary.
-      if (!can_init_statics && has_static_field_initializers) {
-        klass->SetStatus(mirror::Class::kStatusVerified);  // Don't leave class in initializing state.
-        success = false;
-      } else {
-        klass->SetStatus(mirror::Class::kStatusInitialized);
-      }
+      // Set the class as initialized except if failed to initialize static fields.
+      klass->SetStatus(mirror::Class::kStatusInitialized);
       if (VLOG_IS_ON(class_linker)) {
         ClassHelper kh(klass);
         LOG(INFO) << "Initialized class " << kh.GetDescriptor() << " from " << kh.GetLocation();
@@ -2827,6 +2909,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
@@ -2953,44 +3036,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;
   }
 
-  Thread* self = Thread::Current();
-  ScopedThreadStateChange tsc(self, kRunnable);
-  bool success = InitializeClass(c, can_run_clinit, can_init_fields);
+  bool success = InitializeClass(c, can_init_fields, can_init_parents);
   if (!success) {
-    CHECK(self->IsExceptionPending() || !can_run_clinit) << PrettyClass(c);
+    Thread* self = Thread::Current();
+    CHECK(self->IsExceptionPending() || !can_init_fields || !can_init_parents) << PrettyClass(c);
   }
   return success;
 }
@@ -3005,35 +3060,6 @@
   }
 }
 
-bool ClassLinker::InitializeStaticFields(mirror::Class* klass) {
-  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()) {
-    // 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;
-  }
-  return false;
-}
-
 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 d0cc562..5ef6d8f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -62,6 +62,8 @@
 
   ~ClassLinker();
 
+  bool IsInBootClassPath(const char* descriptor);
+
   // Finds a class by its descriptor, loading it if necessary.
   // If class_loader is null, searches boot_class_path_.
   mirror::Class* FindClass(const char* descriptor, mirror::ClassLoader* class_loader)
@@ -435,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)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool IsSameDescriptorInDifferentClassContexts(const char* descriptor,
                                                 const mirror::Class* klass1,
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 535d540..cb486d5 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -369,14 +369,17 @@
                                                mirror::Object* receiver,
                                                Thread* self, mirror::ArtMethod** sp)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  DCHECK(proxy_method->IsProxyMethod()) << PrettyMethod(proxy_method);
+  DCHECK(receiver->GetClass()->IsProxyClass()) << PrettyMethod(proxy_method);
   // Ensure we don't get thread suspension until the object arguments are safely in jobjects.
   const char* old_cause =
       self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments");
   // Register the top of the managed stack, making stack crawlable.
-  DCHECK_EQ(*sp, proxy_method);
+  DCHECK_EQ(*sp, proxy_method) << PrettyMethod(proxy_method);
   self->SetTopOfStack(sp, 0);
   DCHECK_EQ(proxy_method->GetFrameSizeInBytes(),
-            Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes());
+            Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes())
+      << PrettyMethod(proxy_method);
   self->VerifyStack();
   // Start new JNI local reference state.
   JNIEnvExt* env = self->GetJniEnv();
@@ -387,15 +390,18 @@
 
   // Placing arguments into args vector and remove the receiver.
   MethodHelper proxy_mh(proxy_method);
+  DCHECK(!proxy_mh.IsStatic()) << PrettyMethod(proxy_method);
   std::vector<jvalue> args;
   BuildQuickArgumentVisitor local_ref_visitor(sp, proxy_mh.IsStatic(), proxy_mh.GetShorty(),
                                               proxy_mh.GetShortyLength(), &soa, &args);
+
   local_ref_visitor.VisitArguments();
+  DCHECK_GT(args.size(), 0U) << PrettyMethod(proxy_method);
   args.erase(args.begin());
 
   // Convert proxy method into expected interface method.
   mirror::ArtMethod* interface_method = proxy_method->FindOverriddenMethod();
-  DCHECK(interface_method != NULL);
+  DCHECK(interface_method != NULL) << PrettyMethod(proxy_method);
   DCHECK(!interface_method->IsProxyMethod()) << PrettyMethod(interface_method);
   jobject interface_method_jobj = soa.AddLocalReference<jobject>(interface_method);
 
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..79d156d 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -39,7 +39,7 @@
     vm_ = Runtime::Current()->GetJavaVM();
 
     // Turn on -verbose:jni for the JNI tests.
-    gLogVerbosity.jni = true;
+    // gLogVerbosity.jni = true;
 
     vm_->AttachCurrentThread(&env_, NULL);
 
@@ -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)