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