diff options
109 files changed, 3920 insertions, 1996 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 9cc94e8..4af492b 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -513,10 +513,9 @@ void CompilerDriver::CompileAll(jobject class_loader, } } -static DexToDexCompilationLevel GetDexToDexCompilationlevel(mirror::ClassLoader* class_loader, - const DexFile& dex_file, - const DexFile::ClassDef& class_def) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { +static DexToDexCompilationLevel GetDexToDexCompilationlevel( + SirtRef<mirror::ClassLoader>& class_loader, const DexFile& dex_file, + const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const char* descriptor = dex_file.GetClassDescriptor(class_def); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::Class* klass = class_linker->FindClass(descriptor, class_loader); @@ -531,7 +530,7 @@ static DexToDexCompilationLevel GetDexToDexCompilationlevel(mirror::ClassLoader* // function). Since image classes can be verified again while compiling an application, // we must prevent the DEX-to-DEX compiler from introducing them. // TODO: find a way to enable "quick" instructions for image classes and remove this check. - bool compiling_image_classes = (class_loader == NULL); + bool compiling_image_classes = class_loader.get() == nullptr; if (compiling_image_classes) { return kRequired; } else if (klass->IsVerified()) { @@ -579,7 +578,8 @@ void CompilerDriver::CompileOne(const mirror::ArtMethod* method, base::TimingLog { ScopedObjectAccess soa(Thread::Current()); const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), + soa.Decode<mirror::ClassLoader*>(jclass_loader)); dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, *dex_file, class_def); } CompileMethod(code_item, method->GetAccessFlags(), method->GetInvokeType(), @@ -721,8 +721,8 @@ void CompilerDriver::LoadImageClasses(base::TimingLogger& timings) for (const std::pair<uint16_t, const DexFile*>& exception_type : unresolved_exception_types) { uint16_t exception_type_idx = exception_type.first; const DexFile* dex_file = exception_type.second; - mirror::DexCache* dex_cache = class_linker->FindDexCache(*dex_file); - mirror:: ClassLoader* class_loader = NULL; + SirtRef<mirror::DexCache> dex_cache(self, class_linker->FindDexCache(*dex_file)); + SirtRef<mirror::ClassLoader> class_loader(self, nullptr); SirtRef<mirror::Class> klass(self, class_linker->ResolveType(*dex_file, exception_type_idx, dex_cache, class_loader)); if (klass.get() == NULL) { @@ -782,15 +782,14 @@ void CompilerDriver::UpdateImageClasses(base::TimingLogger& timings) { const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); gc::Heap* heap = Runtime::Current()->GetHeap(); // TODO: Image spaces only? + ScopedObjectAccess soa(Thread::Current()); WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this); + heap->VisitObjects(FindClinitImageClassesCallback, this); self->EndAssertNoThreadSuspension(old_cause); } } -bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, - uint32_t type_idx) { +bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx) { if (IsImage() && IsImageClass(dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) { if (kIsDebugBuild) { @@ -815,7 +814,7 @@ bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, if (IsImage()) { // We resolve all const-string strings when building for the image. ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(dex_file); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), Runtime::Current()->GetClassLinker()->FindDexCache(dex_file)); Runtime::Current()->GetClassLinker()->ResolveString(dex_file, string_idx, dex_cache); result = true; } @@ -903,26 +902,27 @@ bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_id } static mirror::Class* ComputeCompilingMethodsClass(ScopedObjectAccess& soa, - mirror::DexCache* dex_cache, + SirtRef<mirror::DexCache>& dex_cache, const DexCompilationUnit* mUnit) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // The passed dex_cache is a hint, sanity check before asking the class linker that will take a // lock. if (dex_cache->GetDexFile() != mUnit->GetDexFile()) { - dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); + dex_cache.reset(mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); } - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()); - const DexFile::MethodId& referrer_method_id = mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); + SirtRef<mirror::ClassLoader> + class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); + const DexFile::MethodId& referrer_method_id = + mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); return mUnit->GetClassLinker()->ResolveType(*mUnit->GetDexFile(), referrer_method_id.class_idx_, dex_cache, class_loader); } -static mirror::ArtField* ComputeFieldReferencedFromCompilingMethod(ScopedObjectAccess& soa, - const DexCompilationUnit* mUnit, - uint32_t field_idx) +static mirror::ArtField* ComputeFieldReferencedFromCompilingMethod( + ScopedObjectAccess& soa, const DexCompilationUnit* mUnit, uint32_t field_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); return mUnit->GetClassLinker()->ResolveField(*mUnit->GetDexFile(), field_idx, dex_cache, class_loader, false); } @@ -932,8 +932,8 @@ static mirror::ArtMethod* ComputeMethodReferencedFromCompilingMethod(ScopedObjec uint32_t method_idx, InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* dex_cache = mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile()); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*mUnit->GetDexFile())); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); return mUnit->GetClassLinker()->ResolveMethod(*mUnit->GetDexFile(), method_idx, dex_cache, class_loader, NULL, type); } @@ -947,9 +947,10 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi // Try to resolve field and ignore if an Incompatible Class Change Error (ie is static). mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && !resolved_field->IsStatic()) { + SirtRef<mirror::DexCache> dex_cache(soa.Self(), + resolved_field->GetDeclaringClass()->GetDexCache()); mirror::Class* referrer_class = - ComputeCompilingMethodsClass(soa, resolved_field->GetDeclaringClass()->GetDexCache(), - mUnit); + ComputeCompilingMethodsClass(soa, dex_cache, mUnit); if (referrer_class != NULL) { mirror::Class* fields_class = resolved_field->GetDeclaringClass(); bool access_ok = referrer_class->CanAccess(fields_class) && @@ -997,9 +998,9 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila // Try to resolve field and ignore if an Incompatible Class Change Error (ie isn't static). mirror::ArtField* resolved_field = ComputeFieldReferencedFromCompilingMethod(soa, mUnit, field_idx); if (resolved_field != NULL && resolved_field->IsStatic()) { + SirtRef<mirror::DexCache> dex_cache(soa.Self(), resolved_field->GetDeclaringClass()->GetDexCache()); mirror::Class* referrer_class = - ComputeCompilingMethodsClass(soa, resolved_field->GetDeclaringClass()->GetDexCache(), - mUnit); + ComputeCompilingMethodsClass(soa, dex_cache, mUnit); if (referrer_class != NULL) { mirror::Class* fields_class = resolved_field->GetDeclaringClass(); if (fields_class == referrer_class) { @@ -1085,7 +1086,7 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType *direct_code = 0; *direct_method = 0; bool use_dex_cache = false; - bool compiling_boot = Runtime::Current()->GetHeap()->GetContinuousSpaces().size() == 1; + const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot(); if (compiler_backend_ == kPortable) { if (sharp_type != kStatic && sharp_type != kDirect) { return; @@ -1198,9 +1199,9 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui } // Don't try to fast-path if we don't understand the caller's class or this appears to be an // Incompatible Class Change Error. + SirtRef<mirror::DexCache> dex_cache(soa.Self(), resolved_method->GetDeclaringClass()->GetDexCache()); mirror::Class* referrer_class = - ComputeCompilingMethodsClass(soa, resolved_method->GetDeclaringClass()->GetDexCache(), - mUnit); + ComputeCompilingMethodsClass(soa, dex_cache, mUnit); bool icce = resolved_method->CheckIncompatibleClassChange(*invoke_type); if (referrer_class != NULL && !icce) { mirror::Class* methods_class = resolved_method->GetDeclaringClass(); @@ -1254,10 +1255,8 @@ bool CompilerDriver::ComputeInvokeInfo(const DexCompilationUnit* mUnit, const ui const MethodReference* devirt_map_target = verifier::MethodVerifier::GetDevirtMap(caller_method, dex_pc); if (devirt_map_target != NULL) { - mirror::DexCache* target_dex_cache = - mUnit->GetClassLinker()->FindDexCache(*devirt_map_target->dex_file); - mirror::ClassLoader* class_loader = - soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()); + SirtRef<mirror::DexCache> target_dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*devirt_map_target->dex_file)); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); mirror::ArtMethod* called_method = mUnit->GetClassLinker()->ResolveMethod(*devirt_map_target->dex_file, devirt_map_target->dex_method_index, @@ -1509,13 +1508,11 @@ static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manag const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); if (!SkipClass(class_linker, jclass_loader, dex_file, class_def)) { ScopedObjectAccess soa(self); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader); - mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file); - + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader)); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), class_linker->FindDexCache(dex_file)); // Resolve the class. mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, class_loader); - 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 @@ -1598,8 +1595,8 @@ static void ResolveType(const ParallelCompilationManager* manager, size_t type_i ScopedObjectAccess soa(Thread::Current()); ClassLinker* class_linker = manager->GetClassLinker(); const DexFile& dex_file = *manager->GetDexFile(); - mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader()); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), class_linker->FindDexCache(dex_file)); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(manager->GetClassLoader())); mirror::Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader); if (klass == NULL) { @@ -1652,8 +1649,9 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ const char* descriptor = dex_file.GetClassDescriptor(class_def); ClassLinker* class_linker = manager->GetClassLinker(); jobject jclass_loader = manager->GetClassLoader(); - mirror::Class* klass = class_linker->FindClass(descriptor, - soa.Decode<mirror::ClassLoader*>(jclass_loader)); + SirtRef<mirror::ClassLoader> class_loader( + soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader)); + mirror::Class* klass = class_linker->FindClass(descriptor, class_loader); if (klass == NULL) { CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); @@ -1663,11 +1661,10 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ * This is to ensure the class is structurally sound for compilation. An unsound class * will be rejected by the verifier and later skipped during compilation in the compiler. */ - mirror::DexCache* dex_cache = class_linker->FindDexCache(dex_file); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), class_linker->FindDexCache(dex_file)); std::string error_msg; - if (verifier::MethodVerifier::VerifyClass(&dex_file, dex_cache, - soa.Decode<mirror::ClassLoader*>(jclass_loader), - &class_def, true, &error_msg) == + if (verifier::MethodVerifier::VerifyClass(&dex_file, dex_cache, class_loader, &class_def, true, + &error_msg) == verifier::MethodVerifier::kHardFailure) { LOG(ERROR) << "Verification failed on class " << PrettyDescriptor(descriptor) << " because: " << error_msg; @@ -2124,7 +2121,8 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl const char* descriptor = dex_file.StringDataByIdx(class_type_id.descriptor_idx_); ScopedObjectAccess soa(Thread::Current()); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), + soa.Decode<mirror::ClassLoader*>(jclass_loader)); mirror::Class* klass = manager->GetClassLinker()->FindClass(descriptor, class_loader); if (klass != NULL && !SkipClass(jclass_loader, dex_file, klass)) { @@ -2253,7 +2251,8 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile; { ScopedObjectAccess soa(Thread::Current()); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), + soa.Decode<mirror::ClassLoader*>(jclass_loader)); dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, dex_file, class_def); } ClassDataItemIterator it(dex_file, class_data); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 9321f06..9bfea6f 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -368,7 +368,8 @@ class CompilerDriver { ThreadPool& thread_pool, base::TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_, compiled_classes_lock_); - void UpdateImageClasses(base::TimingLogger& timings); + void UpdateImageClasses(base::TimingLogger& timings) + LOCKS_EXCLUDED(Locks::mutator_lock_); static void FindClinitImageClassesCallback(mirror::Object* object, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc index c6687bb..bfc93b3 100644 --- a/compiler/driver/compiler_driver_test.cc +++ b/compiler/driver/compiler_driver_test.cc @@ -78,7 +78,9 @@ class CompilerDriverTest : public CommonTest { const DexFile::ClassDef& class_def = dex_file.GetClassDef(i); const char* descriptor = dex_file.GetClassDescriptor(class_def); ScopedObjectAccess soa(Thread::Current()); - mirror::Class* c = class_linker->FindClass(descriptor, soa.Decode<mirror::ClassLoader*>(class_loader)); + Thread* self = Thread::Current(); + SirtRef<mirror::ClassLoader> loader(self, soa.Decode<mirror::ClassLoader*>(class_loader)); + mirror::Class* c = class_linker->FindClass(descriptor, loader); CHECK(c != NULL); for (size_t i = 0; i < c->NumDirectMethods(); i++) { MakeExecutable(c->GetDirectMethod(i)); @@ -142,8 +144,9 @@ TEST_F(CompilerDriverTest, AbstractMethodErrorStub) { jobject class_loader; { ScopedObjectAccess soa(Thread::Current()); - CompileVirtualMethod(NULL, "java.lang.Class", "isFinalizable", "()Z"); - CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V"); + SirtRef<mirror::ClassLoader> null_loader(soa.Self(), nullptr); + CompileVirtualMethod(null_loader, "java.lang.Class", "isFinalizable", "()Z"); + CompileDirectMethod(null_loader, "java.lang.Object", "<init>", "()V"); class_loader = LoadDex("AbstractMethod"); } ASSERT_TRUE(class_loader != NULL); diff --git a/compiler/image_test.cc b/compiler/image_test.cc index a8b7c88..9d9c064 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -94,8 +94,8 @@ TEST_F(ImageTest, WriteRead) { ASSERT_NE(0U, image_header.GetImageBitmapSize()); gc::Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_EQ(1U, heap->GetContinuousSpaces().size()); - gc::space::ContinuousSpace* space = heap->GetContinuousSpaces().front(); + ASSERT_TRUE(!heap->GetContinuousSpaces().empty()); + gc::space::ContinuousSpace* space = heap->GetNonMovingSpace(); ASSERT_FALSE(space->IsImageSpace()); ASSERT_TRUE(space != NULL); ASSERT_TRUE(space->IsDlMallocSpace()); @@ -139,11 +139,8 @@ TEST_F(ImageTest, WriteRead) { class_linker_ = runtime_->GetClassLinker(); gc::Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_EQ(2U, heap->GetContinuousSpaces().size()); - ASSERT_TRUE(heap->GetContinuousSpaces()[0]->IsImageSpace()); - ASSERT_FALSE(heap->GetContinuousSpaces()[0]->IsDlMallocSpace()); - ASSERT_FALSE(heap->GetContinuousSpaces()[1]->IsImageSpace()); - ASSERT_TRUE(heap->GetContinuousSpaces()[1]->IsDlMallocSpace()); + ASSERT_TRUE(heap->HasImageSpace()); + ASSERT_TRUE(heap->GetNonMovingSpace()->IsDlMallocSpace()); gc::space::ImageSpace* image_space = heap->GetImageSpace(); image_space->VerifyImageAllocations(); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 75be2c9..c22f8d6 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -75,8 +75,6 @@ bool ImageWriter::Write(const std::string& image_filename, image_begin_ = reinterpret_cast<byte*>(image_begin); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - const std::vector<DexCache*>& all_dex_caches = class_linker->GetDexCaches(); - dex_caches_.insert(all_dex_caches.begin(), all_dex_caches.end()); UniquePtr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str())); if (oat_file.get() == NULL) { @@ -121,22 +119,16 @@ bool ImageWriter::Write(const std::string& image_filename, } gc::Heap* heap = Runtime::Current()->GetHeap(); heap->CollectGarbage(false); // Remove garbage. - // Trim size of alloc spaces. - for (const auto& space : heap->GetContinuousSpaces()) { - if (space->IsDlMallocSpace()) { - space->AsDlMallocSpace()->Trim(); - } - } if (!AllocMemory()) { return false; } -#ifndef NDEBUG - { // NOLINT(whitespace/braces) + + if (kIsDebugBuild) { ScopedObjectAccess soa(Thread::Current()); CheckNonImageClassesRemoved(); } -#endif + Thread::Current()->TransitionFromSuspendedToRunnable(); size_t oat_loaded_size = 0; size_t oat_data_offset = 0; @@ -144,8 +136,6 @@ bool ImageWriter::Write(const std::string& image_filename, CalculateNewObjectOffsets(oat_loaded_size, oat_data_offset); CopyAndFixupObjects(); PatchOatCodeAndMethods(); - // Record allocations into the image bitmap. - RecordImageAllocations(); Thread::Current()->TransitionFromRunnableToSuspended(kNative); UniquePtr<File> image_file(OS::CreateEmptyFile(image_filename.c_str())); @@ -178,39 +168,82 @@ bool ImageWriter::Write(const std::string& image_filename, return true; } -void ImageWriter::RecordImageAllocations() { - uint64_t start_time = NanoTime(); - CHECK(image_bitmap_.get() != nullptr); - for (const auto& it : offsets_) { - mirror::Object* obj = reinterpret_cast<mirror::Object*>(image_->Begin() + it.second); - DCHECK_ALIGNED(obj, kObjectAlignment); - image_bitmap_->Set(obj); +void ImageWriter::SetImageOffset(mirror::Object* object, size_t offset) { + DCHECK(object != nullptr); + DCHECK_NE(offset, 0U); + DCHECK(!IsImageOffsetAssigned(object)); + mirror::Object* obj = reinterpret_cast<mirror::Object*>(image_->Begin() + offset); + DCHECK_ALIGNED(obj, kObjectAlignment); + image_bitmap_->Set(obj); + // Before we stomp over the lock word, save the hash code for later. + Monitor::Deflate(Thread::Current(), object);; + LockWord lw(object->GetLockWord()); + switch (lw.GetState()) { + case LockWord::kFatLocked: { + LOG(FATAL) << "Fat locked object " << obj << " found during object copy"; + break; + } + case LockWord::kThinLocked: { + LOG(FATAL) << "Thin locked object " << obj << " found during object copy"; + break; + } + case LockWord::kUnlocked: + // No hash, don't need to save it. + break; + case LockWord::kHashCode: + saved_hashes_.push_back(std::make_pair(obj, lw.GetHashCode())); + break; + default: + LOG(FATAL) << "Unreachable."; + break; } - LOG(INFO) << "RecordImageAllocations took " << PrettyDuration(NanoTime() - start_time); + object->SetLockWord(LockWord::FromForwardingAddress(offset)); + DCHECK(IsImageOffsetAssigned(object)); } -bool ImageWriter::AllocMemory() { - size_t size = 0; - for (const auto& space : Runtime::Current()->GetHeap()->GetContinuousSpaces()) { - if (space->IsDlMallocSpace()) { - size += space->Size(); - } - } +void ImageWriter::AssignImageOffset(mirror::Object* object) { + DCHECK(object != nullptr); + SetImageOffset(object, image_end_); + image_end_ += RoundUp(object->SizeOf(), 8); // 64-bit alignment + DCHECK_LT(image_end_, image_->Size()); +} - int prot = PROT_READ | PROT_WRITE; - size_t length = RoundUp(size, kPageSize); +bool ImageWriter::IsImageOffsetAssigned(const mirror::Object* object) const { + DCHECK(object != nullptr); + return object->GetLockWord().GetState() == LockWord::kForwardingAddress; +} + +size_t ImageWriter::GetImageOffset(const mirror::Object* object) const { + DCHECK(object != nullptr); + DCHECK(IsImageOffsetAssigned(object)); + LockWord lock_word = object->GetLockWord(); + size_t offset = lock_word.ForwardingAddress(); + DCHECK_LT(offset, image_end_); + return offset; +} + +bool ImageWriter::AllocMemory() { + size_t length = RoundUp(Runtime::Current()->GetHeap()->GetTotalMemory(), kPageSize); std::string error_msg; - image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, prot, &error_msg)); + image_.reset(MemMap::MapAnonymous("image writer image", NULL, length, PROT_READ | PROT_WRITE, + &error_msg)); if (UNLIKELY(image_.get() == nullptr)) { LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg; return false; } + + // Create the image bitmap. + image_bitmap_.reset(gc::accounting::SpaceBitmap::Create("image bitmap", image_->Begin(), + length)); + if (image_bitmap_.get() == nullptr) { + LOG(ERROR) << "Failed to allocate memory for image bitmap"; + return false; + } return true; } void ImageWriter::ComputeLazyFieldsForImageClasses() { - Runtime* runtime = Runtime::Current(); - ClassLinker* class_linker = runtime->GetClassLinker(); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); class_linker->VisitClassesWithoutClassesLock(ComputeLazyFieldsForClassesVisitor, NULL); } @@ -223,13 +256,12 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { if (!obj->GetClass()->IsStringClass()) { return; } - String* string = obj->AsString(); + mirror::String* string = obj->AsString(); const uint16_t* utf16_string = string->GetCharArray()->GetData() + string->GetOffset(); - ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg); - for (DexCache* dex_cache : writer->dex_caches_) { + for (DexCache* dex_cache : Runtime::Current()->GetClassLinker()->GetDexCaches()) { const DexFile& dex_file = *dex_cache->GetDexFile(); const DexFile::StringId* string_id = dex_file.FindStringId(utf16_string); - if (string_id != NULL) { + if (string_id != nullptr) { // This string occurs in this dex file, assign the dex cache entry. uint32_t string_idx = dex_file.GetIndexForStringId(*string_id); if (dex_cache->GetResolvedString(string_idx) == NULL) { @@ -239,13 +271,9 @@ void ImageWriter::ComputeEagerResolvedStringsCallback(Object* obj, void* arg) { } } -void ImageWriter::ComputeEagerResolvedStrings() - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // TODO: Check image spaces only? - gc::Heap* heap = Runtime::Current()->GetHeap(); - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(ComputeEagerResolvedStringsCallback, this); +void ImageWriter::ComputeEagerResolvedStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + Runtime::Current()->GetHeap()->VisitObjects(ComputeEagerResolvedStringsCallback, this); } bool ImageWriter::IsImageClass(const Class* klass) { @@ -278,7 +306,7 @@ void ImageWriter::PruneNonImageClasses() { // Clear references to removed classes from the DexCaches. ArtMethod* resolution_method = runtime->GetResolutionMethod(); - for (DexCache* dex_cache : dex_caches_) { + for (DexCache* dex_cache : class_linker->GetDexCaches()) { for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { Class* klass = dex_cache->GetResolvedType(i); if (klass != NULL && !IsImageClass(klass)) { @@ -311,31 +339,22 @@ bool ImageWriter::NonImageClassesVisitor(Class* klass, void* arg) { void ImageWriter::CheckNonImageClassesRemoved() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (compiler_driver_.GetImageClasses() == NULL) { - return; - } - - gc::Heap* heap = Runtime::Current()->GetHeap(); - Thread* self = Thread::Current(); - { - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); + if (compiler_driver_.GetImageClasses() != nullptr) { + gc::Heap* heap = Runtime::Current()->GetHeap(); + ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + heap->VisitObjects(CheckNonImageClassesRemovedCallback, this); } - - ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->GetLiveBitmap()->Walk(CheckNonImageClassesRemovedCallback, this); } void ImageWriter::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) { ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); - if (!obj->IsClass()) { - return; - } - Class* klass = obj->AsClass(); - if (!image_writer->IsImageClass(klass)) { - image_writer->DumpImageClasses(); - CHECK(image_writer->IsImageClass(klass)) << ClassHelper(klass).GetDescriptor() - << " " << PrettyDescriptor(klass); + if (obj->IsClass()) { + Class* klass = obj->AsClass(); + if (!image_writer->IsImageClass(klass)) { + image_writer->DumpImageClasses(); + CHECK(image_writer->IsImageClass(klass)) << ClassHelper(klass).GetDescriptor() + << " " << PrettyDescriptor(klass); + } } } @@ -347,53 +366,50 @@ void ImageWriter::DumpImageClasses() { } } -void ImageWriter::CalculateNewObjectOffsetsCallback(Object* obj, void* arg) { +void ImageWriter::CalculateObjectOffsets(Object* obj) { DCHECK(obj != NULL); - DCHECK(arg != NULL); - ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); - // if it is a string, we want to intern it if its not interned. if (obj->GetClass()->IsStringClass()) { // we must be an interned string that was forward referenced and already assigned - if (image_writer->IsImageOffsetAssigned(obj)) { + if (IsImageOffsetAssigned(obj)) { DCHECK_EQ(obj, obj->AsString()->Intern()); return; } - SirtRef<String> interned(Thread::Current(), obj->AsString()->Intern()); - if (obj != interned.get()) { - if (!image_writer->IsImageOffsetAssigned(interned.get())) { + Thread* self = Thread::Current(); + SirtRef<Object> sirt_obj(self, obj); + mirror::String* interned = obj->AsString()->Intern(); + if (sirt_obj.get() != interned) { + if (!IsImageOffsetAssigned(interned)) { // interned obj is after us, allocate its location early - image_writer->AssignImageOffset(interned.get()); + AssignImageOffset(interned); } // point those looking for this object to the interned version. - image_writer->SetImageOffset(obj, image_writer->GetImageOffset(interned.get())); + SetImageOffset(sirt_obj.get(), GetImageOffset(interned)); return; } // else (obj == interned), nothing to do but fall through to the normal case } - image_writer->AssignImageOffset(obj); + AssignImageOffset(obj); } ObjectArray<Object>* ImageWriter::CreateImageRoots() const { Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); - Class* object_array_class = class_linker->FindSystemClass("[Ljava/lang/Object;"); Thread* self = Thread::Current(); + SirtRef<Class> object_array_class(self, class_linker->FindSystemClass("[Ljava/lang/Object;")); // build an Object[] of all the DexCaches used in the source_space_ - ObjectArray<Object>* dex_caches = ObjectArray<Object>::Alloc(self, object_array_class, - dex_caches_.size()); + ObjectArray<Object>* dex_caches = ObjectArray<Object>::Alloc(self, object_array_class.get(), + class_linker->GetDexCaches().size()); int i = 0; - for (DexCache* dex_cache : dex_caches_) { + for (DexCache* dex_cache : class_linker->GetDexCaches()) { dex_caches->Set(i++, dex_cache); } // build an Object[] of the roots needed to restore the runtime - SirtRef<ObjectArray<Object> > - image_roots(self, - ObjectArray<Object>::Alloc(self, object_array_class, - ImageHeader::kImageRootsMax)); + SirtRef<ObjectArray<Object> > image_roots( + self, ObjectArray<Object>::Alloc(self, object_array_class.get(), ImageHeader::kImageRootsMax)); image_roots->Set(ImageHeader::kResolutionMethod, runtime->GetResolutionMethod()); image_roots->Set(ImageHeader::kImtConflictMethod, runtime->GetImtConflictMethod()); image_roots->Set(ImageHeader::kDefaultImt, runtime->GetDefaultImt()); @@ -405,24 +421,82 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots() const { runtime->GetCalleeSaveMethod(Runtime::kRefsAndArgs)); image_roots->Set(ImageHeader::kOatLocation, String::AllocFromModifiedUtf8(self, oat_file_->GetLocation().c_str())); - image_roots->Set(ImageHeader::kDexCaches, - dex_caches); - image_roots->Set(ImageHeader::kClassRoots, - class_linker->GetClassRoots()); + image_roots->Set(ImageHeader::kDexCaches, dex_caches); + image_roots->Set(ImageHeader::kClassRoots, class_linker->GetClassRoots()); for (int i = 0; i < ImageHeader::kImageRootsMax; i++) { CHECK(image_roots->Get(i) != NULL); } return image_roots.get(); } +// Walk instance fields of the given Class. Separate function to allow recursion on the super +// class. +void ImageWriter::WalkInstanceFields(mirror::Object* obj, mirror::Class* klass) { + // Visit fields of parent classes first. + SirtRef<mirror::Class> sirt_class(Thread::Current(), klass); + mirror::Class* super = sirt_class->GetSuperClass(); + if (super != nullptr) { + WalkInstanceFields(obj, super); + } + // + size_t num_reference_fields = sirt_class->NumReferenceInstanceFields(); + for (size_t i = 0; i < num_reference_fields; ++i) { + mirror::ArtField* field = sirt_class->GetInstanceField(i); + MemberOffset field_offset = field->GetOffset(); + mirror::Object* value = obj->GetFieldObject<mirror::Object*>(field_offset, false); + if (value != nullptr) { + WalkFieldsInOrder(value); + } + } +} + +// For an unvisited object, visit it then all its children found via fields. +void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { + if (!IsImageOffsetAssigned(obj)) { + // Walk instance fields of all objects + Thread* self = Thread::Current(); + SirtRef<mirror::Object> sirt_obj(self, obj); + SirtRef<mirror::Class> klass(self, obj->GetClass()); + // visit the object itself. + CalculateObjectOffsets(sirt_obj.get()); + WalkInstanceFields(sirt_obj.get(), klass.get()); + // Walk static fields of a Class. + if (sirt_obj->IsClass()) { + size_t num_static_fields = klass->NumReferenceStaticFields(); + for (size_t i = 0; i < num_static_fields; ++i) { + mirror::ArtField* field = klass->GetStaticField(i); + MemberOffset field_offset = field->GetOffset(); + mirror::Object* value = sirt_obj->GetFieldObject<mirror::Object*>(field_offset, false); + if (value != nullptr) { + WalkFieldsInOrder(value); + } + } + } else if (sirt_obj->IsObjectArray()) { + // Walk elements of an object array. + int32_t length = sirt_obj->AsObjectArray<mirror::Object>()->GetLength(); + for (int32_t i = 0; i < length; i++) { + mirror::ObjectArray<mirror::Object>* obj_array = sirt_obj->AsObjectArray<mirror::Object>(); + mirror::Object* value = obj_array->Get(i); + if (value != nullptr) { + WalkFieldsInOrder(value); + } + } + } + } +} + +void ImageWriter::WalkFieldsCallback(mirror::Object* obj, void* arg) { + ImageWriter* writer = reinterpret_cast<ImageWriter*>(arg); + DCHECK(writer != nullptr); + writer->WalkFieldsInOrder(obj); +} + void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_data_offset) { CHECK_NE(0U, oat_loaded_size); Thread* self = Thread::Current(); SirtRef<ObjectArray<Object> > image_roots(self, CreateImageRoots()); gc::Heap* heap = Runtime::Current()->GetHeap(); - const auto& spaces = heap->GetContinuousSpaces(); - DCHECK(!spaces.empty()); DCHECK_EQ(0U, image_end_); // Leave space for the header, but do not write it yet, we need to @@ -431,21 +505,14 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d { WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); // TODO: Image spaces only? - // TODO: Add InOrderWalk to heap bitmap. const char* old = self->StartAssertNoThreadSuspension("ImageWriter"); - DCHECK(heap->GetLargeObjectsSpace()->GetLiveObjects()->IsEmpty()); - for (const auto& space : spaces) { - space->GetLiveBitmap()->InOrderWalk(CalculateNewObjectOffsetsCallback, this); - DCHECK_LT(image_end_, image_->Size()); - } + DCHECK_LT(image_end_, image_->Size()); + // Clear any pre-existing monitors which may have been in the monitor words. + heap->VisitObjects(WalkFieldsCallback, this); self->EndAssertNoThreadSuspension(old); } - // Create the image bitmap. - image_bitmap_.reset(gc::accounting::SpaceBitmap::Create("image bitmap", image_->Begin(), - image_end_)); const byte* oat_file_begin = image_begin_ + RoundUp(image_end_, kPageSize); const byte* oat_file_end = oat_file_begin + oat_loaded_size; oat_data_begin_ = oat_file_begin + oat_data_offset; @@ -456,7 +523,8 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d ImageHeader image_header(reinterpret_cast<uint32_t>(image_begin_), static_cast<uint32_t>(image_end_), RoundUp(image_end_, kPageSize), - image_bitmap_->Size(), + RoundUp(image_end_ / gc::accounting::SpaceBitmap::kAlignment, + sizeof(size_t)), reinterpret_cast<uint32_t>(GetImageAddress(image_roots.get())), oat_file_->GetOatHeader().GetChecksum(), reinterpret_cast<uint32_t>(oat_file_begin), @@ -477,17 +545,19 @@ void ImageWriter::CopyAndFixupObjects() heap->DisableObjectValidation(); // TODO: Image spaces only? WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(CopyAndFixupObjectsCallback, this); + heap->VisitObjects(CopyAndFixupObjectsCallback, this); + // Fix up the object previously had hash codes. + for (const std::pair<mirror::Object*, uint32_t>& hash_pair : saved_hashes_) { + hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second)); + } + saved_hashes_.clear(); self->EndAssertNoThreadSuspension(old_cause); } -void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { - DCHECK(object != NULL); +void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) { + DCHECK(obj != NULL); DCHECK(arg != NULL); - const Object* obj = object; ImageWriter* image_writer = reinterpret_cast<ImageWriter*>(arg); - // see GetLocalAddress for similar computation size_t offset = image_writer->GetImageOffset(obj); byte* dst = image_writer->image_->Begin() + offset; @@ -498,33 +568,7 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* object, void* arg) { Object* copy = reinterpret_cast<Object*>(dst); // Write in a hash code of objects which have inflated monitors or a hash code in their monitor // word. - LockWord lw(copy->GetLockWord()); - switch (lw.GetState()) { - case LockWord::kFatLocked: { - Monitor* monitor = lw.FatLockMonitor(); - CHECK(monitor != nullptr); - CHECK(!monitor->IsLocked()); - if (monitor->HasHashCode()) { - copy->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode())); - } else { - copy->SetLockWord(LockWord()); - } - break; - } - case LockWord::kThinLocked: { - LOG(FATAL) << "Thin locked object " << obj << " found during object copy"; - break; - } - case LockWord::kUnlocked: - break; - case LockWord::kHashCode: - // Do nothing since we can just keep the same hash code. - CHECK_NE(lw.GetHashCode(), 0); - break; - default: - LOG(FATAL) << "Unreachable."; - break; - } + copy->SetLockWord(LockWord()); image_writer->FixupObject(obj, copy); } @@ -629,19 +673,13 @@ void ImageWriter::FixupInstanceFields(const Object* orig, Object* copy) { DCHECK(copy != NULL); Class* klass = orig->GetClass(); DCHECK(klass != NULL); - FixupFields(orig, - copy, - klass->GetReferenceInstanceOffsets(), - false); + FixupFields(orig, copy, klass->GetReferenceInstanceOffsets(), false); } void ImageWriter::FixupStaticFields(const Class* orig, Class* copy) { DCHECK(orig != NULL); DCHECK(copy != NULL); - FixupFields(orig, - copy, - orig->GetReferenceStaticOffsets(), - true); + FixupFields(orig, copy, orig->GetReferenceStaticOffsets(), true); } void ImageWriter::FixupFields(const Object* orig, @@ -693,11 +731,13 @@ void ImageWriter::FixupFields(const Object* orig, static ArtMethod* GetTargetMethod(const CompilerDriver::PatchInformation* patch) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - DexCache* dex_cache = class_linker->FindDexCache(patch->GetDexFile()); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, class_linker->FindDexCache(patch->GetDexFile())); + SirtRef<mirror::ClassLoader> class_loader(self, nullptr); ArtMethod* method = class_linker->ResolveMethod(patch->GetDexFile(), patch->GetTargetMethodIdx(), dex_cache, - NULL, + class_loader, NULL, patch->GetTargetInvokeType()); CHECK(method != NULL) @@ -749,15 +789,15 @@ void ImageWriter::SetPatchLocation(const CompilerDriver::PatchInformation* patch // TODO: make this Thumb2 specific uint8_t* base = reinterpret_cast<uint8_t*>(reinterpret_cast<uint32_t>(oat_code) & ~0x1); uint32_t* patch_location = reinterpret_cast<uint32_t*>(base + patch->GetLiteralOffset()); -#ifndef NDEBUG - const DexFile::MethodId& id = patch->GetDexFile().GetMethodId(patch->GetTargetMethodIdx()); - uint32_t expected = reinterpret_cast<uint32_t>(&id); - uint32_t actual = *patch_location; - CHECK(actual == expected || actual == value) << std::hex - << "actual=" << actual - << "expected=" << expected - << "value=" << value; -#endif + if (kIsDebugBuild) { + const DexFile::MethodId& id = patch->GetDexFile().GetMethodId(patch->GetTargetMethodIdx()); + uint32_t expected = reinterpret_cast<uint32_t>(&id); + uint32_t actual = *patch_location; + CHECK(actual == expected || actual == value) << std::hex + << "actual=" << actual + << "expected=" << expected + << "value=" << value; + } *patch_location = value; oat_header.UpdateChecksum(patch_location, sizeof(value)); } diff --git a/compiler/image_writer.h b/compiler/image_writer.h index 0b408e8..695f59b 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -63,31 +63,11 @@ class ImageWriter { void RecordImageAllocations() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // We use the lock word to store the offset of the object in the image. - void AssignImageOffset(mirror::Object* object) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(object != NULL); - SetImageOffset(object, image_end_); - image_end_ += RoundUp(object->SizeOf(), 8); // 64-bit alignment - DCHECK_LT(image_end_, image_->Size()); - } - - void SetImageOffset(mirror::Object* object, size_t offset) { - DCHECK(object != NULL); - DCHECK_NE(offset, 0U); - DCHECK(!IsImageOffsetAssigned(object)); - offsets_.Put(object, offset); - } - - size_t IsImageOffsetAssigned(const mirror::Object* object) const { - DCHECK(object != NULL); - return offsets_.find(object) != offsets_.end(); - } - - size_t GetImageOffset(const mirror::Object* object) const { - DCHECK(object != NULL); - DCHECK(IsImageOffsetAssigned(object)); - return offsets_.find(object)->second; - } + void AssignImageOffset(mirror::Object* object) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetImageOffset(mirror::Object* object, size_t offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsImageOffsetAssigned(const mirror::Object* object) const; + size_t GetImageOffset(const mirror::Object* object) const; mirror::Object* GetImageAddress(const mirror::Object* object) const { if (object == NULL) { @@ -147,7 +127,14 @@ class ImageWriter { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::ObjectArray<mirror::Object>* CreateImageRoots() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void CalculateNewObjectOffsetsCallback(mirror::Object* obj, void* arg) + void CalculateObjectOffsets(mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void WalkInstanceFields(mirror::Object* obj, mirror::Class* klass) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void WalkFieldsInOrder(mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static void WalkFieldsCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Creates the contiguous image in memory and adjusts pointers. @@ -180,9 +167,6 @@ class ImageWriter { const CompilerDriver& compiler_driver_; - // Map of Object to where it will be at runtime. - SafeMap<const mirror::Object*, size_t> offsets_; - // oat file with code for this image OatFile* oat_file_; @@ -195,6 +179,9 @@ class ImageWriter { // Beginning target image address for the output image. byte* image_begin_; + // Saved hashes (objects are inside of the image so that they don't move). + std::vector<std::pair<mirror::Object*, uint32_t> > saved_hashes_; + // Beginning target oat address for the pointers from the output image to its oat file. const byte* oat_data_begin_; @@ -211,9 +198,6 @@ class ImageWriter { uint32_t quick_imt_conflict_trampoline_offset_; uint32_t quick_resolution_trampoline_offset_; uint32_t quick_to_interpreter_bridge_offset_; - - // DexCaches seen while scanning for fixing up CodeAndDirectMethods - std::set<mirror::DexCache*> dex_caches_; }; } // namespace art diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 667b913..21dd11e 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -48,9 +48,9 @@ class JniCompilerTest : public CommonTest { void CompileForTest(jobject class_loader, bool direct, const char* method_name, const char* method_sig) { ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(class_loader)); // Compile the native method before starting the runtime - mirror::Class* c = class_linker_->FindClass("LMyClassNatives;", - soa.Decode<mirror::ClassLoader*>(class_loader)); + mirror::Class* c = class_linker_->FindClass("LMyClassNatives;", loader); mirror::ArtMethod* method; if (direct) { method = c->FindDirectMethod(method_name, method_sig); diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 6213b45..c423f34 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -130,7 +130,8 @@ TEST_F(OatTest, WriteRead) { num_virtual_methods = it.NumVirtualMethods(); } const char* descriptor = dex_file->GetClassDescriptor(class_def); - mirror::Class* klass = class_linker->FindClass(descriptor, NULL); + SirtRef<mirror::ClassLoader> loader(Thread::Current(), nullptr); + mirror::Class* klass = class_linker->FindClass(descriptor, loader); UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(i)); CHECK_EQ(mirror::Class::Status::kStatusNotReady, oat_class->GetStatus()) << descriptor; diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index f3bb112..28fb147 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -405,23 +405,23 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); gc_map_offset = (gc_map_size == 0) ? 0 : offset; -#if !defined(NDEBUG) - // We expect GC maps except when the class hasn't been verified or the method is native - ClassReference class_ref(&dex_file, class_def_index); - CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); - mirror::Class::Status status; - if (compiled_class != NULL) { - status = compiled_class->GetStatus(); - } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) { - status = mirror::Class::kStatusError; - } else { - status = mirror::Class::kStatusNotReady; + if (kIsDebugBuild) { + // We expect GC maps except when the class hasn't been verified or the method is native + ClassReference class_ref(&dex_file, class_def_index); + CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref); + mirror::Class::Status status; + if (compiled_class != NULL) { + status = compiled_class->GetStatus(); + } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) { + status = mirror::Class::kStatusError; + } else { + status = mirror::Class::kStatusNotReady; + } + CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) + << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " + << (status < mirror::Class::kStatusVerified) << " " << status << " " + << PrettyMethod(method_idx, dex_file); } - CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) - << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " - << (status < mirror::Class::kStatusVerified) << " " << status << " " - << PrettyMethod(method_idx, dex_file); -#endif // Deduplicate GC maps SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter = @@ -448,11 +448,12 @@ size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index, if (compiler_driver_->IsImage()) { ClassLinker* linker = Runtime::Current()->GetClassLinker(); - mirror::DexCache* dex_cache = linker->FindDexCache(dex_file); // Unchecked as we hold mutator_lock_ on entry. ScopedObjectAccessUnchecked soa(Thread::Current()); + SirtRef<mirror::DexCache> dex_cache(soa.Self(), linker->FindDexCache(dex_file)); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr); mirror::ArtMethod* method = linker->ResolveMethod(dex_file, method_idx, dex_cache, - NULL, NULL, invoke_type); + class_loader, nullptr, invoke_type); CHECK(method != NULL); method->SetFrameSizeInBytes(frame_size_in_bytes); method->SetCoreSpillMask(core_spill_mask); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 90276c2..e219dd3 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -257,6 +257,9 @@ class OatDumper { os << "OAT DEX FILE:\n"; os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str()); os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum()); + + // Create the verifier early. + std::string error_msg; UniquePtr<const DexFile> dex_file(oat_dex_file.OpenDexFile(&error_msg)); if (dex_file.get() == NULL) { @@ -377,8 +380,20 @@ class OatDumper { oat_method.GetCode() != NULL ? "..." : ""); Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count); std::ostream indent2_os(&indent2_filter); - DumpCode(indent2_os, oat_method, dex_method_idx, &dex_file, class_def, code_item, - method_access_flags); + + Runtime* runtime = Runtime::Current(); + if (runtime != nullptr) { + ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::DexCache> dex_cache( + soa.Self(), runtime->GetClassLinker()->FindDexCache(dex_file)); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr); + verifier::MethodVerifier verifier(&dex_file, &dex_cache, &class_loader, &class_def, code_item, + dex_method_idx, nullptr, method_access_flags, true, true); + verifier.Verify(); + DumpCode(indent2_os, &verifier, oat_method, code_item); + } else { + DumpCode(indent2_os, nullptr, oat_method, code_item); + } } } @@ -566,24 +581,10 @@ class OatDumper { } } - void DumpVRegsAtDexPc(std::ostream& os, const OatFile::OatMethod& oat_method, - uint32_t dex_method_idx, const DexFile* dex_file, - const DexFile::ClassDef& class_def, const DexFile::CodeItem* code_item, - uint32_t method_access_flags, uint32_t dex_pc) { - static UniquePtr<verifier::MethodVerifier> verifier; - static const DexFile* verified_dex_file = NULL; - static uint32_t verified_dex_method_idx = DexFile::kDexNoIndex; - if (dex_file != verified_dex_file || verified_dex_method_idx != dex_method_idx) { - ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file); - mirror::ClassLoader* class_loader = NULL; - verifier.reset(new verifier::MethodVerifier(dex_file, dex_cache, class_loader, &class_def, - code_item, dex_method_idx, NULL, - method_access_flags, true, true)); - verifier->Verify(); - verified_dex_file = dex_file; - verified_dex_method_idx = dex_method_idx; - } + void DumpVRegsAtDexPc(std::ostream& os, verifier::MethodVerifier* verifier, + const OatFile::OatMethod& oat_method, + const DexFile::CodeItem* code_item, uint32_t dex_pc) { + DCHECK(verifier != nullptr); std::vector<int32_t> kinds = verifier->DescribeVRegs(dex_pc); bool first = true; for (size_t reg = 0; reg < code_item->registers_size_; reg++) { @@ -633,18 +634,16 @@ class OatDumper { uint32_t method_access_flags) { if ((method_access_flags & kAccNative) == 0) { ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file); - mirror::ClassLoader* class_loader = NULL; + SirtRef<mirror::DexCache> dex_cache(soa.Self(), Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file)); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr); verifier::MethodVerifier::VerifyMethodAndDump(os, dex_method_idx, dex_file, dex_cache, class_loader, &class_def, code_item, NULL, method_access_flags); } } - void DumpCode(std::ostream& os, const OatFile::OatMethod& oat_method, - uint32_t dex_method_idx, const DexFile* dex_file, - const DexFile::ClassDef& class_def, const DexFile::CodeItem* code_item, - uint32_t method_access_flags) { + void DumpCode(std::ostream& os, verifier::MethodVerifier* verifier, + const OatFile::OatMethod& oat_method, const DexFile::CodeItem* code_item) { const void* code = oat_method.GetCode(); size_t code_size = oat_method.GetCodeSize(); if (code == NULL || code_size == 0) { @@ -653,16 +652,14 @@ class OatDumper { } const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code); size_t offset = 0; - const bool kDumpVRegs = (Runtime::Current() != NULL); while (offset < code_size) { DumpMappingAtOffset(os, oat_method, offset, false); offset += disassembler_->Dump(os, native_pc + offset); uint32_t dex_pc = DumpMappingAtOffset(os, oat_method, offset, true); if (dex_pc != DexFile::kDexNoIndex) { DumpGcMapAtNativePcOffset(os, oat_method, code_item, offset); - if (kDumpVRegs) { - DumpVRegsAtDexPc(os, oat_method, dex_method_idx, dex_file, class_def, code_item, - method_access_flags, dex_pc); + if (verifier != nullptr) { + DumpVRegsAtDexPc(os, verifier, oat_method, code_item, dex_pc); } } } diff --git a/runtime/Android.mk b/runtime/Android.mk index bef4381..97cbdd9 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -50,8 +50,10 @@ LIBART_COMMON_SRC_FILES := \ gc/collector/garbage_collector.cc \ gc/collector/mark_sweep.cc \ gc/collector/partial_mark_sweep.cc \ + gc/collector/semi_space.cc \ gc/collector/sticky_mark_sweep.cc \ gc/heap.cc \ + gc/space/bump_pointer_space.cc \ gc/space/dlmalloc_space.cc \ gc/space/image_space.cc \ gc/space/large_object_space.cc \ diff --git a/runtime/arch/alloc_entrypoints.S b/runtime/arch/alloc_entrypoints.S new file mode 100644 index 0000000..840f3c6 --- /dev/null +++ b/runtime/arch/alloc_entrypoints.S @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Called by managed code to allocate an object */ +TWO_ARG_DOWNCALL art_quick_alloc_object, artAllocObjectFromCode, RETURN_IF_RESULT_IS_NON_ZERO +TWO_ARG_DOWNCALL art_quick_alloc_object_instrumented, artAllocObjectFromCodeInstrumented, RETURN_IF_RESULT_IS_NON_ZERO +/* Called by managed code to allocate an object when the caller doesn't know whether it has access + * to the created type. */ +TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check, artAllocObjectFromCodeWithAccessCheck, RETURN_IF_RESULT_IS_NON_ZERO +TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check_instrumented, artAllocObjectFromCodeWithAccessCheckInstrumented, RETURN_IF_RESULT_IS_NON_ZERO +/* Called by managed code to allocate an array. */ +THREE_ARG_DOWNCALL art_quick_alloc_array, artAllocArrayFromCode, RETURN_IF_RESULT_IS_NON_ZERO +THREE_ARG_DOWNCALL art_quick_alloc_array_instrumented, artAllocArrayFromCodeInstrumented, RETURN_IF_RESULT_IS_NON_ZERO +/* Called by managed code to allocate an array when the caller doesn't know whether it has access + * to the created type. */ +THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check, artAllocArrayFromCodeWithAccessCheck, RETURN_IF_RESULT_IS_NON_ZERO +THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check_instrumented, artAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_RESULT_IS_NON_ZERO +/* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. */ +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array, artCheckAndAllocArrayFromCode, RETURN_IF_RESULT_IS_NON_ZERO +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_instrumented, artCheckAndAllocArrayFromCodeInstrumented, RETURN_IF_RESULT_IS_NON_ZERO +/* Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. */ +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check, artCheckAndAllocArrayFromCodeWithAccessCheck +THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check_instrumented, artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 1a058ea..dbfb93a 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -830,205 +830,41 @@ ENTRY art_quick_resolve_string DELIVER_PENDING_EXCEPTION END art_quick_resolve_string - /* - * Called by managed code to allocate an object - */ - .extern artAllocObjectFromCode -ENTRY art_quick_alloc_object - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl artAllocObjectFromCode @ (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_object - - .extern artAllocObjectFromCodeInstrumented -ENTRY art_quick_alloc_object_instrumented +// Macro to facilitate adding new allocation entrypoints. +.macro TWO_ARG_DOWNCALL name, entrypoint, return + .extern \entrypoint +ENTRY \name SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC mov r2, r9 @ pass Thread::Current mov r3, sp @ pass SP - bl artAllocObjectFromCodeInstrumented @ (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_object_instrumented - - /* - * Called by managed code to allocate an object when the caller doesn't know whether it has - * access to the created type. - */ - .extern artAllocObjectFromCodeWithAccessCheck -ENTRY art_quick_alloc_object_with_access_check - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl artAllocObjectFromCodeWithAccessCheck @ (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_object_with_access_check - - .extern artAllocObjectFromCodeWithAccessCheckInstrumented -ENTRY art_quick_alloc_object_with_access_check_instrumented - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r2, r9 @ pass Thread::Current - mov r3, sp @ pass SP - bl artAllocObjectFromCodeWithAccessCheckInstrumented @ (uint32_t type_idx, Method* method, Thread*, SP) - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_object_with_access_check_instrumented - - /* - * Called by managed code to allocate an array. - */ - .extern artAllocArrayFromCode -ENTRY art_quick_alloc_array - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP) - bl artAllocArrayFromCode - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 + bl \entrypoint @ (uint32_t type_idx, Method* method, Thread*, SP) RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO + \return DELIVER_PENDING_EXCEPTION -END art_quick_alloc_array - - .extern artAllocArrayFromCodeInstrumented -ENTRY art_quick_alloc_array_instrumented - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP) - bl artAllocArrayFromCodeInstrumented - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_array_instrumented - - /* - * Called by managed code to allocate an array when the caller doesn't know whether it has - * access to the created type. - */ - .extern artAllocArrayFromCodeWithAccessCheck -ENTRY art_quick_alloc_array_with_access_check - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artAllocArrayFromCodeWithAccessCheck(type_idx, method, component_count, Thread*, SP) - bl artAllocArrayFromCodeWithAccessCheck - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_array_with_access_check - - .extern artAllocArrayFromCodeWithAccessCheckInstrumented -ENTRY art_quick_alloc_array_with_access_check_instrumented - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, component_count, Thread*, SP) - bl artAllocArrayFromCodeWithAccessCheckInstrumented - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_alloc_array_with_access_check_instrumented - - /* - * Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. - */ - .extern artCheckAndAllocArrayFromCode -ENTRY art_quick_check_and_alloc_array - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artCheckAndAllocArrayFromCode(uint32_t type_idx, Method* method, int32_t count, Thread* , SP) - bl artCheckAndAllocArrayFromCode - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_check_and_alloc_array - - .extern artCheckAndAllocArrayFromCodeInstrumented -ENTRY art_quick_check_and_alloc_array_instrumented - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artCheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, Method* method, int32_t count, Thread* , SP) - bl artCheckAndAllocArrayFromCodeInstrumented - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_check_and_alloc_array_instrumented +END \name +.endm - /* - * Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY. - */ - .extern artCheckAndAllocArrayFromCodeWithAccessCheck -ENTRY art_quick_check_and_alloc_array_with_access_check +// Macro to facilitate adding new array allocation entrypoints. +.macro THREE_ARG_DOWNCALL name, entrypoint, return + .extern \entrypoint +ENTRY \name SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC mov r3, r9 @ pass Thread::Current mov r12, sp str r12, [sp, #-16]! @ expand the frame and pass SP .pad #16 .cfi_adjust_cfa_offset 16 - @ artCheckAndAllocArrayFromCodeWithAccessCheck(type_idx, method, count, Thread* , SP) - bl artCheckAndAllocArrayFromCodeWithAccessCheck + @ (uint32_t type_idx, Method* method, int32_t component_count, Thread*, SP) + bl \entrypoint add sp, #16 @ strip the extra frame .cfi_adjust_cfa_offset -16 RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO + \return DELIVER_PENDING_EXCEPTION -END art_quick_check_and_alloc_array_with_access_check +END \name +.endm - .extern artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented -ENTRY art_quick_check_and_alloc_array_with_access_check_instrumented - SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC - mov r3, r9 @ pass Thread::Current - mov r12, sp - str r12, [sp, #-16]! @ expand the frame and pass SP - .pad #16 - .cfi_adjust_cfa_offset 16 - @ artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented(type_idx, method, count, Thread* , SP) - bl artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented - add sp, #16 @ strip the extra frame - .cfi_adjust_cfa_offset -16 - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME - RETURN_IF_RESULT_IS_NON_ZERO - DELIVER_PENDING_EXCEPTION -END art_quick_check_and_alloc_array_with_access_check_instrumented +#include "arch/alloc_entrypoints.S" /* * Called by managed code when the value in rSUSPEND has been decremented to 0. @@ -1107,11 +943,10 @@ ENTRY art_quick_to_interpreter_bridge ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ add sp, #16 @ skip r1-r3, 4 bytes padding. .cfi_adjust_cfa_offset -16 - cbnz r2, 1f @ success if no exception is pending RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + cbnz r2, 1f @ success if no exception is pending bx lr @ return on success 1: - RESTORE_REF_ONLY_CALLEE_SAVE_FRAME DELIVER_PENDING_EXCEPTION END art_quick_to_interpreter_bridge diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index ee78d45..decdb50 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -401,7 +401,7 @@ MACRO3(THREE_ARG_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name, 0) END_MACRO -MACRO0(RETURN_IF_EAX_NOT_ZERO) +MACRO0(RETURN_IF_RESULT_IS_NON_ZERO) testl %eax, %eax // eax == 0 ? jz 1f // if eax == 0 goto 1 ret // return @@ -426,24 +426,12 @@ MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION) DELIVER_PENDING_EXCEPTION END_MACRO -TWO_ARG_DOWNCALL art_quick_alloc_object, artAllocObjectFromCode, RETURN_IF_EAX_NOT_ZERO -TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check, artAllocObjectFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_alloc_array, artAllocArrayFromCode, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check, artAllocArrayFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array, artCheckAndAllocArrayFromCode, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check, artCheckAndAllocArrayFromCodeWithAccessCheck, RETURN_IF_EAX_NOT_ZERO - -TWO_ARG_DOWNCALL art_quick_alloc_object_instrumented, artAllocObjectFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO -TWO_ARG_DOWNCALL art_quick_alloc_object_with_access_check_instrumented, artAllocObjectFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_alloc_array_instrumented, artAllocArrayFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check_instrumented, artAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_instrumented, artCheckAndAllocArrayFromCodeInstrumented, RETURN_IF_EAX_NOT_ZERO -THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check_instrumented, artCheckAndAllocArrayFromCodeWithAccessCheckInstrumented, RETURN_IF_EAX_NOT_ZERO - -TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_EAX_NOT_ZERO -TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_EAX_NOT_ZERO -TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_EAX_NOT_ZERO -TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_EAX_NOT_ZERO +#include "arch/alloc_entrypoints.S" + +TWO_ARG_DOWNCALL art_quick_resolve_string, artResolveStringFromCode, RETURN_IF_RESULT_IS_NON_ZERO +TWO_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO +TWO_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO +TWO_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO TWO_ARG_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h index c0cfee2..29b3981 100644 --- a/runtime/base/mutex-inl.h +++ b/runtime/base/mutex-inl.h @@ -130,7 +130,7 @@ static inline void CheckUnattachedThread(LockLevel level) NO_THREAD_SAFETY_ANALY // TODO: tighten this check. if (kDebugLocking) { Runtime* runtime = Runtime::Current(); - CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDown() || + CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDownLocked() || level == kDefaultMutexLevel || level == kRuntimeShutdownLock || level == kThreadListLock || level == kLoggingLock || level == kAbortLock); } diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 249f031..1c7d744 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -266,9 +266,8 @@ Mutex::Mutex(const char* name, LockLevel level, bool recursive) Mutex::~Mutex() { #if ART_USE_FUTEXES if (state_ != 0) { - MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown(); + bool shutting_down = runtime == nullptr || runtime->IsShuttingDown(Thread::Current()); LOG(shutting_down ? WARNING : FATAL) << "destroying mutex with owner: " << exclusive_owner_; } else { CHECK_EQ(exclusive_owner_, 0U) << "unexpectedly found an owner on unlocked mutex " << name_; @@ -641,9 +640,8 @@ ConditionVariable::ConditionVariable(const char* name, Mutex& guard) ConditionVariable::~ConditionVariable() { #if ART_USE_FUTEXES if (num_waiters_!= 0) { - MutexLock mu(Thread::Current(), *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown(); + bool shutting_down = runtime == nullptr || runtime->IsShuttingDown(Thread::Current()); LOG(shutting_down ? WARNING : FATAL) << "ConditionVariable::~ConditionVariable for " << name_ << " called with " << num_waiters_ << " waiters."; } diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index feb8a6c..a875017 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -329,6 +329,11 @@ class ConditionVariable { // TODO: remove this. void WaitHoldingLocks(Thread* self) NO_THREAD_SAFETY_ANALYSIS; + // Return the number of people that are waiting on this condition. + int32_t GetNumWaiters() const NO_THREAD_SAFETY_ANALYSIS { + return num_waiters_; + } + private: const char* const name_; // The Mutex being used by waiters. It is an error to mix condition variables between different diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc index 6df1126..45a546f 100644 --- a/runtime/base/timing_logger.cc +++ b/runtime/base/timing_logger.cc @@ -86,6 +86,11 @@ void CumulativeLogger::AddLogger(const base::TimingLogger &logger) { } } +size_t CumulativeLogger::GetIterations() const { + MutexLock mu(Thread::Current(), lock_); + return iterations_; +} + void CumulativeLogger::Dump(std::ostream &os) { MutexLock mu(Thread::Current(), lock_); DumpHistogram(os); diff --git a/runtime/base/timing_logger.h b/runtime/base/timing_logger.h index 07d1ee0..501d2d7 100644 --- a/runtime/base/timing_logger.h +++ b/runtime/base/timing_logger.h @@ -45,6 +45,7 @@ class CumulativeLogger { // parent class that is unable to determine the "name" of a sub-class. void SetName(const std::string& name); void AddLogger(const base::TimingLogger& logger) LOCKS_EXCLUDED(lock_); + size_t GetIterations() const; private: typedef std::map<std::string, Histogram<uint64_t> *> Histograms; diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc index 54cbfe6..a84e18a 100644 --- a/runtime/check_jni.cc +++ b/runtime/check_jni.cc @@ -205,7 +205,7 @@ class ScopedCheck { // If java_object is a weak global ref whose referent has been cleared, // obj will be NULL. Otherwise, obj should always be non-NULL // and valid. - if (!Runtime::Current()->GetHeap()->IsHeapAddress(obj)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "field operation on invalid %s: %p", ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object); @@ -242,7 +242,7 @@ class ScopedCheck { void CheckInstanceFieldID(jobject java_object, jfieldID fid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::Object* o = soa_.Decode<mirror::Object*>(java_object); - if (o == NULL || !Runtime::Current()->GetHeap()->IsHeapAddress(o)) { + if (o == NULL || !Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "field operation on invalid %s: %p", ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object); @@ -455,7 +455,8 @@ class ScopedCheck { mirror::Class* c = reinterpret_cast<mirror::Class*>(Thread::Current()->DecodeJObject(jc)); if (c == NULL) { msg += "NULL"; - } else if (c == kInvalidIndirectRefObject || !Runtime::Current()->GetHeap()->IsHeapAddress(c)) { + } else if (c == kInvalidIndirectRefObject || + !Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) { StringAppendF(&msg, "INVALID POINTER:%p", jc); } else if (!c->IsClass()) { msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c); @@ -621,7 +622,7 @@ class ScopedCheck { } mirror::Object* obj = soa_.Decode<mirror::Object*>(java_object); - if (!Runtime::Current()->GetHeap()->IsHeapAddress(obj)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "%s is an invalid %s: %p (%p)", what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(), java_object, obj); @@ -675,7 +676,7 @@ class ScopedCheck { } mirror::Array* a = soa_.Decode<mirror::Array*>(java_array); - if (!Runtime::Current()->GetHeap()->IsHeapAddress(a)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(a)) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "jarray is an invalid %s: %p (%p)", ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(), java_array, a); @@ -696,7 +697,7 @@ class ScopedCheck { return NULL; } mirror::ArtField* f = soa_.DecodeField(fid); - if (!Runtime::Current()->GetHeap()->IsHeapAddress(f) || !f->IsArtField()) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f) || !f->IsArtField()) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "invalid jfieldID: %p", fid); return NULL; @@ -710,7 +711,7 @@ class ScopedCheck { return NULL; } mirror::ArtMethod* m = soa_.DecodeMethod(mid); - if (!Runtime::Current()->GetHeap()->IsHeapAddress(m) || !m->IsArtMethod()) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(m) || !m->IsArtMethod()) { Runtime::Current()->GetHeap()->DumpSpaces(); JniAbortF(function_name_, "invalid jmethodID: %p", mid); return NULL; @@ -731,7 +732,7 @@ class ScopedCheck { } mirror::Object* o = soa_.Decode<mirror::Object*>(java_object); - if (!Runtime::Current()->GetHeap()->IsHeapAddress(o)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { Runtime::Current()->GetHeap()->DumpSpaces(); // TODO: when we remove work_around_app_jni_bugs, this should be impossible. JniAbortF(function_name_, "native code passing in reference to invalid %s: %p", diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h index ad568b1..0436435 100644 --- a/runtime/class_linker-inl.h +++ b/runtime/class_linker-inl.h @@ -18,20 +18,21 @@ #define ART_RUNTIME_CLASS_LINKER_INL_H_ #include "class_linker.h" - #include "mirror/art_field.h" +#include "mirror/class_loader.h" #include "mirror/dex_cache.h" #include "mirror/iftable.h" #include "mirror/object_array.h" +#include "sirt_ref.h" namespace art { inline mirror::String* ClassLinker::ResolveString(uint32_t string_idx, - const mirror::ArtMethod* referrer) { + const mirror::ArtMethod* referrer) { mirror::String* resolved_string = referrer->GetDexCacheStrings()->Get(string_idx); if (UNLIKELY(resolved_string == NULL)) { mirror::Class* declaring_class = referrer->GetDeclaringClass(); - mirror::DexCache* dex_cache = declaring_class->GetDexCache(); + SirtRef<mirror::DexCache> dex_cache(Thread::Current(), declaring_class->GetDexCache()); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_string = ResolveString(dex_file, string_idx, dex_cache); } @@ -43,8 +44,9 @@ inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, mirror::Class* resolved_type = referrer->GetDexCacheResolvedTypes()->Get(type_idx); if (UNLIKELY(resolved_type == NULL)) { mirror::Class* declaring_class = referrer->GetDeclaringClass(); - mirror::DexCache* dex_cache = declaring_class->GetDexCache(); - mirror::ClassLoader* class_loader = declaring_class->GetClassLoader(); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, declaring_class->GetDexCache()); + SirtRef<mirror::ClassLoader> class_loader(self, declaring_class->GetClassLoader()); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); } @@ -53,10 +55,12 @@ inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, inline mirror::Class* ClassLinker::ResolveType(uint16_t type_idx, const mirror::ArtField* referrer) { mirror::Class* declaring_class = referrer->GetDeclaringClass(); - mirror::DexCache* dex_cache = declaring_class->GetDexCache(); - mirror::Class* resolved_type = dex_cache->GetResolvedType(type_idx); + mirror::DexCache* dex_cache_ptr = declaring_class->GetDexCache(); + mirror::Class* resolved_type = dex_cache_ptr->GetResolvedType(type_idx); if (UNLIKELY(resolved_type == NULL)) { - mirror::ClassLoader* class_loader = declaring_class->GetClassLoader(); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, dex_cache_ptr); + SirtRef<mirror::ClassLoader> class_loader(self, declaring_class->GetClassLoader()); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader); } @@ -70,8 +74,9 @@ inline mirror::ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, referrer->GetDexCacheResolvedMethods()->Get(method_idx); if (UNLIKELY(resolved_method == NULL || resolved_method->IsRuntimeMethod())) { mirror::Class* declaring_class = referrer->GetDeclaringClass(); - mirror::DexCache* dex_cache = declaring_class->GetDexCache(); - mirror::ClassLoader* class_loader = declaring_class->GetClassLoader(); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, declaring_class->GetDexCache()); + SirtRef<mirror::ClassLoader> class_loader(self, declaring_class->GetClassLoader()); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_method = ResolveMethod(dex_file, method_idx, dex_cache, class_loader, referrer, type); } @@ -81,12 +86,13 @@ inline mirror::ArtMethod* ClassLinker::ResolveMethod(uint32_t method_idx, inline mirror::ArtField* ClassLinker::ResolveField(uint32_t field_idx, const mirror::ArtMethod* referrer, bool is_static) { + mirror::Class* declaring_class = referrer->GetDeclaringClass(); mirror::ArtField* resolved_field = - referrer->GetDeclaringClass()->GetDexCache()->GetResolvedField(field_idx); + declaring_class->GetDexCache()->GetResolvedField(field_idx); if (UNLIKELY(resolved_field == NULL)) { - mirror::Class* declaring_class = referrer->GetDeclaringClass(); - mirror::DexCache* dex_cache = declaring_class->GetDexCache(); - mirror::ClassLoader* class_loader = declaring_class->GetClassLoader(); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, declaring_class->GetDexCache()); + SirtRef<mirror::ClassLoader> class_loader(self, declaring_class->GetClassLoader()); const DexFile& dex_file = *dex_cache->GetDexFile(); resolved_field = ResolveField(dex_file, field_idx, dex_cache, class_loader, is_static); } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 184e5d4..cfe3bf4 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -170,20 +170,6 @@ const char* ClassLinker::class_roots_descriptors_[] = { "[Ljava/lang/StackTraceElement;", }; -ClassLinker* ClassLinker::CreateFromCompiler(const std::vector<const DexFile*>& boot_class_path, - InternTable* intern_table) { - CHECK_NE(boot_class_path.size(), 0U); - UniquePtr<ClassLinker> class_linker(new ClassLinker(intern_table)); - class_linker->InitFromCompiler(boot_class_path); - return class_linker.release(); -} - -ClassLinker* ClassLinker::CreateFromImage(InternTable* intern_table) { - UniquePtr<ClassLinker> class_linker(new ClassLinker(intern_table)); - class_linker->InitFromImage(); - return class_linker.release(); -} - ClassLinker::ClassLinker(InternTable* intern_table) // dex_lock_ is recursive as it may be used in stack dumping. : dex_lock_("ClassLinker dex lock", kDefaultMutexLevel), @@ -211,14 +197,16 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class // java_lang_Class comes first, it's needed for AllocClass Thread* self = Thread::Current(); gc::Heap* heap = Runtime::Current()->GetHeap(); - SirtRef<mirror::Class> - java_lang_Class(self, - down_cast<mirror::Class*>(heap->AllocObject(self, NULL, - sizeof(mirror::ClassClass)))); + // The GC can't handle an object with a null class since we can't get the size of this object. + heap->IncrementDisableGC(self); + SirtRef<mirror::Class> java_lang_Class( + self, down_cast<mirror::Class*>( + heap->AllocNonMovableObject(self, NULL, sizeof(mirror::ClassClass)))); CHECK(java_lang_Class.get() != NULL); mirror::Class::SetClassClass(java_lang_Class.get()); java_lang_Class->SetClass(java_lang_Class.get()); java_lang_Class->SetClassSize(sizeof(mirror::ClassClass)); + heap->DecrementDisableGC(self); // AllocClass(mirror::Class*) can now be used // Class[] is used for reflection support. @@ -401,7 +389,7 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class array_iftable_->SetInterface(1, java_io_Serializable); // Sanity check Class[] and Object[]'s interfaces. - ClassHelper kh(class_array_class.get(), this); + ClassHelper kh(class_array_class.get()); CHECK_EQ(java_lang_Cloneable, kh.GetDirectInterface(0)); CHECK_EQ(java_io_Serializable, kh.GetDirectInterface(1)); kh.ChangeClass(object_array_class.get()); @@ -487,7 +475,7 @@ void ClassLinker::FinishInit() { FindSystemClass("Ljava/lang/ref/FinalizerReference;"); mirror::ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0); - FieldHelper fh(pendingNext, this); + FieldHelper fh(pendingNext); CHECK_STREQ(fh.GetName(), "pendingNext"); CHECK_STREQ(fh.GetTypeDescriptor(), "Ljava/lang/ref/Reference;"); @@ -1043,6 +1031,7 @@ void ClassLinker::InitFromImage() { VLOG(startup) << "ClassLinker::InitFromImage entering"; CHECK(!init_done_); + Thread* self = Thread::Current(); gc::Heap* heap = Runtime::Current()->GetHeap(); gc::space::ImageSpace* space = heap->GetImageSpace(); dex_cache_image_class_lookup_required_ = true; @@ -1059,9 +1048,10 @@ void ClassLinker::InitFromImage() { mirror::ObjectArray<mirror::DexCache>* dex_caches = dex_caches_object->AsObjectArray<mirror::DexCache>(); - mirror::ObjectArray<mirror::Class>* class_roots = - space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>(); - class_roots_ = class_roots; + SirtRef<mirror::ObjectArray<mirror::Class> > class_roots( + self, + space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()); + class_roots_ = class_roots.get(); // Special case of setting up the String class early so that we can test arbitrary objects // as being Strings or not @@ -1069,7 +1059,6 @@ void ClassLinker::InitFromImage() { CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(), static_cast<uint32_t>(dex_caches->GetLength())); - Thread* self = Thread::Current(); for (int32_t i = 0; i < dex_caches->GetLength(); i++) { SirtRef<mirror::DexCache> dex_cache(self, dex_caches->Get(i)); const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); @@ -1096,13 +1085,12 @@ void ClassLinker::InitFromImage() { // Set entry point to interpreter if in InterpretOnly mode. if (Runtime::Current()->GetInstrumentation()->InterpretOnly()) { ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - heap->FlushAllocStack(); - heap->GetLiveBitmap()->Walk(InitFromImageInterpretOnlyCallback, this); + heap->VisitObjects(InitFromImageInterpretOnlyCallback, this); } // reinit class_roots_ mirror::Class::SetClassClass(class_roots->Get(kJavaLangClass)); - class_roots_ = class_roots; + class_roots_ = class_roots.get(); // reinit array_iftable_ from any array class instance, they should be == array_iftable_ = GetClassRoot(kObjectArrayClass)->GetIfTable(); @@ -1192,7 +1180,6 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* ar } } - ClassLinker::~ClassLinker() { mirror::Class::ResetClass(); mirror::String::ResetClass(); @@ -1214,10 +1201,10 @@ ClassLinker::~ClassLinker() { mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) { gc::Heap* heap = Runtime::Current()->GetHeap(); - mirror::Class* dex_cache_class = GetClassRoot(kJavaLangDexCache); - SirtRef<mirror::DexCache> dex_cache(self, - down_cast<mirror::DexCache*>(heap->AllocObject(self, dex_cache_class, - dex_cache_class->GetObjectSize()))); + SirtRef<mirror::Class> dex_cache_class(self, GetClassRoot(kJavaLangDexCache)); + SirtRef<mirror::DexCache> dex_cache( + self, down_cast<mirror::DexCache*>( + heap->AllocObject(self, dex_cache_class.get(), dex_cache_class->GetObjectSize()))); if (dex_cache.get() == NULL) { return NULL; } @@ -1253,13 +1240,8 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_fi return NULL; } - dex_cache->Init(&dex_file, - location.get(), - strings.get(), - types.get(), - methods.get(), - fields.get(), - initialized_static_storage.get()); + dex_cache->Init(&dex_file, location.get(), strings.get(), types.get(), methods.get(), + fields.get(), initialized_static_storage.get()); return dex_cache.get(); } @@ -1267,7 +1249,7 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Cl size_t class_size) { DCHECK_GE(class_size, sizeof(mirror::Class)); gc::Heap* heap = Runtime::Current()->GetHeap(); - mirror::Object* k = heap->AllocObject(self, java_lang_Class, class_size); + mirror::Object* k = heap->AllocNonMovableObject(self, java_lang_Class, class_size); if (UNLIKELY(k == NULL)) { CHECK(self->IsExceptionPending()); // OOME. return NULL; @@ -1285,18 +1267,19 @@ mirror::Class* ClassLinker::AllocClass(Thread* self, size_t class_size) { } mirror::ArtField* ClassLinker::AllocArtField(Thread* self) { - return down_cast<mirror::ArtField*>(GetClassRoot(kJavaLangReflectArtField)->AllocObject(self)); + return down_cast<mirror::ArtField*>( + GetClassRoot(kJavaLangReflectArtField)->Alloc<false, true>(self)); } mirror::ArtMethod* ClassLinker::AllocArtMethod(Thread* self) { - return down_cast<mirror::ArtMethod*>(GetClassRoot(kJavaLangReflectArtMethod)->AllocObject(self)); + return down_cast<mirror::ArtMethod*>( + GetClassRoot(kJavaLangReflectArtMethod)->Alloc<false, true>(self)); } -mirror::ObjectArray<mirror::StackTraceElement>* ClassLinker::AllocStackTraceElementArray(Thread* self, - size_t length) { - return mirror::ObjectArray<mirror::StackTraceElement>::Alloc(self, - GetClassRoot(kJavaLangStackTraceElementArrayClass), - length); +mirror::ObjectArray<mirror::StackTraceElement>* ClassLinker::AllocStackTraceElementArray( + Thread* self, size_t length) { + return mirror::ObjectArray<mirror::StackTraceElement>::Alloc( + self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length); } static mirror::Class* EnsureResolved(Thread* self, mirror::Class* klass) @@ -1332,10 +1315,12 @@ bool ClassLinker::IsInBootClassPath(const char* descriptor) { } mirror::Class* ClassLinker::FindSystemClass(const char* descriptor) { - return FindClass(descriptor, NULL); + SirtRef<mirror::ClassLoader> class_loader(Thread::Current(), nullptr); + return FindClass(descriptor, class_loader); } -mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoader* class_loader) { +mirror::Class* ClassLinker::FindClass(const char* descriptor, + SirtRef<mirror::ClassLoader>& class_loader) { DCHECK_NE(*descriptor, '\0') << "descriptor is empty string"; Thread* self = Thread::Current(); DCHECK(self != NULL); @@ -1346,20 +1331,19 @@ mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoade return FindPrimitiveClass(descriptor[0]); } // Find the class in the loaded classes table. - mirror::Class* klass = LookupClass(descriptor, class_loader); + mirror::Class* klass = LookupClass(descriptor, class_loader.get()); if (klass != NULL) { return EnsureResolved(self, klass); } // Class is not yet loaded. if (descriptor[0] == '[') { return CreateArrayClass(descriptor, class_loader); - - } else if (class_loader == NULL) { + } else if (class_loader.get() == nullptr) { DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_); if (pair.second != NULL) { - return DefineClass(descriptor, NULL, *pair.first, *pair.second); + SirtRef<mirror::ClassLoader> class_loader(self, nullptr); + return DefineClass(descriptor, class_loader, *pair.first, *pair.second); } - } else if (Runtime::Current()->UseCompileTimeClassPath()) { // First try the boot class path, we check the descriptor first to avoid an unnecessary // throw of a NoClassDefFoundError. @@ -1372,7 +1356,8 @@ mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoade const std::vector<const DexFile*>* class_path; { ScopedObjectAccessUnchecked soa(self); - ScopedLocalRef<jobject> jclass_loader(soa.Env(), soa.AddLocalReference<jobject>(class_loader)); + ScopedLocalRef<jobject> jclass_loader(soa.Env(), + soa.AddLocalReference<jobject>(class_loader.get())); class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get()); } @@ -1384,7 +1369,7 @@ mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoade } else { ScopedObjectAccessUnchecked soa(self->GetJniEnv()); ScopedLocalRef<jobject> class_loader_object(soa.Env(), - soa.AddLocalReference<jobject>(class_loader)); + soa.AddLocalReference<jobject>(class_loader.get())); std::string class_name_string(DescriptorToDot(descriptor)); ScopedLocalRef<jobject> result(soa.Env(), NULL); { @@ -1418,7 +1403,7 @@ mirror::Class* ClassLinker::FindClass(const char* descriptor, mirror::ClassLoade } mirror::Class* ClassLinker::DefineClass(const char* descriptor, - mirror::ClassLoader* class_loader, + SirtRef<mirror::ClassLoader>& class_loader, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) { Thread* self = Thread::Current(); @@ -1449,7 +1434,7 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, return NULL; } klass->SetDexCache(FindDexCache(dex_file)); - LoadClass(dex_file, dex_class_def, klass, class_loader); + LoadClass(dex_file, dex_class_def, klass, class_loader.get()); // Check for a pending exception during load if (self->IsExceptionPending()) { klass->SetStatus(mirror::Class::kStatusError, self); @@ -1457,14 +1442,12 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, } ObjectLock lock(self, klass.get()); klass->SetClinitThreadId(self->GetTid()); - { - // Add the newly loaded class to the loaded classes table. - mirror::Class* existing = InsertClass(descriptor, klass.get(), Hash(descriptor)); - if (existing != NULL) { - // We failed to insert because we raced with another thread. Calling EnsureResolved may cause - // this thread to block. - return EnsureResolved(self, existing); - } + // Add the newly loaded class to the loaded classes table. + mirror::Class* existing = InsertClass(descriptor, klass.get(), Hash(descriptor)); + if (existing != NULL) { + // We failed to insert because we raced with another thread. Calling EnsureResolved may cause + // this thread to block. + return EnsureResolved(self, existing); } // Finish loading (if necessary) by finding parents CHECK(!klass->IsLoaded()); @@ -1476,7 +1459,9 @@ mirror::Class* ClassLinker::DefineClass(const char* descriptor, CHECK(klass->IsLoaded()); // Link the class (if necessary) CHECK(!klass->IsResolved()); - if (!LinkClass(klass, NULL, self)) { + // TODO: Use fast jobjects? + SirtRef<mirror::ObjectArray<mirror::Class> > interfaces(self, nullptr); + if (!LinkClass(self, klass, interfaces)) { // Linking failed. klass->SetStatus(mirror::Class::kStatusError, self); return NULL; @@ -2083,7 +2068,7 @@ mirror::Class* ClassLinker::InitializePrimitiveClass(mirror::Class* primitive_cl // // Returns NULL with an exception raised on failure. mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor, - mirror::ClassLoader* class_loader) { + SirtRef<mirror::ClassLoader>& class_loader) { // Identify the underlying component type CHECK_EQ('[', descriptor[0]); mirror::Class* component_type = FindClass(descriptor + 1, class_loader); @@ -2109,7 +2094,7 @@ mirror::Class* ClassLinker::CreateArrayClass(const char* descriptor, // because we effectively do this lookup again when we add the new // class to the hash table --- necessary because of possible races with // other threads.) - if (class_loader != component_type->GetClassLoader()) { + if (class_loader.get() != component_type->GetClassLoader()) { mirror::Class* new_class = LookupClass(descriptor, component_type->GetClassLoader()); if (new_class != NULL) { return new_class; @@ -2266,11 +2251,10 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k bool ClassLinker::RemoveClass(const char* descriptor, const mirror::ClassLoader* class_loader) { size_t hash = Hash(descriptor); WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - ClassHelper kh; for (auto it = class_table_.lower_bound(hash), end = class_table_.end(); it != end && it->first == hash; ++it) { mirror::Class* klass = it->second; - kh.ChangeClass(klass); + ClassHelper kh(klass); if ((klass->GetClassLoader() == class_loader) && (strcmp(descriptor, kh.GetDescriptor()) == 0)) { class_table_.erase(it); @@ -2313,18 +2297,17 @@ mirror::Class* ClassLinker::LookupClass(const char* descriptor, mirror::Class* ClassLinker::LookupClassFromTableLocked(const char* descriptor, const mirror::ClassLoader* class_loader, size_t hash) { - ClassHelper kh(NULL, this); auto end = class_table_.end(); for (auto it = class_table_.lower_bound(hash); it != end && it->first == hash; ++it) { mirror::Class* klass = it->second; - kh.ChangeClass(klass); + ClassHelper kh(klass); if ((klass->GetClassLoader() == class_loader) && (strcmp(descriptor, kh.GetDescriptor()) == 0)) { if (kIsDebugBuild) { // Check for duplicates in the table. for (++it; it != end && it->first == hash; ++it) { mirror::Class* klass2 = it->second; - kh.ChangeClass(klass2); + ClassHelper kh(klass2); CHECK(!((klass2->GetClassLoader() == class_loader) && (strcmp(descriptor, kh.GetDescriptor()) == 0))) << PrettyClass(klass) << " " << klass << " " << klass->GetClassLoader() << " " @@ -2354,14 +2337,13 @@ void ClassLinker::MoveImageClassesToClassTable() { const char* old_no_suspend_cause = self->StartAssertNoThreadSuspension("Moving image classes to class table"); mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(); - ClassHelper kh(NULL, this); for (int32_t i = 0; i < dex_caches->GetLength(); i++) { mirror::DexCache* dex_cache = dex_caches->Get(i); mirror::ObjectArray<mirror::Class>* types = dex_cache->GetResolvedTypes(); for (int32_t j = 0; j < types->GetLength(); j++) { mirror::Class* klass = types->Get(j); if (klass != NULL) { - kh.ChangeClass(klass); + ClassHelper kh(klass); DCHECK(klass->GetClassLoader() == NULL); const char* descriptor = kh.GetDescriptor(); size_t hash = Hash(descriptor); @@ -2429,11 +2411,10 @@ void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Clas } size_t hash = Hash(descriptor); ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); - ClassHelper kh(NULL, this); for (auto it = class_table_.lower_bound(hash), end = class_table_.end(); it != end && it->first == hash; ++it) { mirror::Class* klass = it->second; - kh.ChangeClass(klass); + ClassHelper kh(klass); if (strcmp(descriptor, kh.GetDescriptor()) == 0) { result.push_back(klass); } @@ -2687,12 +2668,10 @@ static void CheckProxyConstructor(mirror::ArtMethod* constructor); static void CheckProxyMethod(mirror::ArtMethod* method, SirtRef<mirror::ArtMethod>& prototype); -mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, - mirror::ObjectArray<mirror::Class>* interfaces, - mirror::ClassLoader* loader, - mirror::ObjectArray<mirror::ArtMethod>* methods, - mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >* throws) { - Thread* self = Thread::Current(); +mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccess& soa, jstring name, + jobjectArray interfaces, jobject loader, + jobjectArray methods, jobjectArray throws) { + Thread* self = soa.Self(); SirtRef<mirror::Class> klass(self, AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::SynthesizedProxyClass))); if (klass.get() == NULL) { @@ -2702,9 +2681,9 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, DCHECK(klass->GetClass() != NULL); klass->SetObjectSize(sizeof(mirror::Proxy)); klass->SetAccessFlags(kAccClassIsProxy | kAccPublic | kAccFinal); - klass->SetClassLoader(loader); + klass->SetClassLoader(soa.Decode<mirror::ClassLoader*>(loader)); DCHECK_EQ(klass->GetPrimitiveType(), Primitive::kPrimNot); - klass->SetName(name); + klass->SetName(soa.Decode<mirror::String*>(name)); mirror::Class* proxy_class = GetClassRoot(kJavaLangReflectProxy); klass->SetDexCache(proxy_class->GetDexCache()); klass->SetStatus(mirror::Class::kStatusIdx, self); @@ -2742,8 +2721,7 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, // Proxies have 1 direct method, the constructor { - mirror::ObjectArray<mirror::ArtMethod>* directs = - AllocArtMethodArray(self, 1); + mirror::ObjectArray<mirror::ArtMethod>* directs = AllocArtMethodArray(self, 1); if (UNLIKELY(directs == NULL)) { CHECK(self->IsExceptionPending()); // OOME. return NULL; @@ -2757,11 +2735,11 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, klass->SetDirectMethod(0, constructor); } - // Create virtual method using specified prototypes - size_t num_virtual_methods = methods->GetLength(); + // Create virtual method using specified prototypes. + size_t num_virtual_methods = + soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods)->GetLength(); { - mirror::ObjectArray<mirror::ArtMethod>* virtuals = - AllocArtMethodArray(self, num_virtual_methods); + mirror::ObjectArray<mirror::ArtMethod>* virtuals = AllocArtMethodArray(self, num_virtual_methods); if (UNLIKELY(virtuals == NULL)) { CHECK(self->IsExceptionPending()); // OOME. return NULL; @@ -2769,7 +2747,9 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, klass->SetVirtualMethods(virtuals); } for (size_t i = 0; i < num_virtual_methods; ++i) { - SirtRef<mirror::ArtMethod> prototype(self, methods->Get(i)); + mirror::ObjectArray<mirror::ArtMethod>* decoded_methods = + soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods); + SirtRef<mirror::ArtMethod> prototype(self, decoded_methods->Get(i)); mirror::ArtMethod* clone = CreateProxyMethod(self, klass, prototype); if (UNLIKELY(clone == NULL)) { CHECK(self->IsExceptionPending()); // OOME. @@ -2785,13 +2765,15 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, { ObjectLock lock(self, klass.get()); // Must hold lock on object when resolved. // Link the fields and virtual methods, creating vtable and iftables - if (!LinkClass(klass, interfaces, self)) { + SirtRef<mirror::ObjectArray<mirror::Class> > sirt_interfaces( + self, soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)); + if (!LinkClass(self, klass, sirt_interfaces)) { klass->SetStatus(mirror::Class::kStatusError, self); return NULL; } - interfaces_sfield->SetObject(klass.get(), interfaces); - throws_sfield->SetObject(klass.get(), throws); + interfaces_sfield->SetObject(klass.get(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)); + throws_sfield->SetObject(klass.get(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws)); klass->SetStatus(mirror::Class::kStatusInitialized, self); } @@ -2800,22 +2782,25 @@ mirror::Class* ClassLinker::CreateProxyClass(mirror::String* name, CHECK(klass->GetIFields() == NULL); CheckProxyConstructor(klass->GetDirectMethod(0)); for (size_t i = 0; i < num_virtual_methods; ++i) { - SirtRef<mirror::ArtMethod> prototype(self, methods->Get(i)); + mirror::ObjectArray<mirror::ArtMethod>* decoded_methods = + soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(methods); + SirtRef<mirror::ArtMethod> prototype(self, decoded_methods->Get(i)); CheckProxyMethod(klass->GetVirtualMethod(i), prototype); } + mirror::String* decoded_name = soa.Decode<mirror::String*>(name); std::string interfaces_field_name(StringPrintf("java.lang.Class[] %s.interfaces", - name->ToModifiedUtf8().c_str())); + decoded_name->ToModifiedUtf8().c_str())); CHECK_EQ(PrettyField(klass->GetStaticField(0)), interfaces_field_name); std::string throws_field_name(StringPrintf("java.lang.Class[][] %s.throws", - name->ToModifiedUtf8().c_str())); + decoded_name->ToModifiedUtf8().c_str())); CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name); mirror::SynthesizedProxyClass* synth_proxy_class = down_cast<mirror::SynthesizedProxyClass*>(klass.get()); - CHECK_EQ(synth_proxy_class->GetInterfaces(), interfaces); - CHECK_EQ(synth_proxy_class->GetThrows(), throws); + CHECK_EQ(synth_proxy_class->GetInterfaces(), soa.Decode<mirror::ObjectArray<mirror::Class>*>(interfaces)); + CHECK_EQ(synth_proxy_class->GetThrows(), soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(throws)); } std::string descriptor(GetDescriptorForProxy(klass.get())); mirror::Class* existing = InsertClass(descriptor.c_str(), klass.get(), Hash(descriptor.c_str())); @@ -2977,6 +2962,10 @@ static bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, return true; } +bool ClassLinker::IsInitialized() const { + return init_done_; +} + 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 @@ -3084,7 +3073,9 @@ bool ClassLinker::InitializeClass(mirror::Class* klass, bool can_init_statics, 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(), + SirtRef<mirror::ClassLoader> class_loader(self, klass->GetClassLoader()); + SirtRef<mirror::DexCache> dex_cache(self, kh.GetDexCache()); + EncodedStaticFieldValueIterator it(dex_file, &dex_cache, &class_loader, this, *dex_class_def); if (it.HasNext()) { CHECK(can_init_statics); @@ -3196,12 +3187,11 @@ bool ClassLinker::ValidateSuperClassDescriptors(const mirror::Class* klass) { } } } - mirror::IfTable* iftable = klass->GetIfTable(); for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { - mirror::Class* interface = iftable->GetInterface(i); + mirror::Class* interface = klass->GetIfTable()->GetInterface(i); if (klass->GetClassLoader() != interface->GetClassLoader()) { for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) { - const mirror::ArtMethod* method = iftable->GetMethodArray(i)->Get(j); + const mirror::ArtMethod* method = klass->GetIfTable()->GetMethodArray(i)->Get(j); if (!IsSameMethodSignatureInDifferentClassContexts(method, interface, method->GetDeclaringClass())) { ThrowLinkageError(klass, "Class %s method %s resolves differently in interface %s", @@ -3259,11 +3249,14 @@ bool ClassLinker::IsSameDescriptorInDifferentClassContexts(const char* descripto if (klass1 == klass2) { return true; } - mirror::Class* found1 = FindClass(descriptor, klass1->GetClassLoader()); + Thread* self = Thread::Current(); + SirtRef<mirror::ClassLoader> class_loader1(self, klass1->GetClassLoader()); + mirror::Class* found1 = FindClass(descriptor, class_loader1); if (found1 == NULL) { Thread::Current()->ClearException(); } - mirror::Class* found2 = FindClass(descriptor, klass2->GetClassLoader()); + SirtRef<mirror::ClassLoader> class_loader2(self, klass2->GetClassLoader()); + mirror::Class* found2 = FindClass(descriptor, class_loader2); if (found2 == NULL) { Thread::Current()->ClearException(); } @@ -3285,17 +3278,20 @@ bool ClassLinker::EnsureInitialized(mirror::Class* c, bool can_init_fields, bool } void ClassLinker::ConstructFieldMap(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, - mirror::Class* c, SafeMap<uint32_t, mirror::ArtField*>& field_map) { - mirror::ClassLoader* cl = c->GetClassLoader(); + mirror::Class* c, + SafeMap<uint32_t, mirror::ArtField*>& field_map) { const byte* class_data = dex_file.GetClassData(dex_class_def); ClassDataItemIterator it(dex_file, class_data); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, c->GetDexCache()); + SirtRef<mirror::ClassLoader> class_loader(self, c->GetClassLoader()); for (size_t i = 0; it.HasNextStaticField(); i++, it.Next()) { - field_map.Put(i, ResolveField(dex_file, it.GetMemberIndex(), c->GetDexCache(), cl, true)); + field_map.Put(i, ResolveField(dex_file, it.GetMemberIndex(), dex_cache, class_loader, true)); } } -bool ClassLinker::LinkClass(SirtRef<mirror::Class>& klass, - mirror::ObjectArray<mirror::Class>* interfaces, Thread* self) { +bool ClassLinker::LinkClass(Thread* self, SirtRef<mirror::Class>& klass, + SirtRef<mirror::ObjectArray<mirror::Class> >& interfaces) { CHECK_EQ(mirror::Class::kStatusLoaded, klass->GetStatus()); if (!LinkSuperClass(klass)) { return false; @@ -3419,7 +3415,7 @@ bool ClassLinker::LinkSuperClass(SirtRef<mirror::Class>& klass) { // Populate the class vtable and itable. Compute return type indices. bool ClassLinker::LinkMethods(SirtRef<mirror::Class>& klass, - mirror::ObjectArray<mirror::Class>* interfaces) { + SirtRef<mirror::ObjectArray<mirror::Class> >& interfaces) { if (klass->IsInterface()) { // No vtable. size_t count = klass->NumVirtualMethods(); @@ -3453,15 +3449,13 @@ bool ClassLinker::LinkVirtualMethods(SirtRef<mirror::Class>& klass) { return false; } // See if any of our virtual methods override the superclass. - MethodHelper local_mh(NULL, this); - MethodHelper super_mh(NULL, this); for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) { mirror::ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i); - local_mh.ChangeMethod(local_method); + MethodHelper local_mh(local_method); size_t j = 0; for (; j < actual_count; ++j) { mirror::ArtMethod* super_method = vtable->Get(j); - super_mh.ChangeMethod(super_method); + MethodHelper super_mh(super_method); if (local_mh.HasSameNameAndSignature(&super_mh)) { if (klass->CanAccessMember(super_method->GetDeclaringClass(), super_method->GetAccessFlags())) { if (super_method->IsFinal()) { @@ -3525,7 +3519,7 @@ bool ClassLinker::LinkVirtualMethods(SirtRef<mirror::Class>& klass) { } bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass, - mirror::ObjectArray<mirror::Class>* interfaces) { + SirtRef<mirror::ObjectArray<mirror::Class> >& interfaces) { // Set the imt table to be all conflicts by default. klass->SetImTable(Runtime::Current()->GetDefaultImt()); size_t super_ifcount; @@ -3535,11 +3529,13 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass, super_ifcount = 0; } size_t ifcount = super_ifcount; - ClassHelper kh(klass.get(), this); - uint32_t num_interfaces = interfaces == NULL ? kh.NumDirectInterfaces() : interfaces->GetLength(); + ClassHelper kh(klass.get()); + uint32_t num_interfaces = + interfaces.get() == nullptr ? kh.NumDirectInterfaces() : interfaces->GetLength(); ifcount += num_interfaces; for (size_t i = 0; i < num_interfaces; i++) { - mirror::Class* interface = interfaces == NULL ? kh.GetDirectInterface(i) : interfaces->Get(i); + mirror::Class* interface = + interfaces.get() == nullptr ? kh.GetDirectInterface(i) : interfaces->Get(i); ifcount += interface->GetIfTableCount(); } if (ifcount == 0) { @@ -3580,7 +3576,8 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass, // Flatten the interface inheritance hierarchy. size_t idx = super_ifcount; for (size_t i = 0; i < num_interfaces; i++) { - mirror::Class* interface = interfaces == NULL ? kh.GetDirectInterface(i) : interfaces->Get(i); + mirror::Class* interface = + interfaces.get() == nullptr ? kh.GetDirectInterface(i) : interfaces->Get(i); DCHECK(interface != NULL); if (!interface->IsInterface()) { ClassHelper ih(interface); @@ -3643,20 +3640,21 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass, return false; } std::vector<mirror::ArtMethod*> miranda_list; - MethodHelper vtable_mh(NULL, this); - MethodHelper interface_mh(NULL, this); + MethodHelper vtable_mh(NULL); + MethodHelper interface_mh(NULL); for (size_t i = 0; i < ifcount; ++i) { mirror::Class* interface = iftable->GetInterface(i); size_t num_methods = interface->NumVirtualMethods(); if (num_methods > 0) { - mirror::ObjectArray<mirror::ArtMethod>* method_array = - AllocArtMethodArray(self, num_methods); - if (UNLIKELY(method_array == NULL)) { + SirtRef<mirror::ObjectArray<mirror::ArtMethod> > + method_array(self, AllocArtMethodArray(self, num_methods)); + if (UNLIKELY(method_array.get() == nullptr)) { CHECK(self->IsExceptionPending()); // OOME. return false; } - iftable->SetMethodArray(i, method_array); - mirror::ObjectArray<mirror::ArtMethod>* vtable = klass->GetVTableDuringLinking(); + iftable->SetMethodArray(i, method_array.get()); + SirtRef<mirror::ObjectArray<mirror::ArtMethod> > vtable(self, + klass->GetVTableDuringLinking()); for (size_t j = 0; j < num_methods; ++j) { mirror::ArtMethod* interface_method = interface->GetVirtualMethod(j); interface_mh.ChangeMethod(interface_method); @@ -3709,10 +3707,7 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<mirror::Class>& klass, CHECK(self->IsExceptionPending()); // OOME. return false; } -#ifdef MOVING_GARBAGE_COLLECTOR // TODO: If a methods move then the miranda_list may hold stale references. - UNIMPLEMENTED(FATAL); -#endif miranda_list.push_back(miranda_method.get()); } method_array->Set(j, miranda_method.get()); @@ -3791,17 +3786,16 @@ bool ClassLinker::LinkStaticFields(SirtRef<mirror::Class>& klass) { } struct LinkFieldsComparator { - explicit LinkFieldsComparator(FieldHelper* fh) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : fh_(fh) {} + explicit LinkFieldsComparator() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + } // No thread safety analysis as will be called from STL. Checked lock held in constructor. bool operator()(const mirror::ArtField* field1, const mirror::ArtField* field2) NO_THREAD_SAFETY_ANALYSIS { // First come reference fields, then 64-bit, and finally 32-bit - fh_->ChangeField(field1); - Primitive::Type type1 = fh_->GetTypeAsPrimitiveType(); - fh_->ChangeField(field2); - Primitive::Type type2 = fh_->GetTypeAsPrimitiveType(); + FieldHelper fh1(field1); + Primitive::Type type1 = fh1.GetTypeAsPrimitiveType(); + FieldHelper fh2(field2); + Primitive::Type type2 = fh2.GetTypeAsPrimitiveType(); bool isPrimitive1 = type1 != Primitive::kPrimNot; bool isPrimitive2 = type2 != Primitive::kPrimNot; bool is64bit1 = isPrimitive1 && (type1 == Primitive::kPrimLong || type1 == Primitive::kPrimDouble); @@ -3813,14 +3807,10 @@ struct LinkFieldsComparator { } // same basic group? then sort by string. - fh_->ChangeField(field1); - const char* name1 = fh_->GetName(); - fh_->ChangeField(field2); - const char* name2 = fh_->GetName(); + const char* name1 = fh1.GetName(); + const char* name2 = fh2.GetName(); return strcmp(name1, name2) < 0; } - - FieldHelper* fh_; }; bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) { @@ -3855,17 +3845,15 @@ bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) { CHECK(f != NULL); grouped_and_sorted_fields.push_back(f); } - FieldHelper fh(NULL, this); - std::sort(grouped_and_sorted_fields.begin(), - grouped_and_sorted_fields.end(), - LinkFieldsComparator(&fh)); + std::sort(grouped_and_sorted_fields.begin(), grouped_and_sorted_fields.end(), + LinkFieldsComparator()); // References should be at the front. size_t current_field = 0; size_t num_reference_fields = 0; for (; current_field < num_fields; current_field++) { mirror::ArtField* field = grouped_and_sorted_fields.front(); - fh.ChangeField(field); + FieldHelper fh(field); Primitive::Type type = fh.GetTypeAsPrimitiveType(); bool isPrimitive = type != Primitive::kPrimNot; if (isPrimitive) { @@ -3884,7 +3872,7 @@ bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) { if (current_field != num_fields && !IsAligned<8>(field_offset.Uint32Value())) { for (size_t i = 0; i < grouped_and_sorted_fields.size(); i++) { mirror::ArtField* field = grouped_and_sorted_fields[i]; - fh.ChangeField(field); + FieldHelper fh(field); Primitive::Type type = fh.GetTypeAsPrimitiveType(); CHECK(type != Primitive::kPrimNot); // should only be working on primitive types if (type == Primitive::kPrimLong || type == Primitive::kPrimDouble) { @@ -3906,7 +3894,7 @@ bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) { while (!grouped_and_sorted_fields.empty()) { mirror::ArtField* field = grouped_and_sorted_fields.front(); grouped_and_sorted_fields.pop_front(); - fh.ChangeField(field); + FieldHelper fh(field); Primitive::Type type = fh.GetTypeAsPrimitiveType(); CHECK(type != Primitive::kPrimNot); // should only be working on primitive types fields->Set(current_field, field); @@ -3920,11 +3908,11 @@ bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) { // We lie to the GC about the java.lang.ref.Reference.referent field, so it doesn't scan it. if (!is_static && - (strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get(), this).GetDescriptor()) == 0)) { + (strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get()).GetDescriptor()) == 0)) { // We know there are no non-reference fields in the Reference classes, and we know // that 'referent' is alphabetically last, so this is easy... CHECK_EQ(num_reference_fields, num_fields); - fh.ChangeField(fields->Get(num_fields - 1)); + FieldHelper fh(fields->Get(num_fields - 1)); CHECK_STREQ(fh.GetName(), "referent"); --num_reference_fields; } @@ -3942,10 +3930,10 @@ bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) { << " offset=" << field->GetField32(MemberOffset(mirror::ArtField::OffsetOffset()), false); } - fh.ChangeField(field); + FieldHelper fh(field); Primitive::Type type = fh.GetTypeAsPrimitiveType(); bool is_primitive = type != Primitive::kPrimNot; - if ((strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get(), this).GetDescriptor()) == 0) + if ((strcmp("Ljava/lang/ref/Reference;", ClassHelper(klass.get()).GetDescriptor()) == 0) && (strcmp("referent", fh.GetName()) == 0)) { is_primitive = true; // We lied above, so we have to expect a lie here. } @@ -3970,7 +3958,7 @@ bool ClassLinker::LinkFields(SirtRef<mirror::Class>& klass, bool is_static) { } else { klass->SetNumReferenceInstanceFields(num_reference_fields); if (!klass->IsVariableSize()) { - DCHECK_GE(size, sizeof(mirror::Object)) << ClassHelper(klass.get(), this).GetDescriptor(); + DCHECK_GE(size, sizeof(mirror::Object)) << ClassHelper(klass.get()).GetDescriptor(); size_t previous_size = klass->GetObjectSize(); if (previous_size != 0) { // Make sure that we didn't originally have an incorrect size. @@ -4034,9 +4022,9 @@ void ClassLinker::CreateReferenceOffsets(SirtRef<mirror::Class>& klass, bool is_ } } -mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, - uint32_t string_idx, mirror::DexCache* dex_cache) { - DCHECK(dex_cache != NULL); +mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, uint32_t string_idx, + SirtRef<mirror::DexCache>& dex_cache) { + DCHECK(dex_cache.get() != nullptr); mirror::String* resolved = dex_cache->GetResolvedString(string_idx); if (resolved != NULL) { return resolved; @@ -4048,11 +4036,18 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, return string; } -mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, - uint16_t type_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader) { - DCHECK(dex_cache != NULL); +mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx, + const mirror::Class* referrer) { + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, referrer->GetDexCache()); + SirtRef<mirror::ClassLoader> class_loader(self, referrer->GetClassLoader()); + return ResolveType(dex_file, type_idx, dex_cache, class_loader); +} + +mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, uint16_t type_idx, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader) { + DCHECK(dex_cache.get() != NULL); mirror::Class* resolved = dex_cache->GetResolvedType(type_idx); if (resolved == NULL) { const char* descriptor = dex_file.StringByTypeIdx(type_idx); @@ -4082,11 +4077,11 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader, const mirror::ArtMethod* referrer, InvokeType type) { - DCHECK(dex_cache != NULL); + DCHECK(dex_cache.get() != NULL); // Check for hit in the dex cache. mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); if (resolved != NULL && !resolved->IsRuntimeMethod()) { @@ -4104,15 +4099,15 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, switch (type) { case kDirect: // Fall-through. case kStatic: - resolved = klass->FindDirectMethod(dex_cache, method_idx); + resolved = klass->FindDirectMethod(dex_cache.get(), method_idx); break; case kInterface: - resolved = klass->FindInterfaceMethod(dex_cache, method_idx); + resolved = klass->FindInterfaceMethod(dex_cache.get(), method_idx); DCHECK(resolved == NULL || resolved->GetDeclaringClass()->IsInterface()); break; case kSuper: // Fall-through. case kVirtual: - resolved = klass->FindVirtualMethod(dex_cache, method_idx); + resolved = klass->FindVirtualMethod(dex_cache.get(), method_idx); break; default: LOG(FATAL) << "Unreachable - invocation type: " << type; @@ -4227,12 +4222,11 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, } } -mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, - uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, +mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, uint32_t field_idx, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader, bool is_static) { - DCHECK(dex_cache != NULL); + DCHECK(dex_cache.get() != nullptr); mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); if (resolved != NULL) { return resolved; @@ -4245,9 +4239,9 @@ mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, } if (is_static) { - resolved = klass->FindStaticField(dex_cache, field_idx); + resolved = klass->FindStaticField(dex_cache.get(), field_idx); } else { - resolved = klass->FindInstanceField(dex_cache, field_idx); + resolved = klass->FindInstanceField(dex_cache.get(), field_idx); } if (resolved == NULL) { @@ -4269,9 +4263,9 @@ mirror::ArtField* ClassLinker::ResolveField(const DexFile& dex_file, mirror::ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader) { - DCHECK(dex_cache != NULL); + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader) { + DCHECK(dex_cache.get() != nullptr); mirror::ArtField* resolved = dex_cache->GetResolvedField(field_idx); if (resolved != NULL) { return resolved; diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 473370d..4e2cc06 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -25,6 +25,7 @@ #include "base/mutex.h" #include "dex_file.h" #include "gtest/gtest.h" +#include "jni.h" #include "root_visitor.h" #include "oat_file.h" @@ -45,6 +46,7 @@ namespace mirror { class InternTable; class ObjectLock; +class ScopedObjectAccess; template<class T> class SirtRef; typedef bool (ClassVisitor)(mirror::Class* c, void* arg); @@ -56,29 +58,31 @@ class ClassLinker { // (non-marker) interfaces. static constexpr size_t kImtSize = 64; - // Creates the class linker by bootstrapping from dex files. - static ClassLinker* CreateFromCompiler(const std::vector<const DexFile*>& boot_class_path, - InternTable* intern_table) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + explicit ClassLinker(InternTable* intern_table); + ~ClassLinker(); - // Creates the class linker from an image. - static ClassLinker* CreateFromImage(InternTable* intern_table) + // Initialize class linker by bootstraping from dex files + void InitFromCompiler(const std::vector<const DexFile*>& boot_class_path) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ~ClassLinker(); + // Initialize class linker from one or more images. + void InitFromImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 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) + mirror::Class* FindClass(const char* descriptor, SirtRef<mirror::ClassLoader>& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::Class* FindSystemClass(const char* descriptor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Reutrns true if the class linker is initialized. + bool IsInitialized() const; + // Define a new a class based on a ClassDef from a DexFile - mirror::Class* DefineClass(const char* descriptor, mirror::ClassLoader* class_loader, + mirror::Class* DefineClass(const char* descriptor, SirtRef<mirror::ClassLoader>& class_loader, const DexFile& dex_file, const DexFile::ClassDef& dex_class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -122,7 +126,7 @@ class ClassLinker { // Resolve a String with the given index from the DexFile, storing the // result in the DexCache. mirror::String* ResolveString(const DexFile& dex_file, uint32_t string_idx, - mirror::DexCache* dex_cache) + SirtRef<mirror::DexCache>& dex_cache) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Resolve a Type with the given index from the DexFile, storing the @@ -130,12 +134,7 @@ class ClassLinker { // target DexCache and ClassLoader to use for resolution. mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx, const mirror::Class* referrer) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return ResolveType(dex_file, - type_idx, - referrer->GetDexCache(), - referrer->GetClassLoader()); - } + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Resolve a Type with the given index from the DexFile, storing the // result in the DexCache. The referrer is used to identify the @@ -150,10 +149,9 @@ class ClassLinker { // result in DexCache. The ClassLoader is used to search for the // type, since it may be referenced from but not contained within // the given DexFile. - mirror::Class* ResolveType(const DexFile& dex_file, - uint16_t type_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader) + mirror::Class* ResolveType(const DexFile& dex_file, uint16_t type_idx, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Resolve a method with a given ID from the DexFile, storing the @@ -163,8 +161,8 @@ class ClassLinker { // virtual method. mirror::ArtMethod* ResolveMethod(const DexFile& dex_file, uint32_t method_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader, const mirror::ArtMethod* referrer, InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -184,8 +182,8 @@ class ClassLinker { // field. mirror::ArtField* ResolveField(const DexFile& dex_file, uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -195,8 +193,8 @@ class ClassLinker { // field resolution semantics are followed. mirror::ArtField* ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader) + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Get shorty from method index without resolution. Used to do handlerization. @@ -314,10 +312,8 @@ class ClassLinker { void ResolveMethodExceptionHandlerTypes(const DexFile& dex_file, mirror::ArtMethod* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::Class* CreateProxyClass(mirror::String* name, mirror::ObjectArray<mirror::Class>* interfaces, - mirror::ClassLoader* loader, - mirror::ObjectArray<mirror::ArtMethod>* methods, - mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >* throws) + mirror::Class* CreateProxyClass(ScopedObjectAccess& soa, jstring name, jobjectArray interfaces, + jobject loader, jobjectArray methods, jobjectArray throws) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); std::string GetDescriptorForProxy(const mirror::Class* proxy_class) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -364,18 +360,13 @@ class ClassLinker { LOCKS_EXCLUDED(Locks::classlinker_classes_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - private: - explicit ClassLinker(InternTable*); + // Special code to allocate an art method, use this instead of class->AllocObject. + mirror::ArtMethod* AllocArtMethod(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: const OatFile::OatMethod GetOatMethodFor(const mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Initialize class linker by bootstraping from dex files - void InitFromCompiler(const std::vector<const DexFile*>& boot_class_path) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // Initialize class linker from one or more images. - void InitFromImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); OatFile& GetImageOatFile(gc::space::ImageSpace* space) LOCKS_EXCLUDED(dex_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -393,7 +384,6 @@ class ClassLinker { mirror::DexCache* AllocDexCache(Thread* self, const DexFile& dex_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::ArtField* AllocArtField(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::ArtMethod* AllocArtMethod(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); mirror::Class* CreatePrimitiveClass(Thread* self, Primitive::Type type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -401,7 +391,8 @@ class ClassLinker { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::Class* CreateArrayClass(const char* descriptor, mirror::ClassLoader* class_loader) + mirror::Class* CreateArrayClass(const char* descriptor, + SirtRef<mirror::ClassLoader>& class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void AppendToBootClassPath(const DexFile& dex_file) @@ -458,8 +449,8 @@ class ClassLinker { const mirror::Class* klass2) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkClass(SirtRef<mirror::Class>& klass, mirror::ObjectArray<mirror::Class>* interfaces, - Thread* self) + bool LinkClass(Thread* self, SirtRef<mirror::Class>& klass, + SirtRef<mirror::ObjectArray<mirror::Class> >& interfaces) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool LinkSuperClass(SirtRef<mirror::Class>& klass) @@ -468,14 +459,15 @@ class ClassLinker { bool LoadSuperAndInterfaces(SirtRef<mirror::Class>& klass, const DexFile& dex_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool LinkMethods(SirtRef<mirror::Class>& klass, mirror::ObjectArray<mirror::Class>* interfaces) + bool LinkMethods(SirtRef<mirror::Class>& klass, + SirtRef<mirror::ObjectArray<mirror::Class> >& interfaces) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool LinkVirtualMethods(SirtRef<mirror::Class>& klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool LinkInterfaceMethods(SirtRef<mirror::Class>& klass, - mirror::ObjectArray<mirror::Class>* interfaces) + SirtRef<mirror::ObjectArray<mirror::Class> >& interfaces) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool LinkStaticFields(SirtRef<mirror::Class>& klass) diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index a52b680..b8bc474 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -95,7 +95,8 @@ class ClassLinkerTest : public CommonTest { const std::string& component_type, mirror::ClassLoader* class_loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::Class* array = class_linker_->FindClass(array_descriptor.c_str(), class_loader); + SirtRef<mirror::ClassLoader> loader(Thread::Current(), class_loader); + mirror::Class* array = class_linker_->FindClass(array_descriptor.c_str(), loader); ClassHelper array_component_ch(array->GetComponentType()); EXPECT_STREQ(component_type.c_str(), array_component_ch.GetDescriptor()); EXPECT_EQ(class_loader, array->GetClassLoader()); @@ -647,12 +648,12 @@ TEST_F(ClassLinkerTest, FindClassNested) { ScopedObjectAccess soa(Thread::Current()); SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Nested"))); - mirror::Class* outer = class_linker_->FindClass("LNested;", class_loader.get()); + mirror::Class* outer = class_linker_->FindClass("LNested;", class_loader); ASSERT_TRUE(outer != NULL); EXPECT_EQ(0U, outer->NumVirtualMethods()); EXPECT_EQ(1U, outer->NumDirectMethods()); - mirror::Class* inner = class_linker_->FindClass("LNested$Inner;", class_loader.get()); + mirror::Class* inner = class_linker_->FindClass("LNested$Inner;", class_loader); ASSERT_TRUE(inner != NULL); EXPECT_EQ(0U, inner->NumVirtualMethods()); EXPECT_EQ(1U, inner->NumDirectMethods()); @@ -711,7 +712,7 @@ TEST_F(ClassLinkerTest, FindClass) { SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass"))); AssertNonExistentClass("LMyClass;"); - mirror::Class* MyClass = class_linker_->FindClass("LMyClass;", class_loader.get()); + mirror::Class* MyClass = class_linker_->FindClass("LMyClass;", class_loader); kh.ChangeClass(MyClass); ASSERT_TRUE(MyClass != NULL); ASSERT_TRUE(MyClass->GetClass() != NULL); @@ -809,29 +810,30 @@ TEST_F(ClassLinkerTest, ValidateBoxedTypes) { // Validate that the "value" field is always the 0th field in each of java.lang's box classes. // This lets UnboxPrimitive avoid searching for the field by name at runtime. ScopedObjectAccess soa(Thread::Current()); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr); mirror::Class* c; - c = class_linker_->FindClass("Ljava/lang/Boolean;", NULL); + c = class_linker_->FindClass("Ljava/lang/Boolean;", class_loader); FieldHelper fh(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Byte;", NULL); + c = class_linker_->FindClass("Ljava/lang/Byte;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Character;", NULL); + c = class_linker_->FindClass("Ljava/lang/Character;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Double;", NULL); + c = class_linker_->FindClass("Ljava/lang/Double;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Float;", NULL); + c = class_linker_->FindClass("Ljava/lang/Float;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Integer;", NULL); + c = class_linker_->FindClass("Ljava/lang/Integer;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Long;", NULL); + c = class_linker_->FindClass("Ljava/lang/Long;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); - c = class_linker_->FindClass("Ljava/lang/Short;", NULL); + c = class_linker_->FindClass("Ljava/lang/Short;", class_loader); fh.ChangeField(c->GetIFields()->Get(0)); EXPECT_STREQ("value", fh.GetName()); } @@ -840,8 +842,8 @@ TEST_F(ClassLinkerTest, TwoClassLoadersOneClass) { ScopedObjectAccess soa(Thread::Current()); SirtRef<mirror::ClassLoader> class_loader_1(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass"))); SirtRef<mirror::ClassLoader> class_loader_2(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("MyClass"))); - mirror::Class* MyClass_1 = class_linker_->FindClass("LMyClass;", class_loader_1.get()); - mirror::Class* MyClass_2 = class_linker_->FindClass("LMyClass;", class_loader_2.get()); + mirror::Class* MyClass_1 = class_linker_->FindClass("LMyClass;", class_loader_1); + mirror::Class* MyClass_2 = class_linker_->FindClass("LMyClass;", class_loader_2); EXPECT_TRUE(MyClass_1 != NULL); EXPECT_TRUE(MyClass_2 != NULL); EXPECT_NE(MyClass_1, MyClass_2); @@ -850,7 +852,7 @@ TEST_F(ClassLinkerTest, TwoClassLoadersOneClass) { TEST_F(ClassLinkerTest, StaticFields) { ScopedObjectAccess soa(Thread::Current()); SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Statics"))); - mirror::Class* statics = class_linker_->FindClass("LStatics;", class_loader.get()); + mirror::Class* statics = class_linker_->FindClass("LStatics;", class_loader); class_linker_->EnsureInitialized(statics, true, true); // Static final primitives that are initialized by a compile-time constant @@ -932,11 +934,11 @@ TEST_F(ClassLinkerTest, StaticFields) { TEST_F(ClassLinkerTest, Interfaces) { ScopedObjectAccess soa(Thread::Current()); SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("Interfaces"))); - mirror::Class* I = class_linker_->FindClass("LInterfaces$I;", class_loader.get()); - mirror::Class* J = class_linker_->FindClass("LInterfaces$J;", class_loader.get()); - mirror::Class* K = class_linker_->FindClass("LInterfaces$K;", class_loader.get()); - mirror::Class* A = class_linker_->FindClass("LInterfaces$A;", class_loader.get()); - mirror::Class* B = class_linker_->FindClass("LInterfaces$B;", class_loader.get()); + mirror::Class* I = class_linker_->FindClass("LInterfaces$I;", class_loader); + mirror::Class* J = class_linker_->FindClass("LInterfaces$J;", class_loader); + mirror::Class* K = class_linker_->FindClass("LInterfaces$K;", class_loader); + mirror::Class* A = class_linker_->FindClass("LInterfaces$A;", class_loader); + mirror::Class* B = class_linker_->FindClass("LInterfaces$B;", class_loader); EXPECT_TRUE(I->IsAssignableFrom(A)); EXPECT_TRUE(J->IsAssignableFrom(A)); EXPECT_TRUE(J->IsAssignableFrom(K)); @@ -995,8 +997,7 @@ TEST_F(ClassLinkerTest, ResolveVerifyAndClinit) { SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader)); const DexFile* dex_file = Runtime::Current()->GetCompileTimeClassPath(jclass_loader)[0]; CHECK(dex_file != NULL); - - mirror::Class* klass = class_linker_->FindClass("LStaticsFromCode;", class_loader.get()); + mirror::Class* klass = class_linker_->FindClass("LStaticsFromCode;", class_loader); mirror::ArtMethod* clinit = klass->FindClassInitializer(); mirror::ArtMethod* getS0 = klass->FindDirectMethod("getS0", "()Ljava/lang/Object;"); const DexFile::StringId* string_id = dex_file->FindStringId("LStaticsFromCode;"); @@ -1049,10 +1050,9 @@ TEST_F(ClassLinkerTest, FinalizableBit) { TEST_F(ClassLinkerTest, ClassRootDescriptors) { ScopedObjectAccess soa(Thread::Current()); - ClassHelper kh; for (int i = 0; i < ClassLinker::kClassRootsMax; i++) { mirror::Class* klass = class_linker_->GetClassRoot(ClassLinker::ClassRoot(i)); - kh.ChangeClass(klass); + ClassHelper kh(klass); EXPECT_TRUE(kh.GetDescriptor() != NULL); EXPECT_STREQ(kh.GetDescriptor(), class_linker_->GetClassRootDescriptor(ClassLinker::ClassRoot(i))) << " i = " << i; diff --git a/runtime/common_test.h b/runtime/common_test.h index 643ed1d..7cc29a1 100644 --- a/runtime/common_test.h +++ b/runtime/common_test.h @@ -569,7 +569,8 @@ class CommonTest : public testing::Test { void CompileClass(mirror::ClassLoader* class_loader, const char* class_name) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string class_descriptor(DotToDescriptor(class_name)); - mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader); + SirtRef<mirror::ClassLoader> loader(Thread::Current(), class_loader); + mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), loader); CHECK(klass != NULL) << "Class not found " << class_name; for (size_t i = 0; i < klass->NumDirectMethods(); i++) { CompileMethod(klass->GetDirectMethod(i)); @@ -587,10 +588,8 @@ class CommonTest : public testing::Test { MakeExecutable(method); } - void CompileDirectMethod(mirror::ClassLoader* class_loader, - const char* class_name, - const char* method_name, - const char* signature) + void CompileDirectMethod(SirtRef<mirror::ClassLoader>& class_loader, const char* class_name, + const char* method_name, const char* signature) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string class_descriptor(DotToDescriptor(class_name)); mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader); @@ -601,10 +600,8 @@ class CommonTest : public testing::Test { CompileMethod(method); } - void CompileVirtualMethod(mirror::ClassLoader* class_loader, - const char* class_name, - const char* method_name, - const char* signature) + void CompileVirtualMethod(SirtRef<mirror::ClassLoader>& class_loader, const char* class_name, + const char* method_name, const char* signature) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::string class_descriptor(DotToDescriptor(class_name)); mirror::Class* klass = class_linker_->FindClass(class_descriptor.c_str(), class_loader); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 0eecd28..f537709 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1118,7 +1118,8 @@ JDWP::JdwpError Dbg::CreateArrayObject(JDWP::RefTypeId array_class_id, uint32_t if (c == NULL) { return status; } - new_array = gRegistry->Add(mirror::Array::Alloc(Thread::Current(), c, length)); + new_array = gRegistry->Add( + mirror::Array::Alloc<kMovingCollector, true>(Thread::Current(), c, length)); return JDWP::ERR_NONE; } @@ -1133,38 +1134,26 @@ bool Dbg::MatchType(JDWP::RefTypeId instance_class_id, JDWP::RefTypeId class_id) static JDWP::FieldId ToFieldId(const mirror::ArtField* f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(FATAL); -#else + CHECK(!kMovingFields); return static_cast<JDWP::FieldId>(reinterpret_cast<uintptr_t>(f)); -#endif } static JDWP::MethodId ToMethodId(const mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(FATAL); -#else + CHECK(!kMovingMethods); return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(m)); -#endif } static mirror::ArtField* FromFieldId(JDWP::FieldId fid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(FATAL); -#else + CHECK(!kMovingFields); return reinterpret_cast<mirror::ArtField*>(static_cast<uintptr_t>(fid)); -#endif } static mirror::ArtMethod* FromMethodId(JDWP::MethodId mid) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(FATAL); -#else + CHECK(!kMovingMethods); return reinterpret_cast<mirror::ArtMethod*>(static_cast<uintptr_t>(mid)); -#endif } static void SetLocation(JDWP::JdwpLocation& location, mirror::ArtMethod* m, uint32_t dex_pc) @@ -2079,7 +2068,7 @@ void Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int sl CHECK_EQ(width_, sizeof(JDWP::ObjectId)); mirror::Object* o = reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kReferenceVReg)); VLOG(jdwp) << "get array local " << reg << " = " << o; - if (!Runtime::Current()->GetHeap()->IsHeapAddress(o)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { LOG(FATAL) << "Register " << reg << " expected to hold array: " << o; } JDWP::SetObjectId(buf_+1, gRegistry->Add(o)); @@ -2095,7 +2084,7 @@ void Dbg::GetLocalValue(JDWP::ObjectId thread_id, JDWP::FrameId frame_id, int sl CHECK_EQ(width_, sizeof(JDWP::ObjectId)); mirror::Object* o = reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kReferenceVReg)); VLOG(jdwp) << "get object local " << reg << " = " << o; - if (!Runtime::Current()->GetHeap()->IsHeapAddress(o)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) { LOG(FATAL) << "Register " << reg << " expected to hold object: " << o; } tag_ = TagFromObject(o); @@ -3372,7 +3361,7 @@ class HeapChunkContext { return HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); } - if (!Runtime::Current()->GetHeap()->IsHeapAddress(c)) { + if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) { LOG(ERROR) << "Invalid class for managed heap object: " << o << " " << c; return HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN); } @@ -3752,7 +3741,6 @@ jbyteArray Dbg::GetRecentAllocations() { count = gAllocRecordCount; idx = HeadIndex(); - ClassHelper kh; while (count--) { // For each entry: // (4b) total allocation size @@ -3761,7 +3749,7 @@ jbyteArray Dbg::GetRecentAllocations() { // (1b) stack depth AllocRecord* record = &recent_allocation_records_[idx]; size_t stack_depth = record->GetDepth(); - kh.ChangeClass(record->type); + ClassHelper kh(record->type); size_t allocated_object_class_name_index = class_names.IndexOf(kh.GetDescriptor()); JDWP::Append4BE(bytes, record->byte_count); JDWP::Append2BE(bytes, record->thin_lock_id); diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index a897cce..a02823e 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -37,6 +37,7 @@ #include "os.h" #include "safe_map.h" #include "ScopedFd.h" +#include "sirt_ref.h" #include "thread.h" #include "UniquePtr.h" #include "utf-inl.h" @@ -963,12 +964,14 @@ static uint64_t ReadUnsignedLong(const byte* ptr, int zwidth, bool fill_on_right } EncodedStaticFieldValueIterator::EncodedStaticFieldValueIterator(const DexFile& dex_file, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef<mirror::DexCache>* dex_cache, + SirtRef<mirror::ClassLoader>* class_loader, ClassLinker* linker, const DexFile::ClassDef& class_def) : dex_file_(dex_file), dex_cache_(dex_cache), class_loader_(class_loader), linker_(linker), array_size_(), pos_(-1), type_(kByte) { + DCHECK(dex_cache != nullptr); + DCHECK(class_loader != nullptr); ptr_ = dex_file.GetEncodedStaticFieldValuesArray(class_def); if (ptr_ == NULL) { array_size_ = 0; @@ -1051,12 +1054,15 @@ void EncodedStaticFieldValueIterator::ReadValueToField(mirror::ArtField* field) case kDouble: field->SetDouble(field->GetDeclaringClass(), jval_.d); break; case kNull: field->SetObject(field->GetDeclaringClass(), NULL); break; case kString: { - mirror::String* resolved = linker_->ResolveString(dex_file_, jval_.i, dex_cache_); + CHECK(!kMovingFields); + mirror::String* resolved = linker_->ResolveString(dex_file_, jval_.i, *dex_cache_); field->SetObject(field->GetDeclaringClass(), resolved); break; } case kType: { - mirror::Class* resolved = linker_->ResolveType(dex_file_, jval_.i, dex_cache_, class_loader_); + CHECK(!kMovingFields); + mirror::Class* resolved = linker_->ResolveType(dex_file_, jval_.i, *dex_cache_, + *class_loader_); field->SetObject(field->GetDeclaringClass(), resolved); break; } diff --git a/runtime/dex_file.h b/runtime/dex_file.h index a9c24e6..51ab8d8 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -43,6 +43,8 @@ namespace mirror { } // namespace mirror class ClassLinker; class Signature; +template <typename T> +class SirtRef; class StringPiece; class ZipArchive; @@ -1152,8 +1154,8 @@ class ClassDataItemIterator { class EncodedStaticFieldValueIterator { public: - EncodedStaticFieldValueIterator(const DexFile& dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + EncodedStaticFieldValueIterator(const DexFile& dex_file, SirtRef<mirror::DexCache>* dex_cache, + SirtRef<mirror::ClassLoader>* class_loader, ClassLinker* linker, const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -1187,8 +1189,8 @@ class EncodedStaticFieldValueIterator { static const byte kEncodedValueArgShift = 5; const DexFile& dex_file_; - mirror::DexCache* dex_cache_; // Dex cache to resolve literal objects. - mirror::ClassLoader* class_loader_; // ClassLoader to resolve types. + SirtRef<mirror::DexCache>* const dex_cache_; // Dex cache to resolve literal objects. + SirtRef<mirror::ClassLoader>* const class_loader_; // ClassLoader to resolve types. ClassLinker* linker_; // Linker to resolve literal objects. size_t array_size_; // Size of array. size_t pos_; // Current position. diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 24ab1ce..d7bbe64 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -82,7 +82,7 @@ mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) { return NULL; } - return mirror::Array::AllocUninstrumented(self, klass, component_count); + return mirror::Array::Alloc<kMovingCollector, false>(self, klass, component_count); } // Helper function to allocate array for FILLED_NEW_ARRAY. @@ -93,7 +93,7 @@ mirror::Array* CheckAndAllocArrayFromCodeInstrumented(uint32_t type_idx, mirror: if (UNLIKELY(!CheckFilledNewArrayAlloc(type_idx, referrer, component_count, self, access_check, &klass))) { return NULL; } - return mirror::Array::AllocInstrumented(self, klass, component_count); + return mirror::Array::Alloc<kMovingCollector, true>(self, klass, component_count); } void ThrowStackOverflowError(Thread* self) { diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index 7ce50c5..3b58a8d 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -88,7 +88,7 @@ static inline mirror::Object* AllocObjectFromCode(uint32_t type_idx, mirror::Art if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) { return NULL; } - return klass->AllocObjectUninstrumented(self); + return klass->Alloc<kMovingCollector, false>(self); } static inline mirror::Object* AllocObjectFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, @@ -99,7 +99,7 @@ static inline mirror::Object* AllocObjectFromCodeInstrumented(uint32_t type_idx, if (UNLIKELY(!CheckObjectAlloc(type_idx, method, self, access_check, &klass))) { return NULL; } - return klass->AllocObjectInstrumented(self); + return klass->Alloc<kMovingCollector, true>(self); } static inline bool CheckArrayAlloc(uint32_t type_idx, mirror::ArtMethod* method, @@ -142,7 +142,7 @@ static inline mirror::Array* AllocArrayFromCode(uint32_t type_idx, mirror::ArtMe if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) { return NULL; } - return mirror::Array::AllocUninstrumented(self, klass, component_count); + return mirror::Array::Alloc<kMovingCollector, false>(self, klass, component_count); } static inline mirror::Array* AllocArrayFromCodeInstrumented(uint32_t type_idx, mirror::ArtMethod* method, @@ -153,7 +153,7 @@ static inline mirror::Array* AllocArrayFromCodeInstrumented(uint32_t type_idx, m if (UNLIKELY(!CheckArrayAlloc(type_idx, method, component_count, access_check, &klass))) { return NULL; } - return mirror::Array::AllocInstrumented(self, klass, component_count); + return mirror::Array::Alloc<kMovingCollector, true>(self, klass, component_count); } extern mirror::Array* CheckAndAllocArrayFromCode(uint32_t type_idx, mirror::ArtMethod* method, diff --git a/runtime/entrypoints/quick/quick_lock_entrypoints.cc b/runtime/entrypoints/quick/quick_lock_entrypoints.cc index 2102ab1..540abb3 100644 --- a/runtime/entrypoints/quick/quick_lock_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_lock_entrypoints.cc @@ -29,9 +29,15 @@ extern "C" int artLockObjectFromCode(mirror::Object* obj, Thread* self, mirror:: "Null reference used for synchronization (monitor-enter)"); return -1; // Failure. } else { - obj->MonitorEnter(self); // May block - DCHECK(self->HoldsLock(obj)); - DCHECK(!self->IsExceptionPending()); + if (kIsDebugBuild) { + // GC may move the obj, need Sirt for the following DCHECKs. + SirtRef<mirror::Object> sirt_obj(self, obj); + obj->MonitorEnter(self); // May block + CHECK(self->HoldsLock(sirt_obj.get())); + CHECK(!self->IsExceptionPending()); + } else { + obj->MonitorEnter(self); // May block + } return 0; // Success. // Only possible exception is NPE and is handled before entry } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 01d3549..8ba08ee 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -416,10 +416,10 @@ extern "C" uint64_t artQuickProxyInvokeHandler(mirror::ArtMethod* proxy_method, // Read object references held in arguments from quick frames and place in a JNI local references, // so they don't get garbage collected. -class RememberFoGcArgumentVisitor : public QuickArgumentVisitor { +class RememberForGcArgumentVisitor : public QuickArgumentVisitor { public: - RememberFoGcArgumentVisitor(mirror::ArtMethod** sp, bool is_static, const char* shorty, - uint32_t shorty_len, ScopedObjectAccessUnchecked* soa) : + RememberForGcArgumentVisitor(mirror::ArtMethod** sp, bool is_static, const char* shorty, + uint32_t shorty_len, ScopedObjectAccessUnchecked* soa) : QuickArgumentVisitor(sp, is_static, shorty, shorty_len), soa_(soa) {} virtual void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -441,7 +441,7 @@ class RememberFoGcArgumentVisitor : public QuickArgumentVisitor { private: ScopedObjectAccessUnchecked* soa_; std::vector<std::pair<jobject, mirror::Object**> > references_; - DISALLOW_COPY_AND_ASSIGN(RememberFoGcArgumentVisitor); + DISALLOW_COPY_AND_ASSIGN(RememberForGcArgumentVisitor); }; // Lazily resolve a method for quick. Called by stub code. @@ -531,7 +531,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called, uint32_t shorty_len; const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(dex_method_idx), &shorty_len); - RememberFoGcArgumentVisitor visitor(sp, invoke_type == kStatic, shorty, shorty_len, &soa); + RememberForGcArgumentVisitor visitor(sp, invoke_type == kStatic, shorty, shorty_len, &soa); visitor.VisitArguments(); thread->EndAssertNoThreadSuspension(old_cause); // Resolve method filling in dex cache. diff --git a/runtime/exception_test.cc b/runtime/exception_test.cc index a5f9997..e9a6e4f 100644 --- a/runtime/exception_test.cc +++ b/runtime/exception_test.cc @@ -39,7 +39,7 @@ class ExceptionTest : public CommonTest { ScopedObjectAccess soa(Thread::Current()); SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(LoadDex("ExceptionHandle"))); - my_klass_ = class_linker_->FindClass("LExceptionHandle;", class_loader.get()); + my_klass_ = class_linker_->FindClass("LExceptionHandle;", class_loader); ASSERT_TRUE(my_klass_ != NULL); class_linker_->EnsureInitialized(my_klass_, true, true); diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index 7cbe94d..faa198a 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -82,7 +82,7 @@ class ModUnionUpdateObjectReferencesVisitor { if (ref != nullptr) { Object* new_ref = visitor_(ref, arg_); if (new_ref != ref) { - obj->SetFieldObject(offset, ref, false, true); + obj->SetFieldObject(offset, new_ref, true); } } } @@ -154,7 +154,7 @@ class ModUnionReferenceVisitor { // We don't have an early exit since we use the visitor pattern, an early // exit should significantly speed this up. AddToReferenceArrayVisitor visitor(mod_union_table_, references_); - collector::MarkSweep::VisitObjectReferences(obj, visitor); + collector::MarkSweep::VisitObjectReferences(obj, visitor, true); } private: ModUnionTableReferenceCache* const mod_union_table_; @@ -206,7 +206,7 @@ class ModUnionCheckReferences { Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current()); DCHECK(obj != NULL); CheckReferenceVisitor visitor(mod_union_table_, references_); - collector::MarkSweep::VisitObjectReferences(obj, visitor); + collector::MarkSweep::VisitObjectReferences(obj, visitor, true); } private: @@ -334,7 +334,7 @@ void ModUnionTableCardCache::Dump(std::ostream& os) { for (const byte* card_addr : cleared_cards_) { auto start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr)); auto end = start + CardTable::kCardSize; - os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << ","; + os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << "\n"; } os << "]"; } diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index 6691cad..1789103 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -36,6 +36,7 @@ namespace collector { GarbageCollector::GarbageCollector(Heap* heap, const std::string& name) : heap_(heap), name_(name), + clear_soft_references_(false), verbose_(VLOG_IS_ON(heap)), duration_ns_(0), timings_(name_.c_str(), true, verbose_), @@ -60,11 +61,18 @@ void GarbageCollector::ResetCumulativeStatistics() { total_freed_bytes_ = 0; } -void GarbageCollector::Run() { +void GarbageCollector::Run(bool clear_soft_references) { ThreadList* thread_list = Runtime::Current()->GetThreadList(); uint64_t start_time = NanoTime(); pause_times_.clear(); duration_ns_ = 0; + clear_soft_references_ = clear_soft_references; + + // Reset stats. + freed_bytes_ = 0; + freed_large_object_bytes_ = 0; + freed_objects_ = 0; + freed_large_objects_ = 0; InitializePhase(); diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h index 0f566c9..6111c2f 100644 --- a/runtime/gc/collector/garbage_collector.h +++ b/runtime/gc/collector/garbage_collector.h @@ -46,7 +46,7 @@ class GarbageCollector { virtual GcType GetGcType() const = 0; // Run the garbage collector. - void Run(); + void Run(bool clear_soft_references); Heap* GetHeap() const { return heap_; @@ -78,6 +78,34 @@ class GarbageCollector { // this is the allocation space, for full GC then we swap the zygote bitmaps too. void SwapBitmaps() EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + size_t GetFreedBytes() const { + return freed_bytes_; + } + + size_t GetFreedLargeObjectBytes() const { + return freed_large_object_bytes_; + } + + size_t GetFreedObjects() const { + return freed_objects_; + } + + size_t GetFreedLargeObjects() const { + return freed_large_objects_; + } + + uint64_t GetTotalPausedTimeNs() const { + return total_paused_time_ns_; + } + + uint64_t GetTotalFreedBytes() const { + return total_freed_bytes_; + } + + uint64_t GetTotalFreedObjects() const { + return total_freed_objects_; + } + protected: // The initial phase. Done without mutators paused. virtual void InitializePhase() = 0; @@ -98,6 +126,8 @@ class GarbageCollector { std::string name_; + bool clear_soft_references_; + const bool verbose_; uint64_t duration_ns_; @@ -109,6 +139,12 @@ class GarbageCollector { uint64_t total_freed_objects_; uint64_t total_freed_bytes_; + // Single GC statitstics. + AtomicInteger freed_bytes_; + AtomicInteger freed_large_object_bytes_; + AtomicInteger freed_objects_; + AtomicInteger freed_large_objects_; + CumulativeLogger cumulative_timings_; std::vector<uint64_t> pause_times_; diff --git a/runtime/gc/collector/mark_sweep-inl.h b/runtime/gc/collector/mark_sweep-inl.h index 270c9ef..7a51553 100644 --- a/runtime/gc/collector/mark_sweep-inl.h +++ b/runtime/gc/collector/mark_sweep-inl.h @@ -44,8 +44,7 @@ inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& v if (klass->IsObjectArrayClass()) { VisitObjectArrayReferences(obj->AsObjectArray<mirror::Object>(), visitor); } - } else if (UNLIKELY(klass == java_lang_Class_)) { - DCHECK_EQ(klass->GetClass(), java_lang_Class_); + } else if (UNLIKELY(klass == mirror::Class::GetJavaLangClass())) { if (kCountScannedTypes) { ++class_count_; } @@ -56,7 +55,7 @@ inline void MarkSweep::ScanObjectVisit(mirror::Object* obj, const MarkVisitor& v } VisitOtherReferences(klass, obj, visitor); if (UNLIKELY(klass->IsReferenceClass())) { - DelayReferenceReferent(klass, const_cast<mirror::Object*>(obj)); + DelayReferenceReferent(klass, obj); } } } @@ -68,11 +67,10 @@ inline void MarkSweep::VisitObjectReferences(mirror::Object* obj, const Visitor& Locks::mutator_lock_) { DCHECK(obj != NULL); DCHECK(obj->GetClass() != NULL); - mirror::Class* klass = obj->GetClass(); DCHECK(klass != NULL); if (visit_class) { - visitor(obj, klass, MemberOffset(0), false); + visitor(obj, klass, mirror::Object::ClassOffset(), false); } if (klass == mirror::Class::GetJavaLangClass()) { DCHECK_EQ(klass->GetClass(), mirror::Class::GetJavaLangClass()); @@ -90,8 +88,7 @@ inline void MarkSweep::VisitObjectReferences(mirror::Object* obj, const Visitor& } template <typename Visitor> -inline void MarkSweep::VisitInstanceFieldsReferences(mirror::Class* klass, - mirror::Object* obj, +inline void MarkSweep::VisitInstanceFieldsReferences(mirror::Class* klass, mirror::Object* obj, const Visitor& visitor) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { DCHECK(obj != NULL); @@ -119,11 +116,6 @@ inline void MarkSweep::VisitFieldsReferences(mirror::Object* obj, uint32_t ref_o bool is_static, const Visitor& visitor) { if (LIKELY(ref_offsets != CLASS_WALK_SUPER)) { // Found a reference offset bitmap. Mark the specified offsets. -#ifndef MOVING_COLLECTOR - // Clear the class bit since we mark the class as part of marking the classlinker roots. - DCHECK_EQ(mirror::Object::ClassOffset().Uint32Value(), 0U); - ref_offsets &= (1U << (sizeof(ref_offsets) * 8 - 1)) - 1; -#endif while (ref_offsets != 0) { size_t right_shift = CLZ(ref_offsets); MemberOffset field_offset = CLASS_OFFSET_FROM_CLZ(right_shift); diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 2c69c77..11e911c 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -93,6 +93,8 @@ void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) { } // Add the space to the immune region. + // TODO: Use space limits instead of current end_ since the end_ can be changed by dlmalloc + // callbacks. if (immune_begin_ == NULL) { DCHECK(immune_end_ == NULL); SetImmuneRange(reinterpret_cast<Object*>(space->Begin()), @@ -108,14 +110,14 @@ void MarkSweep::ImmuneSpace(space::ContinuousSpace* space) { } // If previous space was immune, then extend the immune region. Relies on continuous spaces // being sorted by Heap::AddContinuousSpace. - if (prev_space != NULL && IsImmuneSpace(prev_space)) { + if (prev_space != nullptr && IsImmuneSpace(prev_space)) { immune_begin_ = std::min(reinterpret_cast<Object*>(space->Begin()), immune_begin_); immune_end_ = std::max(reinterpret_cast<Object*>(space->End()), immune_end_); } } } -bool MarkSweep::IsImmuneSpace(const space::ContinuousSpace* space) { +bool MarkSweep::IsImmuneSpace(const space::ContinuousSpace* space) const { return immune_begin_ <= reinterpret_cast<Object*>(space->Begin()) && immune_end_ >= reinterpret_cast<Object*>(space->End()); @@ -135,10 +137,9 @@ void MarkSweep::BindBitmaps() { MarkSweep::MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix) : GarbageCollector(heap, - name_prefix + (name_prefix.empty() ? "" : " ") + + name_prefix + (is_concurrent ? "concurrent mark sweep": "mark sweep")), current_mark_bitmap_(NULL), - java_lang_Class_(NULL), mark_stack_(NULL), immune_begin_(NULL), immune_end_(NULL), @@ -150,8 +151,7 @@ MarkSweep::MarkSweep(Heap* heap, bool is_concurrent, const std::string& name_pre gc_barrier_(new Barrier(0)), large_object_lock_("mark sweep large object lock", kMarkSweepLargeObjectLock), mark_stack_lock_("mark sweep mark stack lock", kMarkSweepMarkStackLock), - is_concurrent_(is_concurrent), - clear_soft_references_(false) { + is_concurrent_(is_concurrent) { } void MarkSweep::InitializePhase() { @@ -165,10 +165,6 @@ void MarkSweep::InitializePhase() { finalizer_reference_list_ = nullptr; phantom_reference_list_ = nullptr; cleared_reference_list_ = nullptr; - freed_bytes_ = 0; - freed_large_object_bytes_ = 0; - freed_objects_ = 0; - freed_large_objects_ = 0; class_count_ = 0; array_count_ = 0; other_count_ = 0; @@ -179,8 +175,6 @@ void MarkSweep::InitializePhase() { work_chunks_created_ = 0; work_chunks_deleted_ = 0; reference_count_ = 0; - java_lang_Class_ = Class::GetJavaLangClass(); - CHECK(java_lang_Class_ != nullptr); FindDefaultMarkBitmap(); @@ -294,8 +288,7 @@ void MarkSweep::MarkReachableObjects() { // knowing that new allocations won't be marked as live. timings_.StartSplit("MarkStackAsLive"); accounting::ObjectStack* live_stack = heap_->GetLiveStack(); - heap_->MarkAllocStack(heap_->alloc_space_->GetLiveBitmap(), - heap_->large_object_space_->GetLiveObjects(), live_stack); + heap_->MarkAllocStackAsLive(live_stack); live_stack->Reset(); timings_.EndSplit(); // Recursively mark all the non-image bits set in the mark bitmap. @@ -371,8 +364,10 @@ void MarkSweep::SetImmuneRange(Object* begin, Object* end) { void MarkSweep::FindDefaultMarkBitmap() { base::TimingLogger::ScopedSplit split("FindDefaultMarkBitmap", &timings_); for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { - current_mark_bitmap_ = space->GetMarkBitmap(); + accounting::SpaceBitmap* bitmap = space->GetMarkBitmap(); + if (bitmap != nullptr && + space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { + current_mark_bitmap_ = bitmap; CHECK(current_mark_bitmap_ != NULL); return; } @@ -613,10 +608,8 @@ void MarkSweep::BindLiveToMarkBitmap(space::ContinuousSpace* space) { CHECK(space->IsDlMallocSpace()); space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); - accounting::SpaceBitmap* mark_bitmap = alloc_space->mark_bitmap_.release(); + accounting::SpaceBitmap* mark_bitmap = alloc_space->BindLiveToMarkBitmap(); GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap); - alloc_space->temp_bitmap_.reset(mark_bitmap); - alloc_space->mark_bitmap_.reset(live_bitmap); } class ScanObjectVisitor { @@ -625,7 +618,7 @@ class ScanObjectVisitor { : mark_sweep_(mark_sweep) {} // TODO: Fixme when anotatalysis works with visitors. - void operator()(const Object* obj) const ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS { + void operator()(Object* obj) const ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS { if (kCheckLocks) { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current()); @@ -814,6 +807,9 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) { const size_t mark_stack_delta = std::min(CardScanTask::kMaxSize / 2, mark_stack_size / mark_stack_tasks + 1); for (const auto& space : GetHeap()->GetContinuousSpaces()) { + if (space->GetMarkBitmap() == nullptr) { + continue; + } byte* card_begin = space->Begin(); byte* card_end = space->End(); // Align up the end address. For example, the image space's end @@ -856,24 +852,26 @@ void MarkSweep::ScanGrayObjects(bool paused, byte minimum_age) { timings_.EndSplit(); } else { for (const auto& space : GetHeap()->GetContinuousSpaces()) { - // Image spaces are handled properly since live == marked for them. - switch (space->GetGcRetentionPolicy()) { - case space::kGcRetentionPolicyNeverCollect: - timings_.StartSplit(paused ? "(Paused)ScanGrayImageSpaceObjects" : - "ScanGrayImageSpaceObjects"); - break; - case space::kGcRetentionPolicyFullCollect: - timings_.StartSplit(paused ? "(Paused)ScanGrayZygoteSpaceObjects" : - "ScanGrayZygoteSpaceObjects"); - break; - case space::kGcRetentionPolicyAlwaysCollect: - timings_.StartSplit(paused ? "(Paused)ScanGrayAllocSpaceObjects" : - "ScanGrayAllocSpaceObjects"); - break; - } - ScanObjectVisitor visitor(this); - card_table->Scan(space->GetMarkBitmap(), space->Begin(), space->End(), visitor, minimum_age); - timings_.EndSplit(); + if (space->GetMarkBitmap() != nullptr) { + // Image spaces are handled properly since live == marked for them. + switch (space->GetGcRetentionPolicy()) { + case space::kGcRetentionPolicyNeverCollect: + timings_.StartSplit(paused ? "(Paused)ScanGrayImageSpaceObjects" : + "ScanGrayImageSpaceObjects"); + break; + case space::kGcRetentionPolicyFullCollect: + timings_.StartSplit(paused ? "(Paused)ScanGrayZygoteSpaceObjects" : + "ScanGrayZygoteSpaceObjects"); + break; + case space::kGcRetentionPolicyAlwaysCollect: + timings_.StartSplit(paused ? "(Paused)ScanGrayAllocSpaceObjects" : + "ScanGrayAllocSpaceObjects"); + break; + } + ScanObjectVisitor visitor(this); + card_table->Scan(space->GetMarkBitmap(), space->Begin(), space->End(), visitor, minimum_age); + timings_.EndSplit(); + } } } } @@ -954,9 +952,8 @@ void MarkSweep::RecursiveMark() { if ((space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) || (!partial && space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect)) { current_mark_bitmap_ = space->GetMarkBitmap(); - if (current_mark_bitmap_ == NULL) { - GetHeap()->DumpSpaces(); - LOG(FATAL) << "invalid bitmap"; + if (current_mark_bitmap_ == nullptr) { + continue; } if (parallel) { // We will use the mark stack the future. @@ -1121,7 +1118,7 @@ void MarkSweep::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { } void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitmaps) { - space::DlMallocSpace* space = heap_->GetAllocSpace(); + space::DlMallocSpace* space = heap_->GetNonMovingSpace(); timings_.StartSplit("SweepArray"); // Newly allocated objects MUST be in the alloc space and those are the only objects which we are // going to free. @@ -1207,8 +1204,11 @@ void MarkSweep::Sweep(bool swap_bitmaps) { scc.mark_sweep = this; scc.self = Thread::Current(); for (const auto& space : GetHeap()->GetContinuousSpaces()) { + if (!space->IsDlMallocSpace()) { + continue; + } // We always sweep always collect spaces. - bool sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect); + bool sweep_space = space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect; if (!partial && !sweep_space) { // We sweep full collect spaces when the GC isn't a partial GC (ie its full). sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect); @@ -1370,9 +1370,9 @@ class MarkObjectVisitor { // Scans an object reference. Determines the type of the reference // and dispatches to a specialized scanning routine. -void MarkSweep::ScanObject(const Object* obj) { +void MarkSweep::ScanObject(Object* obj) { MarkObjectVisitor visitor(this); - ScanObjectVisit(const_cast<Object*>(obj), visitor); + ScanObjectVisit(obj, visitor); } void MarkSweep::ProcessMarkStackParallel(size_t thread_count) { @@ -1406,12 +1406,12 @@ void MarkSweep::ProcessMarkStack(bool paused) { } else { // TODO: Tune this. static const size_t kFifoSize = 4; - BoundedFifoPowerOfTwo<const Object*, kFifoSize> prefetch_fifo; + BoundedFifoPowerOfTwo<Object*, kFifoSize> prefetch_fifo; for (;;) { - const Object* obj = NULL; + Object* obj = NULL; if (kUseMarkStackPrefetch) { while (!mark_stack_->IsEmpty() && prefetch_fifo.size() < kFifoSize) { - const Object* obj = mark_stack_->PopBack(); + Object* obj = mark_stack_->PopBack(); DCHECK(obj != NULL); __builtin_prefetch(obj); prefetch_fifo.push_back(obj); @@ -1603,9 +1603,6 @@ void MarkSweep::FinishPhase() { timings_.NewSplit("PostGcVerification"); heap->PostGcVerification(this); - timings_.NewSplit("GrowForUtilization"); - heap->GrowForUtilization(GetGcType(), GetDurationNs()); - timings_.NewSplit("RequestHeapTrim"); heap->RequestHeapTrim(); @@ -1651,8 +1648,10 @@ void MarkSweep::FinishPhase() { // Clear all of the spaces' mark bitmaps. for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->GetGcRetentionPolicy() != space::kGcRetentionPolicyNeverCollect) { - space->GetMarkBitmap()->Clear(); + accounting::SpaceBitmap* bitmap = space->GetMarkBitmap(); + if (bitmap != nullptr && + space->GetGcRetentionPolicy() != space::kGcRetentionPolicyNeverCollect) { + bitmap->Clear(); } } mark_stack_->Reset(); diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 3bc014a..cc58412 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -114,7 +114,7 @@ class MarkSweep : public GarbageCollector { EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - bool IsImmuneSpace(const space::ContinuousSpace* space) + bool IsImmuneSpace(const space::ContinuousSpace* space) const; SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Bind the live bits to the mark bits of bitmaps for spaces that are never collected, ie @@ -140,6 +140,7 @@ class MarkSweep : public GarbageCollector { void ProcessReferences(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Update and mark references from immune spaces. virtual void UpdateAndMarkModUnion() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -158,7 +159,7 @@ class MarkSweep : public GarbageCollector { } // Blackens an object. - void ScanObject(const mirror::Object* obj) + void ScanObject(mirror::Object* obj) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -167,38 +168,6 @@ class MarkSweep : public GarbageCollector { void ScanObjectVisit(mirror::Object* obj, const MarkVisitor& visitor) NO_THREAD_SAFETY_ANALYSIS; - size_t GetFreedBytes() const { - return freed_bytes_; - } - - size_t GetFreedLargeObjectBytes() const { - return freed_large_object_bytes_; - } - - size_t GetFreedObjects() const { - return freed_objects_; - } - - size_t GetFreedLargeObjects() const { - return freed_large_objects_; - } - - uint64_t GetTotalTimeNs() const { - return total_time_ns_; - } - - uint64_t GetTotalPausedTimeNs() const { - return total_paused_time_ns_; - } - - uint64_t GetTotalFreedObjects() const { - return total_freed_objects_; - } - - uint64_t GetTotalFreedBytes() const { - return total_freed_bytes_; - } - // Everything inside the immune range is assumed to be marked. void SetImmuneRange(mirror::Object* begin, mirror::Object* end); @@ -216,8 +185,7 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); template <typename Visitor> - static void VisitObjectReferences(mirror::Object* obj, const Visitor& visitor, - bool visit_class = false) + static void VisitObjectReferences(mirror::Object* obj, const Visitor& visitor, bool visit_class) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); @@ -395,9 +363,6 @@ class MarkSweep : public GarbageCollector { // object. accounting::SpaceBitmap* current_mark_bitmap_; - // Cache java.lang.Class for optimization. - mirror::Class* java_lang_Class_; - accounting::ObjectStack* mark_stack_; // Immune range, every object inside the immune range is assumed to be marked. @@ -412,14 +377,6 @@ class MarkSweep : public GarbageCollector { // Parallel finger. AtomicInteger atomic_finger_; - // Number of non large object bytes freed in this collection. - AtomicInteger freed_bytes_; - // Number of large object bytes freed. - AtomicInteger freed_large_object_bytes_; - // Number of objects freed in this collection. - AtomicInteger freed_objects_; - // Number of freed large objects. - AtomicInteger freed_large_objects_; // Number of classes scanned, if kCountScannedTypes. AtomicInteger class_count_; // Number of arrays scanned, if kCountScannedTypes. @@ -443,8 +400,6 @@ class MarkSweep : public GarbageCollector { const bool is_concurrent_; - bool clear_soft_references_; - private: friend class AddIfReachesAllocSpaceVisitor; // Used by mod-union table. friend class CardScanTask; diff --git a/runtime/gc/collector/partial_mark_sweep.cc b/runtime/gc/collector/partial_mark_sweep.cc index 29367ce..8ec28f3 100644 --- a/runtime/gc/collector/partial_mark_sweep.cc +++ b/runtime/gc/collector/partial_mark_sweep.cc @@ -26,7 +26,7 @@ namespace gc { namespace collector { PartialMarkSweep::PartialMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix) - : MarkSweep(heap, is_concurrent, name_prefix + (name_prefix.empty() ? "" : " ") + "partial") { + : MarkSweep(heap, is_concurrent, name_prefix.empty() ? "partial " : name_prefix) { cumulative_timings_.SetName(GetName()); } diff --git a/runtime/gc/collector/semi_space-inl.h b/runtime/gc/collector/semi_space-inl.h new file mode 100644 index 0000000..3b8f7c3 --- /dev/null +++ b/runtime/gc/collector/semi_space-inl.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_INL_H_ +#define ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_INL_H_ + +namespace art { +namespace gc { +namespace collector { + +inline mirror::Object* SemiSpace::GetForwardingAddressInFromSpace(mirror::Object* obj) const { + DCHECK(from_space_->HasAddress(obj)); + LockWord lock_word = obj->GetLockWord(); + if (lock_word.GetState() != LockWord::kForwardingAddress) { + return nullptr; + } + return reinterpret_cast<mirror::Object*>(lock_word.ForwardingAddress()); +} + +} // namespace collector +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_INL_H_ diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc new file mode 100644 index 0000000..d833631 --- /dev/null +++ b/runtime/gc/collector/semi_space.cc @@ -0,0 +1,799 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "semi_space.h" + +#include <functional> +#include <numeric> +#include <climits> +#include <vector> + +#include "base/logging.h" +#include "base/macros.h" +#include "base/mutex-inl.h" +#include "base/timing_logger.h" +#include "gc/accounting/heap_bitmap.h" +#include "gc/accounting/mod_union_table.h" +#include "gc/accounting/space_bitmap-inl.h" +#include "gc/heap.h" +#include "gc/space/bump_pointer_space.h" +#include "gc/space/bump_pointer_space-inl.h" +#include "gc/space/image_space.h" +#include "gc/space/large_object_space.h" +#include "gc/space/space-inl.h" +#include "indirect_reference_table.h" +#include "intern_table.h" +#include "jni_internal.h" +#include "mark_sweep-inl.h" +#include "monitor.h" +#include "mirror/art_field.h" +#include "mirror/art_field-inl.h" +#include "mirror/class-inl.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache.h" +#include "mirror/object-inl.h" +#include "mirror/object_array.h" +#include "mirror/object_array-inl.h" +#include "runtime.h" +#include "semi_space-inl.h" +#include "thread-inl.h" +#include "thread_list.h" +#include "verifier/method_verifier.h" + +using ::art::mirror::Class; +using ::art::mirror::Object; + +namespace art { +namespace gc { +namespace collector { + +static constexpr bool kProtectFromSpace = true; +static constexpr bool kResetFromSpace = true; + +// TODO: Unduplicate logic. +void SemiSpace::ImmuneSpace(space::ContinuousSpace* space) { + // Bind live to mark bitmap if necessary. + if (space->GetLiveBitmap() != space->GetMarkBitmap()) { + BindLiveToMarkBitmap(space); + } + // Add the space to the immune region. + if (immune_begin_ == nullptr) { + DCHECK(immune_end_ == nullptr); + immune_begin_ = reinterpret_cast<Object*>(space->Begin()); + immune_end_ = reinterpret_cast<Object*>(space->End()); + } else { + const space::ContinuousSpace* prev_space = nullptr; + // Find out if the previous space is immune. + for (space::ContinuousSpace* cur_space : GetHeap()->GetContinuousSpaces()) { + if (cur_space == space) { + break; + } + prev_space = cur_space; + } + // If previous space was immune, then extend the immune region. Relies on continuous spaces + // being sorted by Heap::AddContinuousSpace. + if (prev_space != nullptr && IsImmuneSpace(prev_space)) { + immune_begin_ = std::min(reinterpret_cast<Object*>(space->Begin()), immune_begin_); + immune_end_ = std::max(reinterpret_cast<Object*>(space->End()), immune_end_); + } + } +} + +void SemiSpace::BindBitmaps() { + timings_.StartSplit("BindBitmaps"); + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + // Mark all of the spaces we never collect as immune. + for (const auto& space : GetHeap()->GetContinuousSpaces()) { + if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect + || space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) { + ImmuneSpace(space); + } + } + timings_.EndSplit(); +} + +SemiSpace::SemiSpace(Heap* heap, const std::string& name_prefix) + : GarbageCollector(heap, + name_prefix + (name_prefix.empty() ? "" : " ") + "marksweep + semispace"), + mark_stack_(nullptr), + immune_begin_(nullptr), + immune_end_(nullptr), + to_space_(nullptr), + from_space_(nullptr), + soft_reference_list_(nullptr), + weak_reference_list_(nullptr), + finalizer_reference_list_(nullptr), + phantom_reference_list_(nullptr), + cleared_reference_list_(nullptr), + self_(nullptr) { +} + +void SemiSpace::InitializePhase() { + timings_.Reset(); + base::TimingLogger::ScopedSplit split("InitializePhase", &timings_); + mark_stack_ = heap_->mark_stack_.get(); + DCHECK(mark_stack_ != nullptr); + immune_begin_ = nullptr; + immune_end_ = nullptr; + soft_reference_list_ = nullptr; + weak_reference_list_ = nullptr; + finalizer_reference_list_ = nullptr; + phantom_reference_list_ = nullptr; + cleared_reference_list_ = nullptr; + self_ = Thread::Current(); + // Do any pre GC verification. + timings_.NewSplit("PreGcVerification"); + heap_->PreGcVerification(this); +} + +void SemiSpace::ProcessReferences(Thread* self) { + base::TimingLogger::ScopedSplit split("ProcessReferences", &timings_); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + ProcessReferences(&soft_reference_list_, clear_soft_references_, &weak_reference_list_, + &finalizer_reference_list_, &phantom_reference_list_); +} + +void SemiSpace::MarkingPhase() { + Thread* self = Thread::Current(); + Locks::mutator_lock_->AssertExclusiveHeld(self); + base::TimingLogger::ScopedSplit split("MarkingPhase", &timings_); + // Need to do this with mutators paused so that somebody doesn't accidentally allocate into the + // wrong space. + heap_->SwapSemiSpaces(); + // Assume the cleared space is already empty. + BindBitmaps(); + // Process dirty cards and add dirty cards to mod-union tables. + heap_->ProcessCards(timings_); + // Need to do this before the checkpoint since we don't want any threads to add references to + // the live stack during the recursive mark. + timings_.NewSplit("SwapStacks"); + heap_->SwapStacks(); + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + MarkRoots(); + // Mark roots of immune spaces. + UpdateAndMarkModUnion(); + // Recursively mark remaining objects. + MarkReachableObjects(); +} + +bool SemiSpace::IsImmuneSpace(const space::ContinuousSpace* space) const { + return + immune_begin_ <= reinterpret_cast<Object*>(space->Begin()) && + immune_end_ >= reinterpret_cast<Object*>(space->End()); +} + +void SemiSpace::UpdateAndMarkModUnion() { + for (auto& space : heap_->GetContinuousSpaces()) { + // If the space is immune then we need to mark the references to other spaces. + if (IsImmuneSpace(space)) { + accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space); + CHECK(table != nullptr); + // TODO: Improve naming. + base::TimingLogger::ScopedSplit split( + space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : + "UpdateAndMarkImageModUnionTable", + &timings_); + table->UpdateAndMarkReferences(MarkRootCallback, this); + } + } +} + +void SemiSpace::MarkReachableObjects() { + timings_.StartSplit("MarkStackAsLive"); + accounting::ObjectStack* live_stack = heap_->GetLiveStack(); + heap_->MarkAllocStackAsLive(live_stack); + live_stack->Reset(); + timings_.EndSplit(); + // Recursively process the mark stack. + ProcessMarkStack(true); +} + +void SemiSpace::ReclaimPhase() { + base::TimingLogger::ScopedSplit split("ReclaimPhase", &timings_); + Thread* self = Thread::Current(); + ProcessReferences(self); + { + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); + SweepSystemWeaks(); + } + // Record freed memory. + int from_bytes = from_space_->GetBytesAllocated(); + int to_bytes = to_space_->GetBytesAllocated(); + int from_objects = from_space_->GetObjectsAllocated(); + int to_objects = to_space_->GetObjectsAllocated(); + int freed_bytes = from_bytes - to_bytes; + int freed_objects = from_objects - to_objects; + CHECK_GE(freed_bytes, 0); + freed_bytes_.fetch_add(freed_bytes); + freed_objects_.fetch_add(freed_objects); + heap_->RecordFree(static_cast<size_t>(freed_objects), static_cast<size_t>(freed_bytes)); + + timings_.StartSplit("PreSweepingGcVerification"); + heap_->PreSweepingGcVerification(this); + timings_.EndSplit(); + + { + WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); + // Reclaim unmarked objects. + Sweep(false); + // Swap the live and mark bitmaps for each space which we modified space. This is an + // optimization that enables us to not clear live bits inside of the sweep. Only swaps unbound + // bitmaps. + timings_.StartSplit("SwapBitmaps"); + SwapBitmaps(); + timings_.EndSplit(); + // Unbind the live and mark bitmaps. + UnBindBitmaps(); + } + // Release the memory used by the from space. + if (kResetFromSpace) { + // Clearing from space. + from_space_->Clear(); + } + // Protect the from space. + VLOG(heap) + << "mprotect region " << reinterpret_cast<void*>(from_space_->Begin()) << " - " + << reinterpret_cast<void*>(from_space_->Limit()); + if (kProtectFromSpace) { + mprotect(from_space_->Begin(), from_space_->Capacity(), PROT_NONE); + } else { + mprotect(from_space_->Begin(), from_space_->Capacity(), PROT_READ); + } +} + +void SemiSpace::ResizeMarkStack(size_t new_size) { + std::vector<Object*> temp(mark_stack_->Begin(), mark_stack_->End()); + CHECK_LE(mark_stack_->Size(), new_size); + mark_stack_->Resize(new_size); + for (const auto& obj : temp) { + mark_stack_->PushBack(obj); + } +} + +inline void SemiSpace::MarkStackPush(Object* obj) { + if (UNLIKELY(mark_stack_->Size() >= mark_stack_->Capacity())) { + ResizeMarkStack(mark_stack_->Capacity() * 2); + } + // The object must be pushed on to the mark stack. + mark_stack_->PushBack(obj); +} + +// Rare case, probably not worth inlining since it will increase instruction cache miss rate. +bool SemiSpace::MarkLargeObject(const Object* obj) { + // TODO: support >1 discontinuous space. + space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); + accounting::SpaceSetMap* large_objects = large_object_space->GetMarkObjects(); + if (UNLIKELY(!large_objects->Test(obj))) { + large_objects->Set(obj); + return true; + } + return false; +} + +// Used to mark and copy objects. Any newly-marked objects who are in the from space get moved to +// the to-space and have their forward address updated. Objects which have been newly marked are +// pushed on the mark stack. +Object* SemiSpace::MarkObject(Object* obj) { + Object* ret = obj; + if (obj != nullptr && !IsImmune(obj)) { + if (from_space_->HasAddress(obj)) { + mirror::Object* forward_address = GetForwardingAddressInFromSpace(obj); + // If the object has already been moved, return the new forward address. + if (!to_space_->HasAddress(forward_address)) { + // Otherwise, we need to move the object and add it to the markstack for processing. + size_t object_size = obj->SizeOf(); + size_t dummy = 0; + forward_address = to_space_->Alloc(self_, object_size, &dummy); + // Copy over the object and add it to the mark stack since we still need to update it's + // references. + memcpy(reinterpret_cast<void*>(forward_address), obj, object_size); + // Make sure to only update the forwarding address AFTER you copy the object so that the + // monitor word doesn't get stomped over. + COMPILE_ASSERT(sizeof(uint32_t) == sizeof(mirror::Object*), + monitor_size_must_be_same_as_object); + obj->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast<size_t>(forward_address))); + MarkStackPush(forward_address); + } + ret = forward_address; + // TODO: Do we need this if in the else statement? + } else { + accounting::SpaceBitmap* object_bitmap = heap_->GetMarkBitmap()->GetContinuousSpaceBitmap(obj); + if (LIKELY(object_bitmap != nullptr)) { + // This object was not previously marked. + if (!object_bitmap->Test(obj)) { + object_bitmap->Set(obj); + MarkStackPush(obj); + } + } else { + DCHECK(!to_space_->HasAddress(obj)) << "Marking object in to_space_"; + if (MarkLargeObject(obj)) { + MarkStackPush(obj); + } + } + } + } + return ret; +} + +Object* SemiSpace::MarkRootCallback(Object* root, void* arg) { + DCHECK(root != nullptr); + DCHECK(arg != nullptr); + return reinterpret_cast<SemiSpace*>(arg)->MarkObject(root); +} + +// Marks all objects in the root set. +void SemiSpace::MarkRoots() { + timings_.StartSplit("MarkRoots"); + // TODO: Visit up image roots as well? + Runtime::Current()->VisitRoots(MarkRootCallback, this, false, true); + timings_.EndSplit(); +} + +void SemiSpace::BindLiveToMarkBitmap(space::ContinuousSpace* space) { + CHECK(space->IsDlMallocSpace()); + space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = alloc_space->BindLiveToMarkBitmap(); + GetHeap()->GetMarkBitmap()->ReplaceBitmap(mark_bitmap, live_bitmap); +} + +mirror::Object* SemiSpace::GetForwardingAddress(mirror::Object* obj) { + if (from_space_->HasAddress(obj)) { + LOG(FATAL) << "Shouldn't happen!"; + return GetForwardingAddressInFromSpace(obj); + } + return obj; +} + +mirror::Object* SemiSpace::SystemWeakIsMarkedCallback(Object* object, void* arg) { + return reinterpret_cast<SemiSpace*>(arg)->GetMarkedForwardAddress(object); +} + +void SemiSpace::SweepSystemWeaks() { + timings_.StartSplit("SweepSystemWeaks"); + Runtime::Current()->SweepSystemWeaks(SystemWeakIsMarkedCallback, this); + timings_.EndSplit(); +} + +struct SweepCallbackContext { + SemiSpace* mark_sweep; + space::AllocSpace* space; + Thread* self; +}; + +void SemiSpace::SweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { + SweepCallbackContext* context = static_cast<SweepCallbackContext*>(arg); + SemiSpace* gc = context->mark_sweep; + Heap* heap = gc->GetHeap(); + space::AllocSpace* space = context->space; + Thread* self = context->self; + Locks::heap_bitmap_lock_->AssertExclusiveHeld(self); + size_t freed_bytes = space->FreeList(self, num_ptrs, ptrs); + heap->RecordFree(num_ptrs, freed_bytes); + gc->freed_objects_.fetch_add(num_ptrs); + gc->freed_bytes_.fetch_add(freed_bytes); +} + +void SemiSpace::ZygoteSweepCallback(size_t num_ptrs, Object** ptrs, void* arg) { + SweepCallbackContext* context = static_cast<SweepCallbackContext*>(arg); + Locks::heap_bitmap_lock_->AssertExclusiveHeld(context->self); + Heap* heap = context->mark_sweep->GetHeap(); + // We don't free any actual memory to avoid dirtying the shared zygote pages. + for (size_t i = 0; i < num_ptrs; ++i) { + Object* obj = static_cast<Object*>(ptrs[i]); + heap->GetLiveBitmap()->Clear(obj); + heap->GetCardTable()->MarkCard(obj); + } +} + +void SemiSpace::Sweep(bool swap_bitmaps) { + DCHECK(mark_stack_->IsEmpty()); + base::TimingLogger::ScopedSplit("Sweep", &timings_); + + const bool partial = (GetGcType() == kGcTypePartial); + SweepCallbackContext scc; + scc.mark_sweep = this; + scc.self = Thread::Current(); + for (const auto& space : GetHeap()->GetContinuousSpaces()) { + if (!space->IsDlMallocSpace()) { + continue; + } + // We always sweep always collect spaces. + bool sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect); + if (!partial && !sweep_space) { + // We sweep full collect spaces when the GC isn't a partial GC (ie its full). + sweep_space = (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect); + } + if (sweep_space && space->IsDlMallocSpace()) { + uintptr_t begin = reinterpret_cast<uintptr_t>(space->Begin()); + uintptr_t end = reinterpret_cast<uintptr_t>(space->End()); + scc.space = space->AsDlMallocSpace(); + accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); + if (swap_bitmaps) { + std::swap(live_bitmap, mark_bitmap); + } + if (!space->IsZygoteSpace()) { + base::TimingLogger::ScopedSplit split("SweepAllocSpace", &timings_); + // Bitmaps are pre-swapped for optimization which enables sweeping with the heap unlocked. + accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, + &SweepCallback, reinterpret_cast<void*>(&scc)); + } else { + base::TimingLogger::ScopedSplit split("SweepZygote", &timings_); + // Zygote sweep takes care of dirtying cards and clearing live bits, does not free actual + // memory. + accounting::SpaceBitmap::SweepWalk(*live_bitmap, *mark_bitmap, begin, end, + &ZygoteSweepCallback, reinterpret_cast<void*>(&scc)); + } + } + } + + SweepLargeObjects(swap_bitmaps); +} + +void SemiSpace::SweepLargeObjects(bool swap_bitmaps) { + base::TimingLogger::ScopedSplit("SweepLargeObjects", &timings_); + // Sweep large objects + space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); + accounting::SpaceSetMap* large_live_objects = large_object_space->GetLiveObjects(); + accounting::SpaceSetMap* large_mark_objects = large_object_space->GetMarkObjects(); + if (swap_bitmaps) { + std::swap(large_live_objects, large_mark_objects); + } + // O(n*log(n)) but hopefully there are not too many large objects. + size_t freed_objects = 0; + size_t freed_bytes = 0; + Thread* self = Thread::Current(); + for (const Object* obj : large_live_objects->GetObjects()) { + if (!large_mark_objects->Test(obj)) { + freed_bytes += large_object_space->Free(self, const_cast<Object*>(obj)); + ++freed_objects; + } + } + freed_large_objects_.fetch_add(freed_objects); + freed_large_object_bytes_.fetch_add(freed_bytes); + GetHeap()->RecordFree(freed_objects, freed_bytes); +} + +// Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been +// marked, put it on the appropriate list in the heap for later processing. +void SemiSpace::DelayReferenceReferent(mirror::Class* klass, Object* obj) { + DCHECK(klass != nullptr); + DCHECK(klass->IsReferenceClass()); + DCHECK(obj != nullptr); + Object* referent = heap_->GetReferenceReferent(obj); + if (referent != nullptr) { + Object* forward_address = GetMarkedForwardAddress(referent); + if (forward_address == nullptr) { + Thread* self = Thread::Current(); + // TODO: Remove these locks, and use atomic stacks for storing references? + // We need to check that the references haven't already been enqueued since we can end up + // scanning the same reference multiple times due to dirty cards. + if (klass->IsSoftReferenceClass()) { + MutexLock mu(self, *heap_->GetSoftRefQueueLock()); + if (!heap_->IsEnqueued(obj)) { + heap_->EnqueuePendingReference(obj, &soft_reference_list_); + } + } else if (klass->IsWeakReferenceClass()) { + MutexLock mu(self, *heap_->GetWeakRefQueueLock()); + if (!heap_->IsEnqueued(obj)) { + heap_->EnqueuePendingReference(obj, &weak_reference_list_); + } + } else if (klass->IsFinalizerReferenceClass()) { + MutexLock mu(self, *heap_->GetFinalizerRefQueueLock()); + if (!heap_->IsEnqueued(obj)) { + heap_->EnqueuePendingReference(obj, &finalizer_reference_list_); + } + } else if (klass->IsPhantomReferenceClass()) { + MutexLock mu(self, *heap_->GetPhantomRefQueueLock()); + if (!heap_->IsEnqueued(obj)) { + heap_->EnqueuePendingReference(obj, &phantom_reference_list_); + } + } else { + LOG(FATAL) << "Invalid reference type " << PrettyClass(klass) << " " << std::hex + << klass->GetAccessFlags(); + } + } else if (referent != forward_address) { + heap_->SetReferenceReferent(obj, forward_address); + } + } +} + +// Visit all of the references of an object and update. +void SemiSpace::ScanObject(Object* obj) { + DCHECK(obj != NULL); + DCHECK(!from_space_->HasAddress(obj)) << "Scanning object " << obj << " in from space"; + MarkSweep::VisitObjectReferences(obj, [this](Object* obj, Object* ref, const MemberOffset& offset, + bool /* is_static */) ALWAYS_INLINE NO_THREAD_SAFETY_ANALYSIS { + mirror::Object* new_address = MarkObject(ref); + if (new_address != ref) { + DCHECK(new_address != nullptr); + obj->SetFieldObject(offset, new_address, false); + } + }, kMovingClasses); + mirror::Class* klass = obj->GetClass(); + if (UNLIKELY(klass->IsReferenceClass())) { + DelayReferenceReferent(klass, obj); + } +} + +// Scan anything that's on the mark stack. +void SemiSpace::ProcessMarkStack(bool paused) { + timings_.StartSplit(paused ? "(paused)ProcessMarkStack" : "ProcessMarkStack"); + while (!mark_stack_->IsEmpty()) { + ScanObject(mark_stack_->PopBack()); + } + timings_.EndSplit(); +} + +// Walks the reference list marking any references subject to the +// reference clearing policy. References with a black referent are +// removed from the list. References with white referents biased +// toward saving are blackened and also removed from the list. +void SemiSpace::PreserveSomeSoftReferences(Object** list) { + DCHECK(list != NULL); + Object* clear = NULL; + size_t counter = 0; + DCHECK(mark_stack_->IsEmpty()); + timings_.StartSplit("PreserveSomeSoftReferences"); + while (*list != NULL) { + Object* ref = heap_->DequeuePendingReference(list); + Object* referent = heap_->GetReferenceReferent(ref); + if (referent == NULL) { + // Referent was cleared by the user during marking. + continue; + } + Object* forward_address = GetMarkedForwardAddress(referent); + bool is_marked = forward_address != nullptr; + if (!is_marked && ((++counter) & 1)) { + // Referent is white and biased toward saving, mark it. + forward_address = MarkObject(referent); + if (referent != forward_address) { + // Update the referent if we moved it. + heap_->SetReferenceReferent(ref, forward_address); + } + } else { + if (!is_marked) { + // Referent is white, queue it for clearing. + heap_->EnqueuePendingReference(ref, &clear); + } else if (referent != forward_address) { + CHECK(forward_address != nullptr); + heap_->SetReferenceReferent(ref, forward_address); + } + } + } + *list = clear; + timings_.EndSplit(); + // Restart the mark with the newly black references added to the root set. + ProcessMarkStack(true); +} + +inline Object* SemiSpace::GetMarkedForwardAddress(mirror::Object* obj) const + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + // All immune objects are assumed marked. + if (IsImmune(obj)) { + return obj; + } + if (from_space_->HasAddress(obj)) { + mirror::Object* forwarding_address = GetForwardingAddressInFromSpace(const_cast<Object*>(obj)); + // If the object is forwarded then it MUST be marked. + if (to_space_->HasAddress(forwarding_address)) { + return forwarding_address; + } + // Must not be marked, return nullptr; + return nullptr; + } else if (to_space_->HasAddress(obj)) { + // Already forwarded, must be marked. + return obj; + } + return heap_->GetMarkBitmap()->Test(obj) ? obj : nullptr; +} + +// Unlink the reference list clearing references objects with white +// referents. Cleared references registered to a reference queue are +// scheduled for appending by the heap worker thread. +void SemiSpace::ClearWhiteReferences(Object** list) { + DCHECK(list != NULL); + while (*list != NULL) { + Object* ref = heap_->DequeuePendingReference(list); + Object* referent = heap_->GetReferenceReferent(ref); + if (referent != nullptr) { + Object* forward_address = GetMarkedForwardAddress(referent); + if (forward_address == nullptr) { + // Referent is white, clear it. + heap_->ClearReferenceReferent(ref); + if (heap_->IsEnqueuable(ref)) { + heap_->EnqueueReference(ref, &cleared_reference_list_); + } + } else if (referent != forward_address) { + heap_->SetReferenceReferent(ref, forward_address); + } + } + } + DCHECK(*list == NULL); +} + +// Enqueues finalizer references with white referents. White +// referents are blackened, moved to the zombie field, and the +// referent field is cleared. +void SemiSpace::EnqueueFinalizerReferences(Object** list) { + // *list = NULL; + // return; + DCHECK(list != NULL); + timings_.StartSplit("EnqueueFinalizerReferences"); + MemberOffset zombie_offset = heap_->GetFinalizerReferenceZombieOffset(); + bool has_enqueued = false; + while (*list != NULL) { + Object* ref = heap_->DequeuePendingReference(list); + Object* referent = heap_->GetReferenceReferent(ref); + if (referent != nullptr) { + Object* forward_address = GetMarkedForwardAddress(referent); + // Not marked. + if (forward_address == nullptr) { + forward_address = MarkObject(referent); + // If the referent is non-null the reference must queuable. + DCHECK(heap_->IsEnqueuable(ref)); + // Move the referent to the zombie field. + ref->SetFieldObject(zombie_offset, forward_address, false); + heap_->ClearReferenceReferent(ref); + heap_->EnqueueReference(ref, &cleared_reference_list_); + has_enqueued = true; + } else if (referent != forward_address) { + heap_->SetReferenceReferent(ref, forward_address); + } + } + } + timings_.EndSplit(); + if (has_enqueued) { + ProcessMarkStack(true); + } + DCHECK(*list == NULL); +} + +// Process reference class instances and schedule finalizations. +void SemiSpace::ProcessReferences(Object** soft_references, bool clear_soft, + Object** weak_references, + Object** finalizer_references, + Object** phantom_references) { + CHECK(soft_references != NULL); + CHECK(weak_references != NULL); + CHECK(finalizer_references != NULL); + CHECK(phantom_references != NULL); + CHECK(mark_stack_->IsEmpty()); + + // Unless we are in the zygote or required to clear soft references + // with white references, preserve some white referents. + if (!clear_soft && !Runtime::Current()->IsZygote()) { + PreserveSomeSoftReferences(soft_references); + } + + timings_.StartSplit("ProcessReferences"); + // Clear all remaining soft and weak references with white + // referents. + ClearWhiteReferences(soft_references); + ClearWhiteReferences(weak_references); + timings_.EndSplit(); + + // Preserve all white objects with finalize methods and schedule + // them for finalization. + EnqueueFinalizerReferences(finalizer_references); + + timings_.StartSplit("ProcessReferences"); + // Clear all f-reachable soft and weak references with white + // referents. + ClearWhiteReferences(soft_references); + ClearWhiteReferences(weak_references); + + // Clear all phantom references with white referents. + ClearWhiteReferences(phantom_references); + + // At this point all reference lists should be empty. + DCHECK(*soft_references == NULL); + DCHECK(*weak_references == NULL); + DCHECK(*finalizer_references == NULL); + DCHECK(*phantom_references == NULL); + timings_.EndSplit(); +} + +void SemiSpace::UnBindBitmaps() { + base::TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); + for (const auto& space : GetHeap()->GetContinuousSpaces()) { + if (space->IsDlMallocSpace()) { + space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + if (alloc_space->HasBoundBitmaps()) { + alloc_space->UnBindBitmaps(); + heap_->GetMarkBitmap()->ReplaceBitmap(alloc_space->GetLiveBitmap(), + alloc_space->GetMarkBitmap()); + } + } + } +} + +void SemiSpace::SetToSpace(space::ContinuousMemMapAllocSpace* to_space) { + DCHECK(to_space != nullptr); + to_space_ = to_space; +} + +void SemiSpace::SetFromSpace(space::ContinuousMemMapAllocSpace* from_space) { + DCHECK(from_space != nullptr); + from_space_ = from_space; +} + +void SemiSpace::FinishPhase() { + base::TimingLogger::ScopedSplit split("FinishPhase", &timings_); + // Can't enqueue references if we hold the mutator lock. + Object* cleared_references = GetClearedReferences(); + Heap* heap = GetHeap(); + timings_.NewSplit("EnqueueClearedReferences"); + heap->EnqueueClearedReferences(&cleared_references); + + timings_.NewSplit("PostGcVerification"); + heap->PostGcVerification(this); + + // Null the "to" and "from" spaces since compacting from one to the other isn't valid until + // further action is done by the heap. + to_space_ = nullptr; + from_space_ = nullptr; + + // Update the cumulative statistics + total_time_ns_ += GetDurationNs(); + total_paused_time_ns_ += std::accumulate(GetPauseTimes().begin(), GetPauseTimes().end(), 0, + std::plus<uint64_t>()); + total_freed_objects_ += GetFreedObjects() + GetFreedLargeObjects(); + total_freed_bytes_ += GetFreedBytes() + GetFreedLargeObjectBytes(); + + // Ensure that the mark stack is empty. + CHECK(mark_stack_->IsEmpty()); + + // Update the cumulative loggers. + cumulative_timings_.Start(); + cumulative_timings_.AddLogger(timings_); + cumulative_timings_.End(); + + // Clear all of the spaces' mark bitmaps. + for (const auto& space : GetHeap()->GetContinuousSpaces()) { + accounting::SpaceBitmap* bitmap = space->GetMarkBitmap(); + if (bitmap != nullptr && + space->GetGcRetentionPolicy() != space::kGcRetentionPolicyNeverCollect) { + bitmap->Clear(); + } + } + mark_stack_->Reset(); + + // Reset the marked large objects. + space::LargeObjectSpace* large_objects = GetHeap()->GetLargeObjectsSpace(); + large_objects->GetMarkObjects()->Clear(); +} + +} // namespace collector +} // namespace gc +} // namespace art diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h new file mode 100644 index 0000000..13d5195 --- /dev/null +++ b/runtime/gc/collector/semi_space.h @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_H_ +#define ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_H_ + +#include "atomic_integer.h" +#include "barrier.h" +#include "base/macros.h" +#include "base/mutex.h" +#include "garbage_collector.h" +#include "offsets.h" +#include "root_visitor.h" +#include "UniquePtr.h" + +namespace art { + +namespace mirror { + class Class; + class Object; + template<class T> class ObjectArray; +} // namespace mirror + +class StackVisitor; +class Thread; + +namespace gc { + +namespace accounting { + template <typename T> class AtomicStack; + class MarkIfReachesAllocspaceVisitor; + class ModUnionClearCardVisitor; + class ModUnionVisitor; + class ModUnionTableBitmap; + class MarkStackChunk; + typedef AtomicStack<mirror::Object*> ObjectStack; + class SpaceBitmap; +} // namespace accounting + +namespace space { + class BumpPointerSpace; + class ContinuousMemMapAllocSpace; + class ContinuousSpace; +} // namespace space + +class Heap; + +namespace collector { + +class SemiSpace : public GarbageCollector { + public: + explicit SemiSpace(Heap* heap, const std::string& name_prefix = ""); + + ~SemiSpace() {} + + virtual void InitializePhase(); + virtual bool IsConcurrent() const { + return false; + } + virtual void MarkingPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void ReclaimPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void FinishPhase() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + virtual void MarkReachableObjects() + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + virtual GcType GetGcType() const { + return kGcTypePartial; + } + + // Sets which space we will be copying objects to. + void SetToSpace(space::ContinuousMemMapAllocSpace* to_space); + + // Set the space where we copy objects from. + void SetFromSpace(space::ContinuousMemMapAllocSpace* from_space); + + // Initializes internal structures. + void Init(); + + // Find the default mark bitmap. + void FindDefaultMarkBitmap(); + + // Returns the new address of the object. + mirror::Object* MarkObject(mirror::Object* object) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + void ScanObject(mirror::Object* obj) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Marks the root set at the start of a garbage collection. + void MarkRoots() + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Make a space immune, immune spaces have all live objects marked - that is the mark and + // live bitmaps are bound together. + void ImmuneSpace(space::ContinuousSpace* space) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Bind the live bits to the mark bits of bitmaps for spaces that are never collected, ie + // the image. Mark that portion of the heap as immune. + virtual void BindBitmaps() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void BindLiveToMarkBitmap(space::ContinuousSpace* space) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + void UnBindBitmaps() + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + void ProcessReferences(Thread* self) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Sweeps unmarked objects to complete the garbage collection. + virtual void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Sweeps unmarked objects to complete the garbage collection. + void SweepLargeObjects(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Sweep only pointers within an array. WARNING: Trashes objects. + void SweepArray(accounting::ObjectStack* allocation_stack_, bool swap_bitmaps) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + mirror::Object* GetClearedReferences() { + return cleared_reference_list_; + } + + // TODO: enable thread safety analysis when in use by multiple worker threads. + template <typename MarkVisitor> + void ScanObjectVisit(const mirror::Object* obj, const MarkVisitor& visitor) + NO_THREAD_SAFETY_ANALYSIS; + + void SweepSystemWeaks() + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + template <typename Visitor> + static void VisitObjectReferencesAndClass(mirror::Object* obj, const Visitor& visitor) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + static mirror::Object* MarkRootCallback(mirror::Object* root, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + protected: + // Returns null if the object is not marked, otherwise returns the forwarding address (same as + // object for non movable things). + mirror::Object* GetMarkedForwardAddress(mirror::Object* object) const; + + static mirror::Object* SystemWeakIsMarkedCallback(mirror::Object* object, void* arg) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Marks or unmarks a large object based on whether or not set is true. If set is true, then we + // mark, otherwise we unmark. + bool MarkLargeObject(const mirror::Object* obj) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Special sweep for zygote that just marks objects / dirties cards. + static void ZygoteSweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + // Expand mark stack to 2x its current size. + void ResizeMarkStack(size_t new_size); + + // 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 ALWAYS_INLINE { + return obj >= immune_begin_ && obj < immune_end_; + } + + bool IsImmuneSpace(const space::ContinuousSpace* space) const; + + static void VerifyRootCallback(const mirror::Object* root, void* arg, size_t vreg, + const StackVisitor *visitor); + + void VerifyRoot(const mirror::Object* root, size_t vreg, const StackVisitor* visitor) + NO_THREAD_SAFETY_ANALYSIS; + + template <typename Visitor> + static void VisitInstanceFieldsReferences(const mirror::Class* klass, const mirror::Object* obj, + const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Visit the header, static field references, and interface pointers of a class object. + template <typename Visitor> + static void VisitClassReferences(const mirror::Class* klass, const mirror::Object* obj, + const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + template <typename Visitor> + static void VisitStaticFieldsReferences(const mirror::Class* klass, const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + template <typename Visitor> + static void VisitFieldsReferences(const mirror::Object* obj, uint32_t ref_offsets, bool is_static, + const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Visit all of the references in an object array. + template <typename Visitor> + static void VisitObjectArrayReferences(const mirror::ObjectArray<mirror::Object>* array, + const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Visits the header and field references of a data object. + template <typename Visitor> + static void VisitOtherReferences(const mirror::Class* klass, const mirror::Object* obj, + const Visitor& visitor) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) { + return VisitInstanceFieldsReferences(klass, obj, visitor); + } + + // Push an object onto the mark stack. + inline void MarkStackPush(mirror::Object* obj); + + void UpdateAndMarkModUnion() + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Schedules an unmarked object for reference processing. + void DelayReferenceReferent(mirror::Class* klass, mirror::Object* reference) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + // Recursively blackens objects on the mark stack. + void ProcessMarkStack(bool paused) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + void EnqueueFinalizerReferences(mirror::Object** ref) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + void PreserveSomeSoftReferences(mirror::Object** ref) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + void ClearWhiteReferences(mirror::Object** list) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + void ProcessReferences(mirror::Object** soft_references, bool clear_soft_references, + mirror::Object** weak_references, + mirror::Object** finalizer_references, + mirror::Object** phantom_references) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_); + + inline mirror::Object* GetForwardingAddressInFromSpace(mirror::Object* obj) const; + + mirror::Object* GetForwardingAddress(mirror::Object* obj); + + // Current space, we check this space first to avoid searching for the appropriate space for an + // object. + accounting::ObjectStack* mark_stack_; + + // Immune range, every object inside the immune range is assumed to be marked. + mirror::Object* immune_begin_; + mirror::Object* immune_end_; + + // Destination and source spaces. + space::ContinuousMemMapAllocSpace* to_space_; + space::ContinuousMemMapAllocSpace* from_space_; + + mirror::Object* soft_reference_list_; + mirror::Object* weak_reference_list_; + mirror::Object* finalizer_reference_list_; + mirror::Object* phantom_reference_list_; + mirror::Object* cleared_reference_list_; + + Thread* self_; + + private: + DISALLOW_COPY_AND_ASSIGN(SemiSpace); +}; + +} // namespace collector +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_COLLECTOR_SEMI_SPACE_H_ diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc index 9f0bf33..b27b8df 100644 --- a/runtime/gc/collector/sticky_mark_sweep.cc +++ b/runtime/gc/collector/sticky_mark_sweep.cc @@ -26,7 +26,7 @@ namespace collector { StickyMarkSweep::StickyMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix) : PartialMarkSweep(heap, is_concurrent, - name_prefix + (name_prefix.empty() ? "" : " ") + "sticky") { + name_prefix.empty() ? "sticky " : name_prefix) { cumulative_timings_.SetName(GetName()); } @@ -38,7 +38,8 @@ void StickyMarkSweep::BindBitmaps() { // know what was allocated since the last GC. A side-effect of binding the allocation space mark // and live bitmap is that marking the objects will place them in the live bitmap. for (const auto& space : GetHeap()->GetContinuousSpaces()) { - if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { + if (space->IsDlMallocSpace() && + space->GetGcRetentionPolicy() == space::kGcRetentionPolicyAlwaysCollect) { BindLiveToMarkBitmap(space); } } diff --git a/runtime/gc/collector/sticky_mark_sweep.h b/runtime/gc/collector/sticky_mark_sweep.h index 8bee00f..b675877 100644 --- a/runtime/gc/collector/sticky_mark_sweep.h +++ b/runtime/gc/collector/sticky_mark_sweep.h @@ -31,10 +31,6 @@ class StickyMarkSweep : public PartialMarkSweep { return kGcTypeSticky; } - // Don't need to do anything special here since we scan all the cards which may have references - // to the newly allocated objects. - virtual void UpdateAndMarkModUnion() { } - explicit StickyMarkSweep(Heap* heap, bool is_concurrent, const std::string& name_prefix = ""); ~StickyMarkSweep() {} @@ -53,6 +49,10 @@ class StickyMarkSweep : public PartialMarkSweep { void Sweep(bool swap_bitmaps) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + // Don't need to do anything special here since we scan all the cards which may have references + // to the newly allocated objects. + virtual void UpdateAndMarkModUnion() { } + private: DISALLOW_COPY_AND_ASSIGN(StickyMarkSweep); }; diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h index 873eadc..1d3c0d8 100644 --- a/runtime/gc/heap-inl.h +++ b/runtime/gc/heap-inl.h @@ -20,6 +20,7 @@ #include "heap.h" #include "debugger.h" +#include "gc/space/bump_pointer_space-inl.h" #include "gc/space/dlmalloc_space-inl.h" #include "gc/space/large_object_space.h" #include "object_utils.h" @@ -30,8 +31,9 @@ namespace art { namespace gc { -inline mirror::Object* Heap::AllocObjectUninstrumented(Thread* self, mirror::Class* c, size_t byte_count) { - DebugCheckPreconditionsForAllobObject(c, byte_count); +inline mirror::Object* Heap::AllocNonMovableObjectUninstrumented(Thread* self, mirror::Class* c, + size_t byte_count) { + DebugCheckPreconditionsForAllocObject(c, byte_count); mirror::Object* obj; size_t bytes_allocated; AllocationTimer alloc_timer(this, &obj); @@ -39,7 +41,7 @@ inline mirror::Object* Heap::AllocObjectUninstrumented(Thread* self, mirror::Cla &obj, &bytes_allocated); if (LIKELY(!large_object_allocation)) { // Non-large object allocation. - obj = AllocateUninstrumented(self, alloc_space_, byte_count, &bytes_allocated); + obj = AllocateUninstrumented(self, non_moving_space_, byte_count, &bytes_allocated); // Ensure that we did not allocate into a zygote space. DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); } @@ -53,10 +55,45 @@ inline mirror::Object* Heap::AllocObjectUninstrumented(Thread* self, mirror::Cla if (kDesiredHeapVerification > kNoHeapVerification) { VerifyObject(obj); } - return obj; + } else { + ThrowOutOfMemoryError(self, byte_count, large_object_allocation); } - ThrowOutOfMemoryError(self, byte_count, large_object_allocation); - return NULL; + if (kIsDebugBuild) { + self->VerifyStack(); + } + return obj; +} + +inline mirror::Object* Heap::AllocMovableObjectUninstrumented(Thread* self, mirror::Class* c, + size_t byte_count) { + DebugCheckPreconditionsForAllocObject(c, byte_count); + mirror::Object* obj; + AllocationTimer alloc_timer(this, &obj); + byte_count = (byte_count + 7) & ~7; + if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, false))) { + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, false); + if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, true))) { + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); + } + } + obj = bump_pointer_space_->AllocNonvirtual(byte_count); + if (LIKELY(obj != NULL)) { + obj->SetClass(c); + DCHECK(!obj->IsClass()); + // Record allocation after since we want to use the atomic add for the atomic fence to guard + // the SetClass since we do not want the class to appear NULL in another thread. + num_bytes_allocated_.fetch_add(byte_count); + DCHECK(!Dbg::IsAllocTrackingEnabled()); + if (kDesiredHeapVerification > kNoHeapVerification) { + VerifyObject(obj); + } + } else { + ThrowOutOfMemoryError(self, byte_count, false); + } + if (kIsDebugBuild) { + self->VerifyStack(); + } + return obj; } inline size_t Heap::RecordAllocationUninstrumented(size_t size, mirror::Object* obj) { @@ -124,7 +161,7 @@ inline bool Heap::TryAllocLargeObjectUninstrumented(Thread* self, mirror::Class* return large_object_allocation; } -inline void Heap::DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count) { +inline void Heap::DebugCheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) { DCHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) || (c->IsVariableSize() || c->GetObjectSize() == byte_count) || strlen(ClassHelper(c).GetDescriptor()) == 0); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index de3ab0e..69ca620 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -30,11 +30,14 @@ #include "gc/accounting/atomic_stack.h" #include "gc/accounting/card_table-inl.h" #include "gc/accounting/heap_bitmap-inl.h" +#include "gc/accounting/mod_union_table.h" #include "gc/accounting/mod_union_table-inl.h" #include "gc/accounting/space_bitmap-inl.h" #include "gc/collector/mark_sweep-inl.h" #include "gc/collector/partial_mark_sweep.h" +#include "gc/collector/semi_space.h" #include "gc/collector/sticky_mark_sweep.h" +#include "gc/space/bump_pointer_space.h" #include "gc/space/dlmalloc_space-inl.h" #include "gc/space/image_space.h" #include "gc/space/large_object_space.h" @@ -70,9 +73,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max 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), + : non_moving_space_(NULL), + concurrent_gc_(!kMovingCollector && concurrent_gc), parallel_gc_threads_(parallel_gc_threads), conc_gc_threads_(conc_gc_threads), low_memory_mode_(low_memory_mode), @@ -92,6 +94,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max max_allowed_footprint_(initial_size), native_footprint_gc_watermark_(initial_size), native_footprint_limit_(2 * initial_size), + native_need_to_run_finalization_(false), activity_thread_class_(NULL), application_thread_class_(NULL), activity_thread_(NULL), @@ -122,7 +125,9 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max * searching. */ max_allocation_stack_size_(kGCALotMode ? kGcAlotInterval - : (kDesiredHeapVerification > kNoHeapVerification) ? KB : MB), + : (kDesiredHeapVerification > kVerifyAllFast) ? KB : MB), + bump_pointer_space_(nullptr), + temp_space_(nullptr), reference_referent_offset_(0), reference_queue_offset_(0), reference_queueNext_offset_(0), @@ -134,6 +139,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max total_wait_time_(0), total_allocation_time_(0), verify_object_mode_(kHeapVerificationNotPermitted), + gc_disable_count_(0), running_on_valgrind_(RUNNING_ON_VALGRIND) { if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; @@ -147,7 +153,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max if (!image_file_name.empty()) { space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str()); CHECK(image_space != NULL) << "Failed to create space for " << image_file_name; - AddContinuousSpace(image_space); + AddSpace(image_space); // Oat files referenced by image files immediately follow them in memory, ensure alloc space // isn't going to get in the middle byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd(); @@ -159,13 +165,28 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } } - alloc_space_ = space::DlMallocSpace::Create(Runtime::Current()->IsZygote() ? "zygote space" : "alloc space", - initial_size, - growth_limit, capacity, - requested_alloc_space_begin); - CHECK(alloc_space_ != NULL) << "Failed to create alloc space"; - alloc_space_->SetFootprintLimit(alloc_space_->Capacity()); - AddContinuousSpace(alloc_space_); + const char* name = Runtime::Current()->IsZygote() ? "zygote space" : "alloc space"; + non_moving_space_ = space::DlMallocSpace::Create(name, initial_size, growth_limit, capacity, + requested_alloc_space_begin); + + if (kMovingCollector) { + // TODO: Place bump-pointer spaces somewhere to minimize size of card table. + // TODO: Having 3+ spaces as big as the large heap size can cause virtual memory fragmentation + // issues. + const size_t bump_pointer_space_size = std::min(non_moving_space_->Capacity(), 128 * MB); + bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space", + bump_pointer_space_size, nullptr); + CHECK(bump_pointer_space_ != nullptr) << "Failed to create bump pointer space"; + AddSpace(bump_pointer_space_); + temp_space_ = space::BumpPointerSpace::Create("Bump pointer space 2", bump_pointer_space_size, + nullptr); + CHECK(temp_space_ != nullptr) << "Failed to create bump pointer space"; + AddSpace(temp_space_); + } + + CHECK(non_moving_space_ != NULL) << "Failed to create non-moving space"; + non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); + AddSpace(non_moving_space_); // Allocate the large object space. const bool kUseFreeListSpaceForLOS = false; @@ -175,22 +196,23 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max large_object_space_ = space::LargeObjectMapSpace::Create("large object space"); } CHECK(large_object_space_ != NULL) << "Failed to create large object space"; - AddDiscontinuousSpace(large_object_space_); + AddSpace(large_object_space_); // Compute heap capacity. Continuous spaces are sorted in order of Begin(). + CHECK(!continuous_spaces_.empty()); + // Relies on the spaces being sorted. byte* heap_begin = continuous_spaces_.front()->Begin(); - size_t heap_capacity = continuous_spaces_.back()->End() - continuous_spaces_.front()->Begin(); - if (continuous_spaces_.back()->IsDlMallocSpace()) { - heap_capacity += continuous_spaces_.back()->AsDlMallocSpace()->NonGrowthLimitCapacity(); - } + byte* heap_end = continuous_spaces_.back()->Limit(); + size_t heap_capacity = heap_end - heap_begin; // Allocate the card table. card_table_.reset(accounting::CardTable::Create(heap_begin, heap_capacity)); CHECK(card_table_.get() != NULL) << "Failed to create card table"; + // Card cache for now since it makes it easier for us to update the references to the copying + // spaces. accounting::ModUnionTable* mod_union_table = - new accounting::ModUnionTableToZygoteAllocspace("Image mod-union table", this, - GetImageSpace()); + new accounting::ModUnionTableCardCache("Image mod-union table", this, GetImageSpace()); CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table"; AddModUnionTable(mod_union_table); @@ -223,19 +245,23 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max if (ignore_max_footprint_) { SetIdealFootprint(std::numeric_limits<size_t>::max()); - concurrent_start_bytes_ = max_allowed_footprint_; + concurrent_start_bytes_ = std::numeric_limits<size_t>::max(); } + CHECK_NE(max_allowed_footprint_, 0U); // Create our garbage collectors. - for (size_t i = 0; i < 2; ++i) { - const bool concurrent = i != 0; - mark_sweep_collectors_.push_back(new collector::MarkSweep(this, concurrent)); - mark_sweep_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); - mark_sweep_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); + if (!kMovingCollector) { + for (size_t i = 0; i < 2; ++i) { + const bool concurrent = i != 0; + garbage_collectors_.push_back(new collector::MarkSweep(this, concurrent)); + garbage_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); + garbage_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); + } + } else { + semi_space_collector_ = new collector::SemiSpace(this); + garbage_collectors_.push_back(semi_space_collector_); } - CHECK_NE(max_allowed_footprint_, 0U); - if (running_on_valgrind_) { Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(); } @@ -245,6 +271,41 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } } +bool Heap::IsCompilingBoot() const { + for (const auto& space : continuous_spaces_) { + if (space->IsImageSpace()) { + return false; + } else if (space->IsZygoteSpace()) { + return false; + } + } + return true; +} + +bool Heap::HasImageSpace() const { + for (const auto& space : continuous_spaces_) { + if (space->IsImageSpace()) { + return true; + } + } + return false; +} + +void Heap::IncrementDisableGC(Thread* self) { + // Need to do this holding the lock to prevent races where the GC is about to run / running when + // we attempt to disable it. + ScopedThreadStateChange tsc(self, kWaitingForGcToComplete); + MutexLock mu(self, *gc_complete_lock_); + WaitForGcToCompleteLocked(self); + ++gc_disable_count_; +} + +void Heap::DecrementDisableGC(Thread* self) { + MutexLock mu(self, *gc_complete_lock_); + CHECK_GE(gc_disable_count_, 0U); + --gc_disable_count_; +} + void Heap::CreateThreadPool() { const size_t num_threads = std::max(parallel_gc_threads_, conc_gc_threads_); if (num_threads != 0) { @@ -252,12 +313,49 @@ void Heap::CreateThreadPool() { } } +void Heap::VisitObjects(ObjectVisitorCallback callback, void* arg) { + // Visit objects in bump pointer space. + Thread* self = Thread::Current(); + // TODO: Use reference block. + std::vector<SirtRef<mirror::Object>*> saved_refs; + if (bump_pointer_space_ != nullptr) { + // Need to put all these in sirts since the callback may trigger a GC. TODO: Use a better data + // structure. + mirror::Object* obj = reinterpret_cast<mirror::Object*>(bump_pointer_space_->Begin()); + const mirror::Object* end = reinterpret_cast<const mirror::Object*>( + bump_pointer_space_->End()); + while (obj < end) { + saved_refs.push_back(new SirtRef<mirror::Object>(self, obj)); + obj = space::BumpPointerSpace::GetNextObject(obj); + } + } + // TODO: Switch to standard begin and end to use ranged a based loop. + for (mirror::Object** it = allocation_stack_->Begin(), **end = allocation_stack_->End(); + it < end; ++it) { + mirror::Object* obj = *it; + // Objects in the allocation stack might be in a movable space. + saved_refs.push_back(new SirtRef<mirror::Object>(self, obj)); + } + GetLiveBitmap()->Walk(callback, arg); + for (const auto& ref : saved_refs) { + callback(ref->get(), arg); + } + // Need to free the sirts in reverse order they were allocated. + for (size_t i = saved_refs.size(); i != 0; --i) { + delete saved_refs[i - 1]; + } +} + +void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) { + MarkAllocStack(non_moving_space_->GetLiveBitmap(), large_object_space_->GetLiveObjects(), stack); +} + void Heap::DeleteThreadPool() { thread_pool_.reset(nullptr); } static bool ReadStaticInt(JNIEnvExt* env, jclass clz, const char* name, int* out_value) { - CHECK(out_value != NULL); + DCHECK(out_value != NULL); jfieldID field = env->GetStaticFieldID(clz, name, "I"); if (field == NULL) { env->ExceptionClear(); @@ -374,62 +472,71 @@ void Heap::ListenForProcessStateChange() { } } -void Heap::AddContinuousSpace(space::ContinuousSpace* space) { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); +void Heap::AddSpace(space::Space* space) { DCHECK(space != NULL); - DCHECK(space->GetLiveBitmap() != NULL); - live_bitmap_->AddContinuousSpaceBitmap(space->GetLiveBitmap()); - DCHECK(space->GetMarkBitmap() != NULL); - mark_bitmap_->AddContinuousSpaceBitmap(space->GetMarkBitmap()); - continuous_spaces_.push_back(space); - if (space->IsDlMallocSpace() && !space->IsLargeObjectSpace()) { - alloc_space_ = space->AsDlMallocSpace(); - } - - // Ensure that spaces remain sorted in increasing order of start address (required for CMS finger) - std::sort(continuous_spaces_.begin(), continuous_spaces_.end(), - [](const space::ContinuousSpace* a, const space::ContinuousSpace* b) { - return a->Begin() < b->Begin(); - }); - - // Ensure that ImageSpaces < ZygoteSpaces < AllocSpaces so that we can do address based checks to - // avoid redundant marking. - bool seen_zygote = false, seen_alloc = false; - for (const auto& space : continuous_spaces_) { - if (space->IsImageSpace()) { - DCHECK(!seen_zygote); - DCHECK(!seen_alloc); - } else if (space->IsZygoteSpace()) { - DCHECK(!seen_alloc); - seen_zygote = true; - } else if (space->IsDlMallocSpace()) { - seen_alloc = true; + WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + if (space->IsContinuousSpace()) { + DCHECK(!space->IsDiscontinuousSpace()); + space::ContinuousSpace* continuous_space = space->AsContinuousSpace(); + // Continuous spaces don't necessarily have bitmaps. + accounting::SpaceBitmap* live_bitmap = continuous_space->GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = continuous_space->GetMarkBitmap(); + if (live_bitmap != nullptr) { + DCHECK(mark_bitmap != nullptr); + live_bitmap_->AddContinuousSpaceBitmap(live_bitmap); + mark_bitmap_->AddContinuousSpaceBitmap(mark_bitmap); + } + + continuous_spaces_.push_back(continuous_space); + if (continuous_space->IsDlMallocSpace()) { + non_moving_space_ = continuous_space->AsDlMallocSpace(); + } + + // Ensure that spaces remain sorted in increasing order of start address. + std::sort(continuous_spaces_.begin(), continuous_spaces_.end(), + [](const space::ContinuousSpace* a, const space::ContinuousSpace* b) { + return a->Begin() < b->Begin(); + }); + // Ensure that ImageSpaces < ZygoteSpaces < AllocSpaces so that we can do address based checks to + // avoid redundant marking. + bool seen_zygote = false, seen_alloc = false; + for (const auto& space : continuous_spaces_) { + if (space->IsImageSpace()) { + CHECK(!seen_zygote); + CHECK(!seen_alloc); + } else if (space->IsZygoteSpace()) { + CHECK(!seen_alloc); + seen_zygote = true; + } else if (space->IsDlMallocSpace()) { + seen_alloc = true; + } } + } else { + DCHECK(space->IsDiscontinuousSpace()); + space::DiscontinuousSpace* discontinuous_space = space->AsDiscontinuousSpace(); + DCHECK(discontinuous_space->GetLiveObjects() != nullptr); + live_bitmap_->AddDiscontinuousObjectSet(discontinuous_space->GetLiveObjects()); + DCHECK(discontinuous_space->GetMarkObjects() != nullptr); + mark_bitmap_->AddDiscontinuousObjectSet(discontinuous_space->GetMarkObjects()); + discontinuous_spaces_.push_back(discontinuous_space); + } + if (space->IsAllocSpace()) { + alloc_spaces_.push_back(space->AsAllocSpace()); } } void Heap::RegisterGCAllocation(size_t bytes) { - if (this != NULL) { + if (this != nullptr) { gc_memory_overhead_.fetch_add(bytes); } } void Heap::RegisterGCDeAllocation(size_t bytes) { - if (this != NULL) { + if (this != nullptr) { gc_memory_overhead_.fetch_sub(bytes); } } -void Heap::AddDiscontinuousSpace(space::DiscontinuousSpace* space) { - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - DCHECK(space != NULL); - DCHECK(space->GetLiveObjects() != NULL); - live_bitmap_->AddDiscontinuousObjectSet(space->GetLiveObjects()); - DCHECK(space->GetMarkObjects() != NULL); - mark_bitmap_->AddDiscontinuousObjectSet(space->GetMarkObjects()); - discontinuous_spaces_.push_back(space); -} - void Heap::DumpGcPerformanceInfo(std::ostream& os) { // Dump cumulative timings. os << "Dumping cumulative Gc timings\n"; @@ -437,7 +544,7 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { // Dump cumulative loggers for each GC type. uint64_t total_paused_time = 0; - for (const auto& collector : mark_sweep_collectors_) { + for (const auto& collector : garbage_collectors_) { CumulativeLogger& logger = collector->GetCumulativeTimings(); if (logger.GetTotalNs() != 0) { os << Dumpable<CumulativeLogger>(logger); @@ -480,17 +587,14 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) { } Heap::~Heap() { + VLOG(heap) << "Starting ~Heap()"; if (kDumpGcPerformanceOnShutdown) { DumpGcPerformanceInfo(LOG(INFO)); } - - STLDeleteElements(&mark_sweep_collectors_); - - // If we don't reset then the mark stack complains in it's destructor. + STLDeleteElements(&garbage_collectors_); + // If we don't reset then the mark stack complains in its destructor. allocation_stack_->Reset(); live_stack_->Reset(); - - VLOG(heap) << "~Heap()"; STLDeleteValues(&mod_union_tables_); STLDeleteElements(&continuous_spaces_); STLDeleteElements(&discontinuous_spaces_); @@ -499,6 +603,7 @@ Heap::~Heap() { delete weak_ref_queue_lock_; delete finalizer_ref_queue_lock_; delete phantom_ref_queue_lock_; + VLOG(heap) << "Finished ~Heap()"; } space::ContinuousSpace* Heap::FindContinuousSpaceFromObject(const mirror::Object* obj, @@ -579,7 +684,7 @@ inline bool Heap::TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c mirror::Object* obj = AllocateInstrumented(self, large_object_space_, byte_count, bytes_allocated); // Make sure that our large object didn't get placed anywhere within the space interval or else // it breaks the immune range. - DCHECK(obj == NULL || + DCHECK(obj == nullptr || reinterpret_cast<byte*>(obj) < continuous_spaces_.front()->Begin() || reinterpret_cast<byte*>(obj) >= continuous_spaces_.back()->End()); *obj_ptr = obj; @@ -587,16 +692,59 @@ inline bool Heap::TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c return large_object_allocation; } -mirror::Object* Heap::AllocObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count) { - DebugCheckPreconditionsForAllobObject(c, byte_count); +mirror::Object* Heap::AllocMovableObjectInstrumented(Thread* self, mirror::Class* c, + size_t byte_count) { + DebugCheckPreconditionsForAllocObject(c, byte_count); + mirror::Object* obj; + AllocationTimer alloc_timer(this, &obj); + byte_count = RoundUp(byte_count, 8); + if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, false))) { + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, false); + if (UNLIKELY(IsOutOfMemoryOnAllocation(byte_count, true))) { + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); + } + } + obj = bump_pointer_space_->AllocNonvirtual(byte_count); + if (LIKELY(obj != NULL)) { + obj->SetClass(c); + DCHECK(!obj->IsClass()); + // Record allocation after since we want to use the atomic add for the atomic fence to guard + // the SetClass since we do not want the class to appear NULL in another thread. + num_bytes_allocated_.fetch_add(byte_count); + if (Runtime::Current()->HasStatsEnabled()) { + RuntimeStats* thread_stats = Thread::Current()->GetStats(); + ++thread_stats->allocated_objects; + thread_stats->allocated_bytes += byte_count; + RuntimeStats* global_stats = Runtime::Current()->GetStats(); + ++global_stats->allocated_objects; + global_stats->allocated_bytes += byte_count; + } + if (Dbg::IsAllocTrackingEnabled()) { + Dbg::RecordAllocation(c, byte_count); + } + if (kDesiredHeapVerification > kNoHeapVerification) { + VerifyObject(obj); + } + } else { + ThrowOutOfMemoryError(self, byte_count, false); + } + if (kIsDebugBuild) { + self->VerifyStack(); + } + return obj; +} + +mirror::Object* Heap::AllocNonMovableObjectInstrumented(Thread* self, mirror::Class* c, + size_t byte_count) { + DebugCheckPreconditionsForAllocObject(c, byte_count); mirror::Object* obj; size_t bytes_allocated; AllocationTimer alloc_timer(this, &obj); - bool large_object_allocation = TryAllocLargeObjectInstrumented(self, c, byte_count, - &obj, &bytes_allocated); + bool large_object_allocation = TryAllocLargeObjectInstrumented(self, c, byte_count, &obj, + &bytes_allocated); if (LIKELY(!large_object_allocation)) { // Non-large object allocation. - obj = AllocateInstrumented(self, alloc_space_, byte_count, &bytes_allocated); + obj = AllocateInstrumented(self, non_moving_space_, byte_count, &bytes_allocated); // Ensure that we did not allocate into a zygote space. DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj, false)->IsZygoteSpace()); } @@ -612,28 +760,66 @@ mirror::Object* Heap::AllocObjectInstrumented(Thread* self, mirror::Class* c, si if (kDesiredHeapVerification > kNoHeapVerification) { VerifyObject(obj); } - return obj; + } else { + ThrowOutOfMemoryError(self, byte_count, large_object_allocation); } - ThrowOutOfMemoryError(self, byte_count, large_object_allocation); - return NULL; + if (kIsDebugBuild) { + self->VerifyStack(); + } + return obj; } -bool Heap::IsHeapAddress(const mirror::Object* obj) { - // Note: we deliberately don't take the lock here, and mustn't test anything that would - // require taking the lock. - if (obj == NULL) { +void Heap::Trim() { + uint64_t start_ns = NanoTime(); + // Trim the managed spaces. + uint64_t total_alloc_space_allocated = 0; + uint64_t total_alloc_space_size = 0; + uint64_t managed_reclaimed = 0; + for (const auto& space : continuous_spaces_) { + if (space->IsDlMallocSpace() && !space->IsZygoteSpace()) { + gc::space::DlMallocSpace* alloc_space = space->AsDlMallocSpace(); + total_alloc_space_size += alloc_space->Size(); + managed_reclaimed += alloc_space->Trim(); + } + } + total_alloc_space_allocated = GetBytesAllocated() - large_object_space_->GetBytesAllocated() - + bump_pointer_space_->GetBytesAllocated(); + const float managed_utilization = static_cast<float>(total_alloc_space_allocated) / + static_cast<float>(total_alloc_space_size); + uint64_t gc_heap_end_ns = NanoTime(); + // Trim the native heap. + dlmalloc_trim(0); + size_t native_reclaimed = 0; + dlmalloc_inspect_all(DlmallocMadviseCallback, &native_reclaimed); + uint64_t end_ns = NanoTime(); + VLOG(heap) << "Heap trim of managed (duration=" << PrettyDuration(gc_heap_end_ns - start_ns) + << ", advised=" << PrettySize(managed_reclaimed) << ") and native (duration=" + << PrettyDuration(end_ns - gc_heap_end_ns) << ", advised=" << PrettySize(native_reclaimed) + << ") heaps. Managed heap utilization of " << static_cast<int>(100 * managed_utilization) + << "%."; +} + +bool Heap::IsValidObjectAddress(const mirror::Object* obj) const { + // Note: we deliberately don't take the lock here, and mustn't test anything that would require + // taking the lock. + if (obj == nullptr) { return true; } - if (UNLIKELY(!IsAligned<kObjectAlignment>(obj))) { - return false; + return IsAligned<kObjectAlignment>(obj) && IsHeapAddress(obj); +} + +bool Heap::IsHeapAddress(const mirror::Object* obj) const { + if (kMovingCollector && bump_pointer_space_->HasAddress(obj)) { + return true; } - return FindSpaceFromObject(obj, true) != NULL; + // TODO: This probably doesn't work for large objects. + return FindSpaceFromObject(obj, true) != nullptr; } bool Heap::IsLiveObjectLocked(const mirror::Object* obj, bool search_allocation_stack, bool search_live_stack, bool sorted) { // Locks::heap_bitmap_lock_->AssertReaderHeld(Thread::Current()); - if (obj == NULL || UNLIKELY(!IsAligned<kObjectAlignment>(obj))) { + if (obj == nullptr || UNLIKELY(!IsAligned<kObjectAlignment>(obj))) { return false; } space::ContinuousSpace* c_space = FindContinuousSpaceFromObject(obj, true); @@ -642,6 +828,8 @@ bool Heap::IsLiveObjectLocked(const mirror::Object* obj, bool search_allocation_ if (c_space->GetLiveBitmap()->Test(obj)) { return true; } + } else if (bump_pointer_space_->Contains(obj) || temp_space_->Contains(obj)) { + return true; } else { d_space = FindDiscontinuousSpaceFromObject(obj, true); if (d_space != NULL) { @@ -699,16 +887,20 @@ void Heap::VerifyObjectImpl(const mirror::Object* obj) { VerifyObjectBody(obj); } -void Heap::DumpSpaces() { +void Heap::DumpSpaces(std::ostream& stream) { for (const auto& space : continuous_spaces_) { accounting::SpaceBitmap* live_bitmap = space->GetLiveBitmap(); accounting::SpaceBitmap* mark_bitmap = space->GetMarkBitmap(); - LOG(INFO) << space << " " << *space << "\n" - << live_bitmap << " " << *live_bitmap << "\n" - << mark_bitmap << " " << *mark_bitmap; + stream << space << " " << *space << "\n"; + if (live_bitmap != nullptr) { + stream << live_bitmap << " " << *live_bitmap << "\n"; + } + if (mark_bitmap != nullptr) { + stream << mark_bitmap << " " << *mark_bitmap << "\n"; + } } for (const auto& space : discontinuous_spaces_) { - LOG(INFO) << space << " " << *space << "\n"; + stream << space << " " << *space << "\n"; } } @@ -735,7 +927,7 @@ void Heap::VerifyObjectBody(const mirror::Object* obj) { const mirror::Class* c_c_c = *reinterpret_cast<mirror::Class* const *>(raw_addr); CHECK_EQ(c_c, c_c_c); - if (verify_object_mode_ != kVerifyAllFast) { + if (verify_object_mode_ > kVerifyAllFast) { // TODO: the bitmap tests below are racy if VerifyObjectBody is called without the // heap_bitmap_lock_. if (!IsLiveObjectLocked(obj)) { @@ -811,7 +1003,7 @@ inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::Allo inline mirror::Object* Heap::TryToAllocateInstrumented(Thread* self, space::DlMallocSpace* space, size_t alloc_size, bool grow, size_t* bytes_allocated) { if (UNLIKELY(IsOutOfMemoryOnAllocation(alloc_size, grow))) { - return NULL; + return nullptr; } if (LIKELY(!running_on_valgrind_)) { return space->AllocNonvirtual(self, alloc_size, bytes_allocated); @@ -841,7 +1033,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp // The allocation failed. If the GC is running, block until it completes, and then retry the // allocation. - collector::GcType last_gc = WaitForConcurrentGcToComplete(self); + collector::GcType last_gc = WaitForGcToComplete(self); if (last_gc != collector::kGcTypeNone) { // A GC was in progress and we blocked, retry allocation now that memory has been freed. ptr = TryToAllocateInstrumented(self, space, alloc_size, false, bytes_allocated); @@ -857,9 +1049,10 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp collector::GcType gc_type = static_cast<collector::GcType>(i); switch (gc_type) { case collector::kGcTypeSticky: { - const size_t alloc_space_size = alloc_space_->Size(); + const size_t alloc_space_size = non_moving_space_->Size(); run_gc = alloc_space_size > min_alloc_space_size_for_sticky_gc_ && - alloc_space_->Capacity() - alloc_space_size >= min_remaining_space_for_sticky_gc_; + non_moving_space_->Capacity() - alloc_space_size >= + min_remaining_space_for_sticky_gc_; break; } case collector::kGcTypePartial: @@ -869,7 +1062,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp run_gc = true; break; default: - break; + LOG(FATAL) << "Invalid GC type"; } if (run_gc) { @@ -897,11 +1090,11 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, space::AllocSpace* sp // or the requested size is really big. Do another GC, collecting SoftReferences this time. The // VM spec requires that all SoftReferences have been collected and cleared before throwing OOME. - // OLD-TODO: wait for the finalizers from the previous GC to finish + // TODO: Run finalization, but this can cause more allocations to occur. VLOG(gc) << "Forcing collection of SoftReferences for " << PrettySize(alloc_size) << " allocation"; - // We don't need a WaitForConcurrentGcToComplete here either. + // We don't need a WaitForGcToComplete here either. CollectGarbageInternal(collector::kGcTypeFull, kGcCauseForAlloc, true); return TryToAllocateInstrumented(self, space, alloc_size, true, bytes_allocated); } @@ -914,51 +1107,24 @@ void Heap::SetTargetHeapUtilization(float target) { size_t Heap::GetObjectsAllocated() const { size_t total = 0; - typedef std::vector<space::ContinuousSpace*>::const_iterator It; - for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { - space::ContinuousSpace* space = *it; - if (space->IsDlMallocSpace()) { - total += space->AsDlMallocSpace()->GetObjectsAllocated(); - } - } - typedef std::vector<space::DiscontinuousSpace*>::const_iterator It2; - for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { - space::DiscontinuousSpace* space = *it; - total += space->AsLargeObjectSpace()->GetObjectsAllocated(); + for (space::AllocSpace* space : alloc_spaces_) { + total += space->GetObjectsAllocated(); } return total; } size_t Heap::GetObjectsAllocatedEver() const { size_t total = 0; - typedef std::vector<space::ContinuousSpace*>::const_iterator It; - for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { - space::ContinuousSpace* space = *it; - if (space->IsDlMallocSpace()) { - total += space->AsDlMallocSpace()->GetTotalObjectsAllocated(); - } - } - typedef std::vector<space::DiscontinuousSpace*>::const_iterator It2; - for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { - space::DiscontinuousSpace* space = *it; - total += space->AsLargeObjectSpace()->GetTotalObjectsAllocated(); + for (space::AllocSpace* space : alloc_spaces_) { + total += space->GetTotalObjectsAllocated(); } return total; } size_t Heap::GetBytesAllocatedEver() const { size_t total = 0; - typedef std::vector<space::ContinuousSpace*>::const_iterator It; - for (It it = continuous_spaces_.begin(), end = continuous_spaces_.end(); it != end; ++it) { - space::ContinuousSpace* space = *it; - if (space->IsDlMallocSpace()) { - total += space->AsDlMallocSpace()->GetTotalBytesAllocated(); - } - } - typedef std::vector<space::DiscontinuousSpace*>::const_iterator It2; - for (It2 it = discontinuous_spaces_.begin(), end = discontinuous_spaces_.end(); it != end; ++it) { - space::DiscontinuousSpace* space = *it; - total += space->AsLargeObjectSpace()->GetTotalBytesAllocated(); + for (space::AllocSpace* space : alloc_spaces_) { + total += space->GetTotalBytesAllocated(); } return total; } @@ -1056,8 +1222,8 @@ class ReferringObjectsFinder { // For bitmap Visit. // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for // annotalysis on visitors. - void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS { - collector::MarkSweep::VisitObjectReferences(obj, *this, true); + void operator()(const mirror::Object* o) const NO_THREAD_SAFETY_ANALYSIS { + collector::MarkSweep::VisitObjectReferences(const_cast<mirror::Object*>(o), *this, true); } // For MarkSweep::VisitObjectReferences. @@ -1093,56 +1259,69 @@ void Heap::GetReferringObjects(mirror::Object* o, int32_t max_count, void Heap::CollectGarbage(bool clear_soft_references) { // Even if we waited for a GC we still need to do another GC since weaks allocated during the // last GC will not have necessarily been cleared. - Thread* self = Thread::Current(); - WaitForConcurrentGcToComplete(self); CollectGarbageInternal(collector::kGcTypeFull, kGcCauseExplicit, clear_soft_references); } void Heap::PreZygoteFork() { static Mutex zygote_creation_lock_("zygote creation lock", kZygoteCreationLock); - // Do this before acquiring the zygote creation lock so that we don't get lock order violations. - CollectGarbage(false); Thread* self = Thread::Current(); MutexLock mu(self, zygote_creation_lock_); - // Try to see if we have any Zygote spaces. if (have_zygote_space_) { return; } - - VLOG(heap) << "Starting PreZygoteFork with alloc space size " << PrettySize(alloc_space_->Size()); - - { - // Flush the alloc stack. - WriterMutexLock mu(self, *Locks::heap_bitmap_lock_); - FlushAllocStack(); + VLOG(heap) << "Starting PreZygoteFork"; + // Do this before acquiring the zygote creation lock so that we don't get lock order violations. + CollectGarbageInternal(collector::kGcTypeFull, kGcCauseBackground, false); + // Trim the pages at the end of the non moving space. + non_moving_space_->Trim(); + non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); + // Create a new bump pointer space which we will compact into. + if (semi_space_collector_ != nullptr) { + space::BumpPointerSpace target_space("zygote bump space", non_moving_space_->End(), + non_moving_space_->Limit()); + // Compact the bump pointer space to a new zygote bump pointer space. + temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); + Compact(&target_space, bump_pointer_space_); + CHECK_EQ(temp_space_->GetBytesAllocated(), 0U); + total_objects_freed_ever_ += semi_space_collector_->GetFreedObjects(); + total_bytes_freed_ever_ += semi_space_collector_->GetFreedBytes(); + // Update the end and write out image. + non_moving_space_->SetEnd(target_space.End()); + non_moving_space_->SetLimit(target_space.Limit()); + accounting::SpaceBitmap* bitmap = non_moving_space_->GetLiveBitmap(); + // Record the allocations in the bitmap. + VLOG(heap) << "Recording zygote allocations"; + mirror::Object* obj = reinterpret_cast<mirror::Object*>(target_space.Begin()); + const mirror::Object* end = reinterpret_cast<const mirror::Object*>(target_space.End()); + while (obj < end) { + bitmap->Set(obj); + obj = space::BumpPointerSpace::GetNextObject(obj); + } } - - // Turns the current alloc space into a Zygote space and obtain the new alloc space composed - // of the remaining available heap memory. - space::DlMallocSpace* zygote_space = alloc_space_; - alloc_space_ = zygote_space->CreateZygoteSpace("alloc space"); - alloc_space_->SetFootprintLimit(alloc_space_->Capacity()); - + // Turn the current alloc space into a zygote space and obtain the new alloc space composed of + // the remaining available heap memory. + space::DlMallocSpace* zygote_space = non_moving_space_; + non_moving_space_ = zygote_space->CreateZygoteSpace("alloc space"); + non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); // Change the GC retention policy of the zygote space to only collect when full. zygote_space->SetGcRetentionPolicy(space::kGcRetentionPolicyFullCollect); - AddContinuousSpace(alloc_space_); + AddSpace(non_moving_space_); have_zygote_space_ = true; - + zygote_space->InvalidateMSpace(); // Create the zygote space mod union table. accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space); CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table"; AddModUnionTable(mod_union_table); - // Reset the cumulative loggers since we now have a few additional timing phases. - for (const auto& collector : mark_sweep_collectors_) { + for (const auto& collector : garbage_collectors_) { collector->ResetCumulativeStatistics(); } } void Heap::FlushAllocStack() { - MarkAllocStack(alloc_space_->GetLiveBitmap(), large_object_space_->GetLiveObjects(), + MarkAllocStack(non_moving_space_->GetLiveBitmap(), large_object_space_->GetLiveObjects(), allocation_stack_.get()); allocation_stack_->Reset(); } @@ -1161,86 +1340,111 @@ void Heap::MarkAllocStack(accounting::SpaceBitmap* bitmap, accounting::SpaceSetM } } +const char* PrettyCause(GcCause cause) { + switch (cause) { + case kGcCauseForAlloc: return "Alloc"; + case kGcCauseBackground: return "Background"; + case kGcCauseExplicit: return "Explicit"; + default: + LOG(FATAL) << "Unreachable"; + } + return ""; +} + +void Heap::SwapSemiSpaces() { + // Swap the spaces so we allocate into the space which we just evacuated. + std::swap(bump_pointer_space_, temp_space_); +} -const char* gc_cause_and_type_strings[3][4] = { - {"", "GC Alloc Sticky", "GC Alloc Partial", "GC Alloc Full"}, - {"", "GC Background Sticky", "GC Background Partial", "GC Background Full"}, - {"", "GC Explicit Sticky", "GC Explicit Partial", "GC Explicit Full"}}; +void Heap::Compact(space::ContinuousMemMapAllocSpace* target_space, + space::ContinuousMemMapAllocSpace* source_space) { + CHECK(kMovingCollector); + CHECK_NE(target_space, source_space) << "In-place compaction unsupported"; + if (target_space != source_space) { + semi_space_collector_->SetFromSpace(source_space); + semi_space_collector_->SetToSpace(target_space); + semi_space_collector_->Run(false); + } +} collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCause gc_cause, bool clear_soft_references) { Thread* self = Thread::Current(); - + Runtime* runtime = Runtime::Current(); ScopedThreadStateChange tsc(self, kWaitingPerformingGc); Locks::mutator_lock_->AssertNotHeld(self); - if (self->IsHandlingStackOverflow()) { LOG(WARNING) << "Performing GC on a thread that is handling a stack overflow."; } - - // Ensure there is only one GC at a time. - bool start_collect = false; - while (!start_collect) { - { - MutexLock mu(self, *gc_complete_lock_); - if (!is_gc_running_) { - is_gc_running_ = true; - start_collect = true; - } - } - if (!start_collect) { - // TODO: timinglog this. - WaitForConcurrentGcToComplete(self); - - // TODO: if another thread beat this one to do the GC, perhaps we should just return here? - // Not doing at the moment to ensure soft references are cleared. + { + gc_complete_lock_->AssertNotHeld(self); + MutexLock mu(self, *gc_complete_lock_); + // Ensure there is only one GC at a time. + WaitForGcToCompleteLocked(self); + // TODO: if another thread beat this one to do the GC, perhaps we should just return here? + // Not doing at the moment to ensure soft references are cleared. + // GC can be disabled if someone has a used GetPrimitiveArrayCritical. + if (gc_disable_count_ != 0) { + LOG(WARNING) << "Skipping GC due to disable count " << gc_disable_count_; + return collector::kGcTypeNone; } + is_gc_running_ = true; } - gc_complete_lock_->AssertNotHeld(self); - if (gc_cause == kGcCauseForAlloc && Runtime::Current()->HasStatsEnabled()) { - ++Runtime::Current()->GetStats()->gc_for_alloc_count; - ++Thread::Current()->GetStats()->gc_for_alloc_count; + if (gc_cause == kGcCauseForAlloc && runtime->HasStatsEnabled()) { + ++runtime->GetStats()->gc_for_alloc_count; + ++self->GetStats()->gc_for_alloc_count; } uint64_t gc_start_time_ns = NanoTime(); uint64_t gc_start_size = GetBytesAllocated(); // Approximate allocation rate in bytes / second. - if (UNLIKELY(gc_start_time_ns == last_gc_time_ns_)) { - LOG(WARNING) << "Timers are broken (gc_start_time == last_gc_time_)."; - } uint64_t ms_delta = NsToMs(gc_start_time_ns - last_gc_time_ns_); - if (ms_delta != 0) { + // Back to back GCs can cause 0 ms of wait time in between GC invocations. + if (LIKELY(ms_delta != 0)) { allocation_rate_ = ((gc_start_size - last_gc_size_) * 1000) / ms_delta; VLOG(heap) << "Allocation rate: " << PrettySize(allocation_rate_) << "/s"; } if (gc_type == collector::kGcTypeSticky && - alloc_space_->Size() < min_alloc_space_size_for_sticky_gc_) { + non_moving_space_->Size() < min_alloc_space_size_for_sticky_gc_) { gc_type = collector::kGcTypePartial; } DCHECK_LT(gc_type, collector::kGcTypeMax); DCHECK_NE(gc_type, collector::kGcTypeNone); - DCHECK_LE(gc_cause, kGcCauseExplicit); - ATRACE_BEGIN(gc_cause_and_type_strings[gc_cause][gc_type]); - - collector::MarkSweep* collector = NULL; - for (const auto& cur_collector : mark_sweep_collectors_) { - if (cur_collector->IsConcurrent() == concurrent_gc_ && cur_collector->GetGcType() == gc_type) { + collector::GarbageCollector* collector = nullptr; + if (kMovingCollector) { + gc_type = semi_space_collector_->GetGcType(); + CHECK_EQ(temp_space_->GetObjectsAllocated(), 0U); + semi_space_collector_->SetFromSpace(bump_pointer_space_); + semi_space_collector_->SetToSpace(temp_space_); + mprotect(temp_space_->Begin(), temp_space_->Capacity(), PROT_READ | PROT_WRITE); + } + for (const auto& cur_collector : garbage_collectors_) { + if (cur_collector->IsConcurrent() == concurrent_gc_ && + cur_collector->GetGcType() == gc_type) { collector = cur_collector; break; } } + if (kMovingCollector) { + gc_type = collector::kGcTypeFull; + } CHECK(collector != NULL) << "Could not find garbage collector with concurrent=" << concurrent_gc_ << " and type=" << gc_type; - collector->clear_soft_references_ = clear_soft_references; - collector->Run(); + ATRACE_BEGIN(StringPrintf("%s %s GC", PrettyCause(gc_cause), collector->GetName()).c_str()); + + collector->Run(clear_soft_references); total_objects_freed_ever_ += collector->GetFreedObjects(); total_bytes_freed_ever_ += collector->GetFreedBytes(); + + // Grow the heap so that we know when to perform the next GC. + GrowForUtilization(gc_type, collector->GetDurationNs()); + if (care_about_pause_times_) { const size_t duration = collector->GetDurationNs(); std::vector<uint64_t> pauses = collector->GetPauseTimes(); @@ -1252,7 +1456,6 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus was_slow = was_slow || pause > long_pause_log_threshold_; } } - if (was_slow) { const size_t percent_free = GetPercentFree(); const size_t current_heap_size = GetBytesAllocated(); @@ -1327,7 +1530,6 @@ class VerifyReferenceVisitor { accounting::CardTable* card_table = heap_->GetCardTable(); accounting::ObjectStack* alloc_stack = heap_->allocation_stack_.get(); accounting::ObjectStack* live_stack = heap_->live_stack_.get(); - if (!failed_) { // Print message on only on first failure to prevent spam. LOG(ERROR) << "!!!!!!!!!!!!!!Heap corruption detected!!!!!!!!!!!!!!!!!!!"; @@ -1337,7 +1539,7 @@ class VerifyReferenceVisitor { byte* card_addr = card_table->CardFromAddr(obj); LOG(ERROR) << "Object " << obj << " references dead object " << ref << " at offset " << offset << "\n card value = " << static_cast<int>(*card_addr); - if (heap_->IsHeapAddress(obj->GetClass())) { + if (heap_->IsValidObjectAddress(obj->GetClass())) { LOG(ERROR) << "Obj type " << PrettyTypeOf(obj); } else { LOG(ERROR) << "Object " << obj << " class(" << obj->GetClass() << ") not a heap address"; @@ -1345,7 +1547,7 @@ class VerifyReferenceVisitor { // Attmept to find the class inside of the recently freed objects. space::ContinuousSpace* ref_space = heap_->FindContinuousSpaceFromObject(ref, true); - if (ref_space->IsDlMallocSpace()) { + if (ref_space != nullptr && ref_space->IsDlMallocSpace()) { space::DlMallocSpace* space = ref_space->AsDlMallocSpace(); mirror::Class* ref_class = space->FindRecentFreedObject(ref); if (ref_class != nullptr) { @@ -1356,7 +1558,7 @@ class VerifyReferenceVisitor { } } - if (ref->GetClass() != nullptr && heap_->IsHeapAddress(ref->GetClass()) && + if (ref->GetClass() != nullptr && heap_->IsValidObjectAddress(ref->GetClass()) && ref->GetClass()->IsClass()) { LOG(ERROR) << "Ref type " << PrettyTypeOf(ref); } else { @@ -1427,17 +1629,25 @@ class VerifyObjectVisitor { public: explicit VerifyObjectVisitor(Heap* heap) : heap_(heap), failed_(false) {} - void operator()(const mirror::Object* obj) const + void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { // Note: we are verifying the references in obj but not obj itself, this is because obj must // be live or else how did we find it in the live bitmap? VerifyReferenceVisitor visitor(heap_); // The class doesn't count as a reference but we should verify it anyways. - visitor(obj, obj->GetClass(), MemberOffset(0), false); - collector::MarkSweep::VisitObjectReferences(const_cast<mirror::Object*>(obj), visitor, true); + collector::MarkSweep::VisitObjectReferences(obj, visitor, true); + if (obj->GetClass()->IsReferenceClass()) { + visitor(obj, heap_->GetReferenceReferent(obj), MemberOffset(0), false); + } failed_ = failed_ || visitor.Failed(); } + static void VisitCallback(mirror::Object* obj, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { + VerifyObjectVisitor* visitor = reinterpret_cast<VerifyObjectVisitor*>(arg); + visitor->operator()(obj); + } + bool Failed() const { return failed_; } @@ -1453,18 +1663,15 @@ bool Heap::VerifyHeapReferences() { // Lets sort our allocation stacks so that we can efficiently binary search them. allocation_stack_->Sort(); live_stack_->Sort(); - // Perform the verification. VerifyObjectVisitor visitor(this); - Runtime::Current()->VisitRoots(VerifyReferenceVisitor::VerifyRoots, &visitor, false, false); - GetLiveBitmap()->Visit(visitor); // Verify objects in the allocation stack since these will be objects which were: // 1. Allocated prior to the GC (pre GC verification). // 2. Allocated during the GC (pre sweep GC verification). - for (mirror::Object** it = allocation_stack_->Begin(); it != allocation_stack_->End(); ++it) { - visitor(*it); - } // We don't want to verify the objects in the live stack since they themselves may be // pointing to dead objects if they are not reachable. + VisitObjects(VerifyObjectVisitor::VisitCallback, &visitor); + // Verify the roots: + Runtime::Current()->VisitRoots(VerifyReferenceVisitor::VerifyRoots, &visitor, false, false); if (visitor.Failed()) { // Dump mod-union tables. for (const auto& table_pair : mod_union_tables_) { @@ -1557,7 +1764,7 @@ class VerifyLiveStackReferences { void operator()(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) { VerifyReferenceCardVisitor visitor(heap_, const_cast<bool*>(&failed_)); - collector::MarkSweep::VisitObjectReferences(obj, visitor, true); + collector::MarkSweep::VisitObjectReferences(const_cast<mirror::Object*>(obj), visitor, true); } bool Failed() const { @@ -1610,10 +1817,14 @@ void Heap::ProcessCards(base::TimingLogger& timings) { "ImageModUnionClearCards"; base::TimingLogger::ScopedSplit split(name, &timings); table->ClearCards(); - } else { + } else if (space->GetType() != space::kSpaceTypeBumpPointerSpace) { base::TimingLogger::ScopedSplit split("AllocSpaceClearCards", &timings); // No mod union table for the AllocSpace. Age the cards so that the GC knows that these cards // were dirty before the GC started. + // TODO: Don't need to use atomic. + // The races are we either end up with: Aged card, unaged card. Since we have the checkpoint + // roots and then we scan / update mod union tables after. We will always scan either card.// + // If we end up with the non aged card, we scan it it in the pause. card_table_->ModifyCardsAtomic(space->Begin(), space->End(), AgeCardVisitor(), VoidFunctor()); } } @@ -1692,36 +1903,27 @@ void Heap::PostGcVerification(collector::GarbageCollector* gc) { } } -collector::GcType Heap::WaitForConcurrentGcToComplete(Thread* self) { +collector::GcType Heap::WaitForGcToComplete(Thread* self) { + ScopedThreadStateChange tsc(self, kWaitingForGcToComplete); + MutexLock mu(self, *gc_complete_lock_); + return WaitForGcToCompleteLocked(self); +} + +collector::GcType Heap::WaitForGcToCompleteLocked(Thread* self) { collector::GcType last_gc_type = collector::kGcTypeNone; - if (concurrent_gc_) { - ATRACE_BEGIN("GC: Wait For Concurrent"); - bool do_wait; - uint64_t wait_start = NanoTime(); - { - // Check if GC is running holding gc_complete_lock_. - MutexLock mu(self, *gc_complete_lock_); - do_wait = is_gc_running_; - } - if (do_wait) { - uint64_t wait_time; - // We must wait, change thread state then sleep on gc_complete_cond_; - ScopedThreadStateChange tsc(Thread::Current(), kWaitingForGcToComplete); - { - MutexLock mu(self, *gc_complete_lock_); - while (is_gc_running_) { - gc_complete_cond_->Wait(self); - } - last_gc_type = last_gc_type_; - wait_time = NanoTime() - wait_start; - total_wait_time_ += wait_time; - } - if (wait_time > long_pause_log_threshold_) { - LOG(INFO) << "WaitForConcurrentGcToComplete blocked for " << PrettyDuration(wait_time); - } - } + uint64_t wait_start = NanoTime(); + while (is_gc_running_) { + ATRACE_BEGIN("GC: Wait For Completion"); + // We must wait, change thread state then sleep on gc_complete_cond_; + gc_complete_cond_->Wait(self); + last_gc_type = last_gc_type_; ATRACE_END(); } + uint64_t wait_time = NanoTime() - wait_start; + total_wait_time_ += wait_time; + if (wait_time > long_pause_log_threshold_) { + LOG(INFO) << "WaitForGcToComplete blocked for " << PrettyDuration(wait_time); + } return last_gc_type; } @@ -1744,6 +1946,23 @@ void Heap::SetIdealFootprint(size_t max_allowed_footprint) { max_allowed_footprint_ = max_allowed_footprint; } +bool Heap::IsMovableObject(const mirror::Object* obj) const { + if (kMovingCollector) { + DCHECK(!IsInTempSpace(obj)); + if (bump_pointer_space_->HasAddress(obj)) { + return true; + } + } + return false; +} + +bool Heap::IsInTempSpace(const mirror::Object* obj) const { + if (temp_space_->HasAddress(obj) && !temp_space_->Contains(obj)) { + return true; + } + return false; +} + void Heap::UpdateMaxNativeFootprint() { size_t native_size = native_bytes_allocated_; // TODO: Tune the native heap utilization to be a value other than the java heap utilization. @@ -1773,6 +1992,7 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { } else if (target_size < bytes_allocated + min_free_) { target_size = bytes_allocated + min_free_; } + native_need_to_run_finalization_ = true; next_gc_type_ = collector::kGcTypeSticky; } else { // Based on how close the current heap size is to the target size, decide @@ -1796,7 +2016,6 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { 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. @@ -1817,13 +2036,11 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { DCHECK_LE(max_allowed_footprint_, growth_limit_); } } - - UpdateMaxNativeFootprint(); } void Heap::ClearGrowthLimit() { growth_limit_ = capacity_; - alloc_space_->ClearGrowthLimit(); + non_moving_space_->ClearGrowthLimit(); } void Heap::SetReferenceOffsets(MemberOffset reference_referent_offset, @@ -1843,6 +2060,12 @@ void Heap::SetReferenceOffsets(MemberOffset reference_referent_offset, CHECK_NE(finalizer_reference_zombie_offset_.Uint32Value(), 0U); } +void Heap::SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) { + DCHECK(reference != NULL); + DCHECK_NE(reference_referent_offset_.Uint32Value(), 0U); + reference->SetFieldObject(reference_referent_offset_, referent, true); +} + mirror::Object* Heap::GetReferenceReferent(mirror::Object* reference) { DCHECK(reference != NULL); DCHECK_NE(reference_referent_offset_.Uint32Value(), 0U); @@ -1852,7 +2075,7 @@ mirror::Object* Heap::GetReferenceReferent(mirror::Object* reference) { void Heap::ClearReferenceReferent(mirror::Object* reference) { DCHECK(reference != NULL); DCHECK_NE(reference_referent_offset_.Uint32Value(), 0U); - reference->SetFieldObject(reference_referent_offset_, NULL, true); + reference->SetFieldObject(reference_referent_offset_, nullptr, true); } // Returns true if the reference object has not yet been enqueued. @@ -1924,19 +2147,41 @@ void Heap::AddFinalizerReference(Thread* self, mirror::Object* object) { arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); } +void Heap::PrintReferenceQueue(std::ostream& os, mirror::Object** queue) { + os << "Refernece queue " << queue << "\n"; + if (queue != nullptr) { + mirror::Object* list = *queue; + if (list != nullptr) { + mirror::Object* cur = list; + do { + mirror::Object* pending_next = + cur->GetFieldObject<mirror::Object*>(reference_pendingNext_offset_, false); + os << "PendingNext=" << pending_next; + if (cur->GetClass()->IsFinalizerReferenceClass()) { + os << " Zombie=" << + cur->GetFieldObject<mirror::Object*>(finalizer_reference_zombie_offset_, false); + } + os << "\n"; + cur = pending_next; + } while (cur != list); + } + } +} + void Heap::EnqueueClearedReferences(mirror::Object** cleared) { - DCHECK(cleared != NULL); - if (*cleared != NULL) { + DCHECK(cleared != nullptr); + mirror::Object* list = *cleared; + if (list != nullptr) { // When a runtime isn't started there are no reference queues to care about so ignore. if (LIKELY(Runtime::Current()->IsStarted())) { ScopedObjectAccess soa(Thread::Current()); JValue result; ArgArray arg_array(NULL, 0); - arg_array.Append(reinterpret_cast<uint32_t>(*cleared)); + arg_array.Append(reinterpret_cast<uint32_t>(list)); soa.DecodeMethod(WellKnownClasses::java_lang_ref_ReferenceQueue_add)->Invoke(soa.Self(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); } - *cleared = NULL; + *cleared = nullptr; } } @@ -1944,42 +2189,27 @@ void Heap::RequestConcurrentGC(Thread* self) { // Make sure that we can do a concurrent GC. Runtime* runtime = Runtime::Current(); DCHECK(concurrent_gc_); - if (runtime == NULL || !runtime->IsFinishedStarting() || - !runtime->IsConcurrentGcEnabled()) { - return; - } - { - MutexLock mu(self, *Locks::runtime_shutdown_lock_); - if (runtime->IsShuttingDown()) { - return; - } - } - if (self->IsHandlingStackOverflow()) { + if (runtime == NULL || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) || + self->IsHandlingStackOverflow()) { return; } - // We already have a request pending, no reason to start more until we update // concurrent_start_bytes_. concurrent_start_bytes_ = std::numeric_limits<size_t>::max(); - JNIEnv* env = self->GetJniEnv(); - DCHECK(WellKnownClasses::java_lang_Daemons != NULL); - DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != NULL); + DCHECK(WellKnownClasses::java_lang_Daemons != nullptr); + DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != nullptr); env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons, WellKnownClasses::java_lang_Daemons_requestGC); CHECK(!env->ExceptionCheck()); } void Heap::ConcurrentGC(Thread* self) { - { - MutexLock mu(self, *Locks::runtime_shutdown_lock_); - if (Runtime::Current()->IsShuttingDown()) { - return; - } + if (Runtime::Current()->IsShuttingDown(self)) { + return; } - // Wait for any GCs currently running to finish. - if (WaitForConcurrentGcToComplete(self) == collector::kGcTypeNone) { + if (WaitForGcToComplete(self) == collector::kGcTypeNone) { CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false); } } @@ -1998,26 +2228,18 @@ void Heap::RequestHeapTrim() { // We could try mincore(2) but that's only a measure of how many pages we haven't given away, // not how much use we're making of those pages. uint64_t ms_time = MilliTime(); - // Note the large object space's bytes allocated is equal to its capacity. - uint64_t los_bytes_allocated = large_object_space_->GetBytesAllocated(); - float utilization = static_cast<float>(GetBytesAllocated() - los_bytes_allocated) / - (GetTotalMemory() - los_bytes_allocated); - if ((utilization > 0.75f && !IsLowMemoryMode()) || ((ms_time - last_trim_time_ms_) < 2 * 1000)) { - // Don't bother trimming the alloc space if it's more than 75% utilized and low memory mode is - // not enabled, or if a heap trim occurred in the last two seconds. + // Don't bother trimming the alloc space if a heap trim occurred in the last two seconds. + if (ms_time - last_trim_time_ms_ < 2 * 1000) { return; } Thread* self = Thread::Current(); - { - MutexLock mu(self, *Locks::runtime_shutdown_lock_); - Runtime* runtime = Runtime::Current(); - if (runtime == NULL || !runtime->IsFinishedStarting() || runtime->IsShuttingDown()) { - // Heap trimming isn't supported without a Java runtime or Daemons (such as at dex2oat time) - // Also: we do not wish to start a heap trim if the runtime is shutting down (a racy check - // as we don't hold the lock while requesting the trim). - return; - } + Runtime* runtime = Runtime::Current(); + if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self)) { + // Heap trimming isn't supported without a Java runtime or Daemons (such as at dex2oat time) + // Also: we do not wish to start a heap trim if the runtime is shutting down (a racy check + // as we don't hold the lock while requesting the trim). + return; } last_trim_time_ms_ = ms_time; @@ -2034,50 +2256,55 @@ void Heap::RequestHeapTrim() { } } -size_t Heap::Trim() { - // Handle a requested heap trim on a thread outside of the main GC thread. - return alloc_space_->Trim(); -} - bool Heap::IsGCRequestPending() const { return concurrent_start_bytes_ != std::numeric_limits<size_t>::max(); } +void Heap::RunFinalization(JNIEnv* env) { + // Can't do this in WellKnownClasses::Init since System is not properly set up at that point. + if (WellKnownClasses::java_lang_System_runFinalization == nullptr) { + CHECK(WellKnownClasses::java_lang_System != nullptr); + WellKnownClasses::java_lang_System_runFinalization = + CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V"); + CHECK(WellKnownClasses::java_lang_System_runFinalization != nullptr); + } + env->CallStaticVoidMethod(WellKnownClasses::java_lang_System, + WellKnownClasses::java_lang_System_runFinalization); +} + void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { + Thread* self = ThreadForEnv(env); + if (native_need_to_run_finalization_) { + RunFinalization(env); + UpdateMaxNativeFootprint(); + native_need_to_run_finalization_ = false; + } // Total number of native bytes allocated. native_bytes_allocated_.fetch_add(bytes); if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_gc_watermark_) { // The second watermark is higher than the gc watermark. If you hit this it means you are // allocating native objects faster than the GC can keep up with. if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_limit_) { - // Can't do this in WellKnownClasses::Init since System is not properly set up at that - // point. - if (UNLIKELY(WellKnownClasses::java_lang_System_runFinalization == NULL)) { - DCHECK(WellKnownClasses::java_lang_System != NULL); - WellKnownClasses::java_lang_System_runFinalization = - CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V"); - CHECK(WellKnownClasses::java_lang_System_runFinalization != NULL); - } - if (WaitForConcurrentGcToComplete(ThreadForEnv(env)) != collector::kGcTypeNone) { - // Just finished a GC, attempt to run finalizers. - env->CallStaticVoidMethod(WellKnownClasses::java_lang_System, - WellKnownClasses::java_lang_System_runFinalization); - CHECK(!env->ExceptionCheck()); - } - - // If we still are over the watermark, attempt a GC for alloc and run finalizers. - if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_limit_) { - CollectGarbageInternal(collector::kGcTypePartial, kGcCauseForAlloc, false); - env->CallStaticVoidMethod(WellKnownClasses::java_lang_System, - WellKnownClasses::java_lang_System_runFinalization); - CHECK(!env->ExceptionCheck()); - } - // We have just run finalizers, update the native watermark since it is very likely that - // finalizers released native managed allocations. - UpdateMaxNativeFootprint(); - } else { - if (!IsGCRequestPending()) { - RequestConcurrentGC(ThreadForEnv(env)); + if (WaitForGcToComplete(self) != collector::kGcTypeNone) { + // Just finished a GC, attempt to run finalizers. + RunFinalization(env); + CHECK(!env->ExceptionCheck()); + } + // If we still are over the watermark, attempt a GC for alloc and run finalizers. + if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_limit_) { + CollectGarbageInternal(collector::kGcTypePartial, kGcCauseForAlloc, false); + RunFinalization(env); + native_need_to_run_finalization_ = false; + CHECK(!env->ExceptionCheck()); + } + // We have just run finalizers, update the native watermark since it is very likely that + // finalizers released native managed allocations. + UpdateMaxNativeFootprint(); + } else if (!IsGCRequestPending()) { + if (concurrent_gc_) { + RequestConcurrentGC(self); + } else { + CollectGarbageInternal(collector::kGcTypePartial, kGcCauseForAlloc, false); } } } @@ -2086,26 +2313,24 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { void Heap::RegisterNativeFree(JNIEnv* env, int bytes) { int expected_size, new_size; do { - expected_size = native_bytes_allocated_.load(); - new_size = expected_size - bytes; - if (UNLIKELY(new_size < 0)) { - ScopedObjectAccess soa(env); - env->ThrowNew(WellKnownClasses::java_lang_RuntimeException, - StringPrintf("Attempted to free %d native bytes with only %d native bytes " - "registered as allocated", bytes, expected_size).c_str()); - break; - } + expected_size = native_bytes_allocated_.load(); + new_size = expected_size - bytes; + if (UNLIKELY(new_size < 0)) { + ScopedObjectAccess soa(env); + env->ThrowNew(WellKnownClasses::java_lang_RuntimeException, + StringPrintf("Attempted to free %d native bytes with only %d native bytes " + "registered as allocated", bytes, expected_size).c_str()); + break; + } } while (!native_bytes_allocated_.compare_and_swap(expected_size, new_size)); } int64_t Heap::GetTotalMemory() const { int64_t ret = 0; for (const auto& space : continuous_spaces_) { - if (space->IsImageSpace()) { - // Currently don't include the image space. - } else if (space->IsDlMallocSpace()) { - // Zygote or alloc space - ret += space->AsDlMallocSpace()->GetFootprint(); + // Currently don't include the image space. + if (!space->IsImageSpace()) { + ret += space->Size(); } } for (const auto& space : discontinuous_spaces_) { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 91909e4..0fa000f 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -31,6 +31,7 @@ #include "jni.h" #include "locks.h" #include "offsets.h" +#include "root_visitor.h" #include "safe_map.h" #include "thread_pool.h" @@ -57,16 +58,19 @@ namespace accounting { namespace collector { class GarbageCollector; class MarkSweep; + class SemiSpace; } // namespace collector namespace space { class AllocSpace; + class BumpPointerSpace; class DiscontinuousSpace; class DlMallocSpace; class ImageSpace; class LargeObjectSpace; class Space; class SpaceTest; + class ContinuousMemMapAllocSpace; } // namespace space class AgeCardVisitor { @@ -101,13 +105,13 @@ enum HeapVerificationMode { }; static constexpr HeapVerificationMode kDesiredHeapVerification = kNoHeapVerification; -// If true, measure the total allocation time. -static constexpr bool kMeasureAllocationTime = false; -// Primitive arrays larger than this size are put in the large object space. -static constexpr size_t kLargeObjectThreshold = 3 * kPageSize; - class Heap { public: + // If true, measure the total allocation time. + static constexpr bool kMeasureAllocationTime = false; + // Primitive arrays larger than this size are put in the large object space. + static constexpr size_t kLargeObjectThreshold = 3 * kPageSize; + static constexpr size_t kDefaultInitialSize = 2 * MB; static constexpr size_t kDefaultMaximumSize = 32 * MB; static constexpr size_t kDefaultMaxFree = 2 * MB; @@ -135,14 +139,47 @@ class Heap { // Allocates and initializes storage for an object instance. mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(!kMovingClasses); return AllocObjectInstrumented(self, klass, num_bytes); } + // Allocates and initializes storage for an object instance. + mirror::Object* AllocNonMovableObject(Thread* self, mirror::Class* klass, size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(!kMovingClasses); + return AllocNonMovableObjectInstrumented(self, klass, num_bytes); + } mirror::Object* AllocObjectInstrumented(Thread* self, mirror::Class* klass, size_t num_bytes) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(!kMovingClasses); + if (kMovingCollector) { + return AllocMovableObjectInstrumented(self, klass, num_bytes); + } else { + return AllocNonMovableObjectInstrumented(self, klass, num_bytes); + } + } mirror::Object* AllocObjectUninstrumented(Thread* self, mirror::Class* klass, size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(!kMovingClasses); + if (kMovingCollector) { + return AllocMovableObjectUninstrumented(self, klass, num_bytes); + } else { + return AllocNonMovableObjectUninstrumented(self, klass, num_bytes); + } + } + mirror::Object* AllocNonMovableObjectInstrumented(Thread* self, mirror::Class* klass, + size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* AllocNonMovableObjectUninstrumented(Thread* self, mirror::Class* klass, + size_t num_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void DebugCheckPreconditionsForAllobObject(mirror::Class* c, size_t byte_count) + // Visit all of the live objects in the heap. + void VisitObjects(ObjectVisitorCallback callback, void* arg) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + + void SwapSemiSpaces() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + + void DebugCheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ThrowOutOfMemoryError(size_t byte_count, bool large_object_allocation); @@ -152,7 +189,7 @@ class Heap { // The given reference is believed to be to an object in the Java heap, check the soundness of it. void VerifyObjectImpl(const mirror::Object* o); void VerifyObject(const mirror::Object* o) { - if (o != NULL && this != NULL && verify_object_mode_ > kNoHeapVerification) { + if (o != nullptr && this != nullptr && verify_object_mode_ > kNoHeapVerification) { VerifyObjectImpl(o); } } @@ -169,7 +206,10 @@ class Heap { // A weaker test than IsLiveObject or VerifyObject that doesn't require the heap lock, // and doesn't abort on error, allowing the caller to report more // meaningful diagnostics. - bool IsHeapAddress(const mirror::Object* obj); + bool IsValidObjectAddress(const mirror::Object* obj) const; + + // Returns true if the address passed in is a heap address, doesn't need to be aligned. + bool IsHeapAddress(const mirror::Object* obj) const; // Returns true if 'obj' is a live heap object, false otherwise (including for invalid addresses). // Requires the heap lock to be held. @@ -177,6 +217,17 @@ class Heap { bool search_live_stack = true, bool sorted = false) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + // Returns true if there is any chance that the object (obj) will move. + bool IsMovableObject(const mirror::Object* obj) const; + + // Returns true if an object is in the temp space, if this happens its usually indicative of + // compaction related errors. + bool IsInTempSpace(const mirror::Object* obj) const; + + // Enables us to prevent GC until objects are released. + void IncrementDisableGC(Thread* self); + void DecrementDisableGC(Thread* self); + // Initiates an explicit garbage collection. void CollectGarbage(bool clear_soft_references) LOCKS_EXCLUDED(Locks::mutator_lock_); @@ -221,9 +272,9 @@ class Heap { // from the system. Doesn't allow the space to exceed its growth limit. void SetIdealFootprint(size_t max_allowed_footprint); - // Blocks the caller until the garbage collector becomes idle and returns - // true if we waited for the GC to complete. - collector::GcType WaitForConcurrentGcToComplete(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_); + // Blocks the caller until the garbage collector becomes idle and returns the type of GC we + // waited for. + collector::GcType WaitForGcToComplete(Thread* self) LOCKS_EXCLUDED(gc_complete_lock_); const std::vector<space::ContinuousSpace*>& GetContinuousSpaces() const { return continuous_spaces_; @@ -239,7 +290,10 @@ class Heap { MemberOffset reference_pendingNext_offset, MemberOffset finalizer_reference_zombie_offset); - mirror::Object* GetReferenceReferent(mirror::Object* reference); + void SetReferenceReferent(mirror::Object* reference, mirror::Object* referent) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* GetReferenceReferent(mirror::Object* reference) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ClearReferenceReferent(mirror::Object* reference) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Returns true if the reference object has not yet been enqueued. @@ -316,7 +370,7 @@ class Heap { } // Returns the number of objects currently allocated. - size_t GetObjectsAllocated() const; + size_t GetObjectsAllocated() const LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); // Returns the total number of objects allocated since the heap was created. size_t GetObjectsAllocatedEver() const; @@ -361,7 +415,8 @@ class Heap { void DumpForSigQuit(std::ostream& os); - size_t Trim(); + // Trim the managed and native heaps by releasing unused memory back to the OS. + void Trim(); accounting::HeapBitmap* GetLiveBitmap() SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { return live_bitmap_.get(); @@ -375,7 +430,7 @@ class Heap { return live_stack_.get(); } - void PreZygoteFork() LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); + void PreZygoteFork() NO_THREAD_SAFETY_ANALYSIS; // Mark and empty stack. void FlushAllocStack() @@ -386,6 +441,10 @@ class Heap { accounting::ObjectStack* stack) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + // Mark the specified allocation stack as live. + void MarkAllocStackAsLive(accounting::ObjectStack* stack) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + // Gets called when we get notified by ActivityThread that the process state has changed. void ListenForProcessStateChange(); @@ -393,8 +452,8 @@ class Heap { // Assumes there is only one image space. space::ImageSpace* GetImageSpace() const; - space::DlMallocSpace* GetAllocSpace() const { - return alloc_space_; + space::DlMallocSpace* GetNonMovingSpace() const { + return non_moving_space_; } space::LargeObjectSpace* GetLargeObjectsSpace() const { @@ -417,7 +476,7 @@ class Heap { return phantom_ref_queue_lock_; } - void DumpSpaces(); + void DumpSpaces(std::ostream& stream = LOG(INFO)); // GC performance measuring void DumpGcPerformanceInfo(std::ostream& os); @@ -442,7 +501,20 @@ class Heap { accounting::ModUnionTable* FindModUnionTableFromSpace(space::Space* space); void AddModUnionTable(accounting::ModUnionTable* mod_union_table); + mirror::Object* AllocMovableObjectInstrumented(Thread* self, mirror::Class* klass, + size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* AllocMovableObjectUninstrumented(Thread* self, mirror::Class* klass, + size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + bool IsCompilingBoot() const; + bool HasImageSpace() const; + private: + void Compact(space::ContinuousMemMapAllocSpace* target_space, + space::ContinuousMemMapAllocSpace* source_space); + bool TryAllocLargeObjectInstrumented(Thread* self, mirror::Class* c, size_t byte_count, mirror::Object** obj_ptr, size_t* bytes_allocated) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -471,6 +543,11 @@ class Heap { LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Allocate into a specific space. + mirror::Object* AllocateInto(Thread* self, space::AllocSpace* space, mirror::Class* c, + size_t bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Try to allocate a number of bytes, this function never does any GCs. mirror::Object* TryToAllocateInstrumented(Thread* self, space::AllocSpace* space, size_t alloc_size, bool grow, size_t* bytes_allocated) @@ -500,6 +577,17 @@ class Heap { // Pushes a list of cleared references out to the managed heap. void EnqueueClearedReferences(mirror::Object** cleared_references); + // Print a reference queue. + void PrintReferenceQueue(std::ostream& os, mirror::Object** queue); + + // Run the finalizers. + void RunFinalization(JNIEnv* env); + + // Blocks the caller until the garbage collector becomes idle and returns the type of GC we + // waited for. + collector::GcType WaitForGcToCompleteLocked(Thread* self) + EXCLUSIVE_LOCKS_REQUIRED(gc_complete_lock_); + void RequestHeapTrim() LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_); void RequestConcurrentGC(Thread* self) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_); bool IsGCRequestPending() const; @@ -537,9 +625,7 @@ class Heap { size_t GetPercentFree(); - void AddContinuousSpace(space::ContinuousSpace* space) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); - void AddDiscontinuousSpace(space::DiscontinuousSpace* space) - LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); + void AddSpace(space::Space* space) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_); // No thread saftey analysis since we call this everywhere and it is impossible to find a proper // lock ordering for it. @@ -560,8 +646,12 @@ class Heap { // All-known discontinuous spaces, where objects may be placed throughout virtual memory. std::vector<space::DiscontinuousSpace*> discontinuous_spaces_; - // The allocation space we are currently allocating into. - space::DlMallocSpace* alloc_space_; + // All-known alloc spaces, where objects may be or have been allocated. + std::vector<space::AllocSpace*> alloc_spaces_; + + // A space where non-movable objects are allocated, when compaction is enabled it contains + // Classes, ArtMethods, ArtFields, and non moving objects. + space::DlMallocSpace* non_moving_space_; // The large object space we are currently allocating into. space::LargeObjectSpace* large_object_space_; @@ -599,6 +689,11 @@ class Heap { // If we have a zygote space. bool have_zygote_space_; + // Number of pinned primitive arrays in the movable space. + // Block all GC until this hits zero, or we hit the timeout! + size_t number_gc_blockers_; + static constexpr size_t KGCBlockTimeout = 30000; + // Guards access to the state of GC, associated conditional variable is used to signal when a GC // completes. Mutex* gc_complete_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; @@ -635,6 +730,9 @@ class Heap { // The watermark at which a GC is performed inside of registerNativeAllocation. size_t native_footprint_limit_; + // Whether or not we need to run finalizers in the next native allocation. + bool native_need_to_run_finalization_; + // Activity manager members. jclass activity_thread_class_; jclass application_thread_class_; @@ -714,6 +812,11 @@ class Heap { // Second allocation stack so that we can process allocation with the heap unlocked. UniquePtr<accounting::ObjectStack> live_stack_; + // Bump pointer spaces. + space::BumpPointerSpace* bump_pointer_space_; + // Temp space is the space which the semispace collector copies to. + space::BumpPointerSpace* temp_space_; + // offset of java.lang.ref.Reference.referent MemberOffset reference_referent_offset_; @@ -748,11 +851,16 @@ class Heap { // The current state of heap verification, may be enabled or disabled. HeapVerificationMode verify_object_mode_; - std::vector<collector::MarkSweep*> mark_sweep_collectors_; + // GC disable count, error on GC if > 0. + size_t gc_disable_count_ GUARDED_BY(gc_complete_lock_); + + std::vector<collector::GarbageCollector*> garbage_collectors_; + collector::SemiSpace* semi_space_collector_; const bool running_on_valgrind_; friend class collector::MarkSweep; + friend class collector::SemiSpace; friend class VerifyReferenceCardVisitor; friend class VerifyReferenceVisitor; friend class VerifyObjectVisitor; diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc index 02708e8..8af2725 100644 --- a/runtime/gc/heap_test.cc +++ b/runtime/gc/heap_test.cc @@ -43,12 +43,14 @@ TEST_F(HeapTest, GarbageCollectClassLinkerInit) { ScopedObjectAccess soa(Thread::Current()); // garbage is created during ClassLinker::Init - mirror::Class* c = class_linker_->FindSystemClass("[Ljava/lang/Object;"); + SirtRef<mirror::Class> c(soa.Self(), class_linker_->FindSystemClass("[Ljava/lang/Object;")); for (size_t i = 0; i < 1024; ++i) { SirtRef<mirror::ObjectArray<mirror::Object> > array(soa.Self(), - mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), c, 2048)); + mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), c.get(), 2048)); for (size_t j = 0; j < 2048; ++j) { - array->Set(j, mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")); + mirror::String* string = mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!"); + // SIRT operator -> deferences the SIRT before running the method. + array->Set(j, string); } } } diff --git a/runtime/gc/space/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h new file mode 100644 index 0000000..85ef2f4 --- /dev/null +++ b/runtime/gc/space/bump_pointer_space-inl.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_INL_H_ +#define ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_INL_H_ + +#include "bump_pointer_space.h" + +namespace art { +namespace gc { +namespace space { + +inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) { + num_bytes = RoundUp(num_bytes, kAlignment); + byte* old_end; + byte* new_end; + do { + old_end = end_; + new_end = old_end + num_bytes; + // If there is no more room in the region, we are out of memory. + if (UNLIKELY(new_end > growth_end_)) { + return nullptr; + } + // TODO: Use a cas which always equals the size of pointers. + } while (android_atomic_cas(reinterpret_cast<int32_t>(old_end), + reinterpret_cast<int32_t>(new_end), + reinterpret_cast<volatile int32_t*>(&end_)) != 0); + // TODO: Less statistics? + total_bytes_allocated_.fetch_add(num_bytes); + num_objects_allocated_.fetch_add(1); + total_objects_allocated_.fetch_add(1); + return reinterpret_cast<mirror::Object*>(old_end); +} + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_INL_H_ diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc new file mode 100644 index 0000000..06ba57e --- /dev/null +++ b/runtime/gc/space/bump_pointer_space.cc @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bump_pointer_space.h" +#include "bump_pointer_space-inl.h" +#include "mirror/object-inl.h" +#include "mirror/class-inl.h" + +namespace art { +namespace gc { +namespace space { + +BumpPointerSpace* BumpPointerSpace::Create(const std::string& name, size_t capacity, + byte* requested_begin) { + capacity = RoundUp(capacity, kPageSize); + std::string error_msg; + UniquePtr<MemMap> mem_map(MemMap::MapAnonymous(name.c_str(), requested_begin, capacity, + PROT_READ | PROT_WRITE, &error_msg)); + if (mem_map.get() == nullptr) { + LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size " + << PrettySize(capacity) << " with message " << error_msg; + return nullptr; + } + return new BumpPointerSpace(name, mem_map.release()); +} + +BumpPointerSpace::BumpPointerSpace(const std::string& name, byte* begin, byte* limit) + : ContinuousMemMapAllocSpace(name, nullptr, begin, begin, limit, + kGcRetentionPolicyAlwaysCollect), + num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0), + growth_end_(limit) { +} + +BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap* mem_map) + : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->Begin(), mem_map->End(), + kGcRetentionPolicyAlwaysCollect), + num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0), + growth_end_(mem_map->End()) { +} + +mirror::Object* BumpPointerSpace::Alloc(Thread*, size_t num_bytes, size_t* bytes_allocated) { + mirror::Object* ret = AllocNonvirtual(num_bytes); + if (LIKELY(ret != nullptr)) { + *bytes_allocated = num_bytes; + } + return ret; +} + +size_t BumpPointerSpace::AllocationSize(const mirror::Object* obj) { + return AllocationSizeNonvirtual(obj); +} + +void BumpPointerSpace::Clear() { + // Release the pages back to the operating system. + CHECK_NE(madvise(Begin(), Limit() - Begin(), MADV_DONTNEED), -1) << "madvise failed"; + // Reset the end of the space back to the beginning, we move the end forward as we allocate + // objects. + SetEnd(Begin()); + growth_end_ = Limit(); + num_objects_allocated_ = 0; +} + +void BumpPointerSpace::Dump(std::ostream& os) const { + os << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(End()) << " - " + << reinterpret_cast<void*>(Limit()); +} + +mirror::Object* BumpPointerSpace::GetNextObject(mirror::Object* obj) { + const uintptr_t position = reinterpret_cast<uintptr_t>(obj) + obj->SizeOf(); + return reinterpret_cast<mirror::Object*>(RoundUp(position, kAlignment)); +} + +} // namespace space +} // namespace gc +} // namespace art diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h new file mode 100644 index 0000000..0faac0c --- /dev/null +++ b/runtime/gc/space/bump_pointer_space.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_ +#define ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_ + +#include "space.h" + +namespace art { +namespace gc { + +namespace collector { + class MarkSweep; +} // namespace collector + +namespace space { + +// A bump pointer space is a space where objects may be allocated and garbage collected. +class BumpPointerSpace : public ContinuousMemMapAllocSpace { + public: + typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg); + + SpaceType GetType() const { + return kSpaceTypeBumpPointerSpace; + } + + // Create a bump pointer space with the requested sizes. The requested base address is not + // guaranteed to be granted, if it is required, the caller should call Begin on the returned + // space to confirm the request was granted. + static BumpPointerSpace* Create(const std::string& name, size_t capacity, byte* requested_begin); + + // Allocate num_bytes, returns nullptr if the space is full. + virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated); + mirror::Object* AllocNonvirtual(size_t num_bytes); + + // Return the storage space required by obj. + virtual size_t AllocationSize(const mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + // Nos unless we support free lists. + virtual size_t Free(Thread*, mirror::Object*) { + return 0; + } + virtual size_t FreeList(Thread*, size_t, mirror::Object**) { + return 0; + } + + size_t AllocationSizeNonvirtual(const mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return obj->SizeOf(); + } + + // Removes the fork time growth limit on capacity, allowing the application to allocate up to the + // maximum reserved size of the heap. + void ClearGrowthLimit() { + growth_end_ = Limit(); + } + + // Override capacity so that we only return the possibly limited capacity + size_t Capacity() const { + return growth_end_ - begin_; + } + + // The total amount of memory reserved for the space. + size_t NonGrowthLimitCapacity() const { + return GetMemMap()->Size(); + } + + accounting::SpaceBitmap* GetLiveBitmap() const { + return nullptr; + } + + accounting::SpaceBitmap* GetMarkBitmap() const { + return nullptr; + } + + // Clear the memory and reset the pointer to the start of the space. + void Clear(); + + void Dump(std::ostream& os) const; + + uint64_t GetBytesAllocated() { + return Size(); + } + + uint64_t GetObjectsAllocated() { + return num_objects_allocated_; + } + + uint64_t GetTotalBytesAllocated() { + return total_bytes_allocated_; + } + + uint64_t GetTotalObjectsAllocated() { + return total_objects_allocated_; + } + + bool Contains(const mirror::Object* obj) const { + const byte* byte_obj = reinterpret_cast<const byte*>(obj); + return byte_obj >= Begin() && byte_obj < End(); + } + + // TODO: Change this? Mainly used for compacting to a particular region of memory. + BumpPointerSpace(const std::string& name, byte* begin, byte* limit); + + // Return the object which comes after obj, while ensuring alignment. + static mirror::Object* GetNextObject(mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + protected: + BumpPointerSpace(const std::string& name, MemMap* mem_map); + + size_t InternalAllocationSize(const mirror::Object* obj); + mirror::Object* AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) + EXCLUSIVE_LOCKS_REQUIRED(lock_); + + // Approximate number of bytes which have been allocated into the space. + AtomicInteger num_objects_allocated_; + AtomicInteger total_bytes_allocated_; + AtomicInteger total_objects_allocated_; + + // Alignment. + static constexpr size_t kAlignment = 8; + + byte* growth_end_; + + private: + friend class collector::MarkSweep; + DISALLOW_COPY_AND_ASSIGN(BumpPointerSpace); +}; + +} // namespace space +} // namespace gc +} // namespace art + +#endif // ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_ diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 9ebc16a..8a5e33a 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -102,8 +102,8 @@ class ValgrindDlMallocSpace : public DlMallocSpace { } ValgrindDlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, - byte* end, size_t growth_limit, size_t initial_size) : - DlMallocSpace(name, mem_map, mspace, begin, end, growth_limit) { + byte* end, byte* limit, size_t growth_limit, size_t initial_size) : + DlMallocSpace(name, mem_map, mspace, begin, end, limit, growth_limit) { VALGRIND_MAKE_MEM_UNDEFINED(mem_map->Begin() + initial_size, mem_map->Size() - initial_size); } @@ -117,15 +117,13 @@ class ValgrindDlMallocSpace : public DlMallocSpace { size_t DlMallocSpace::bitmap_index_ = 0; DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, - byte* end, size_t growth_limit) - : MemMapSpace(name, mem_map, end - begin, kGcRetentionPolicyAlwaysCollect), + byte* end, byte* limit, size_t growth_limit) + : ContinuousMemMapAllocSpace(name, mem_map, begin, end, limit, kGcRetentionPolicyAlwaysCollect), recent_free_pos_(0), total_bytes_freed_(0), total_objects_freed_(0), lock_("allocation space lock", kAllocSpaceLock), mspace_(mspace), growth_limit_(growth_limit) { CHECK(mspace != NULL); - size_t bitmap_index = bitmap_index_++; - static const uintptr_t kGcCardSize = static_cast<uintptr_t>(accounting::CardTable::kCardSize); CHECK(IsAligned<kGcCardSize>(reinterpret_cast<uintptr_t>(mem_map->Begin()))); CHECK(IsAligned<kGcCardSize>(reinterpret_cast<uintptr_t>(mem_map->End()))); @@ -133,12 +131,10 @@ DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* msp StringPrintf("allocspace %s live-bitmap %d", name.c_str(), static_cast<int>(bitmap_index)), Begin(), Capacity())); DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace live bitmap #" << bitmap_index; - mark_bitmap_.reset(accounting::SpaceBitmap::Create( StringPrintf("allocspace %s mark-bitmap %d", name.c_str(), static_cast<int>(bitmap_index)), Begin(), Capacity())); DCHECK(live_bitmap_.get() != NULL) << "could not create allocspace mark bitmap #" << bitmap_index; - for (auto& freed : recent_freed_objects_) { freed.first = nullptr; freed.second = nullptr; @@ -207,12 +203,14 @@ DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_siz // Everything is set so record in immutable structure and leave MemMap* mem_map_ptr = mem_map.release(); DlMallocSpace* space; + byte* begin = mem_map_ptr->Begin(); if (RUNNING_ON_VALGRIND > 0) { - space = new ValgrindDlMallocSpace(name, mem_map_ptr, mspace, mem_map_ptr->Begin(), end, + space = new ValgrindDlMallocSpace(name, mem_map_ptr, mspace, begin, end, begin + capacity, growth_limit, initial_size); } else { - space = new DlMallocSpace(name, mem_map_ptr, mspace, mem_map_ptr->Begin(), end, growth_limit); + space = new DlMallocSpace(name, mem_map_ptr, mspace, begin, end, begin + capacity, growth_limit); } + // We start out with only the initial size possibly containing objects. if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Space::CreateAllocSpace exiting (" << PrettyDuration(NanoTime() - start_time) << " ) " << *space; @@ -318,7 +316,8 @@ DlMallocSpace* DlMallocSpace::CreateZygoteSpace(const char* alloc_space_name) { CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), alloc_space_name); } DlMallocSpace* alloc_space = - new DlMallocSpace(alloc_space_name, mem_map.release(), mspace, end_, end, growth_limit); + new DlMallocSpace(alloc_space_name, mem_map.release(), mspace, end_, end, limit_, + growth_limit); live_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End())); CHECK_EQ(live_bitmap_->HeapLimit(), reinterpret_cast<uintptr_t>(End())); mark_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End())); @@ -343,8 +342,7 @@ mirror::Class* DlMallocSpace::FindRecentFreedObject(const mirror::Object* obj) { } void DlMallocSpace::RegisterRecentFree(mirror::Object* ptr) { - recent_freed_objects_[recent_free_pos_].first = ptr; - recent_freed_objects_[recent_free_pos_].second = ptr->GetClass(); + recent_freed_objects_[recent_free_pos_] = std::make_pair(ptr, ptr->GetClass()); recent_free_pos_ = (recent_free_pos_ + 1) & kRecentFreeMask; } @@ -412,8 +410,8 @@ size_t DlMallocSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** p // Callback from dlmalloc when it needs to increase the footprint extern "C" void* art_heap_morecore(void* mspace, intptr_t increment) { Heap* heap = Runtime::Current()->GetHeap(); - DCHECK_EQ(heap->GetAllocSpace()->GetMspace(), mspace); - return heap->GetAllocSpace()->MoreCore(increment); + DCHECK_EQ(heap->GetNonMovingSpace()->GetMspace(), mspace); + return heap->GetNonMovingSpace()->MoreCore(increment); } void* DlMallocSpace::MoreCore(intptr_t increment) { @@ -482,6 +480,29 @@ size_t DlMallocSpace::GetFootprintLimit() { return mspace_footprint_limit(mspace_); } +// Returns the old mark bitmap. +accounting::SpaceBitmap* DlMallocSpace::BindLiveToMarkBitmap() { + accounting::SpaceBitmap* live_bitmap = GetLiveBitmap(); + accounting::SpaceBitmap* mark_bitmap = mark_bitmap_.release(); + temp_bitmap_.reset(mark_bitmap); + mark_bitmap_.reset(live_bitmap); + return mark_bitmap; +} + +bool DlMallocSpace::HasBoundBitmaps() const { + return temp_bitmap_.get() != nullptr; +} + +void DlMallocSpace::UnBindBitmaps() { + CHECK(HasBoundBitmaps()); + // At this point, the temp_bitmap holds our old mark bitmap. + accounting::SpaceBitmap* new_bitmap = temp_bitmap_.release(); + CHECK_EQ(mark_bitmap_.release(), live_bitmap_.get()); + mark_bitmap_.reset(new_bitmap); + DCHECK(temp_bitmap_.get() == NULL); +} + + void DlMallocSpace::SetFootprintLimit(size_t new_size) { MutexLock mu(Thread::Current(), lock_); VLOG(heap) << "DLMallocSpace::SetFootprintLimit " << PrettySize(new_size); @@ -504,17 +525,25 @@ void DlMallocSpace::Dump(std::ostream& os) const { } uint64_t DlMallocSpace::GetBytesAllocated() { - MutexLock mu(Thread::Current(), lock_); - size_t bytes_allocated = 0; - mspace_inspect_all(mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); - return bytes_allocated; + if (mspace_ != nullptr) { + MutexLock mu(Thread::Current(), lock_); + size_t bytes_allocated = 0; + mspace_inspect_all(mspace_, DlmallocBytesAllocatedCallback, &bytes_allocated); + return bytes_allocated; + } else { + return Size(); + } } uint64_t DlMallocSpace::GetObjectsAllocated() { - MutexLock mu(Thread::Current(), lock_); - size_t objects_allocated = 0; - mspace_inspect_all(mspace_, DlmallocObjectsAllocatedCallback, &objects_allocated); - return objects_allocated; + if (mspace_ != nullptr) { + MutexLock mu(Thread::Current(), lock_); + size_t objects_allocated = 0; + mspace_inspect_all(mspace_, DlmallocObjectsAllocatedCallback, &objects_allocated); + return objects_allocated; + } else { + return 0; + } } } // namespace space diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index 522535e..59dafe3 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -30,7 +30,7 @@ namespace collector { namespace space { // An alloc space is a space where objects may be allocated and garbage collected. -class DlMallocSpace : public MemMapSpace, public AllocSpace { +class DlMallocSpace : public ContinuousMemMapAllocSpace { public: typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg); @@ -136,19 +136,30 @@ class DlMallocSpace : public MemMapSpace, public AllocSpace { return GetObjectsAllocated() + total_objects_freed_; } + // Returns the old mark bitmap. + accounting::SpaceBitmap* BindLiveToMarkBitmap(); + bool HasBoundBitmaps() const; + void UnBindBitmaps(); + // Returns the class of a recently freed object. mirror::Class* FindRecentFreedObject(const mirror::Object* obj); + // Used to ensure that failure happens when you free / allocate into an invalidated space. If we + // don't do this we may get heap corruption instead of a segfault at null. + void InvalidateMSpace() { + mspace_ = nullptr; + } + protected: DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, - size_t growth_limit); + byte* limit, size_t growth_limit); private: size_t InternalAllocationSize(const mirror::Object* obj); mirror::Object* AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated) EXCLUSIVE_LOCKS_REQUIRED(lock_); bool Init(size_t initial_size, size_t maximum_size, size_t growth_size, byte* requested_base); - void RegisterRecentFree(mirror::Object* ptr); + void RegisterRecentFree(mirror::Object* ptr) EXCLUSIVE_LOCKS_REQUIRED(lock_); static void* CreateMallocSpace(void* base, size_t morecore_start, size_t initial_size); UniquePtr<accounting::SpaceBitmap> live_bitmap_; @@ -174,7 +185,7 @@ class DlMallocSpace : public MemMapSpace, public AllocSpace { Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; // Underlying malloc space - void* const mspace_; + void* mspace_; // The capacity of the alloc space until such time that ClearGrowthLimit is called. // The underlying mem_map_ controls the maximum size we allow the heap to grow to. The growth diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index e12ee06..c6177bd 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -39,8 +39,9 @@ AtomicInteger ImageSpace::bitmap_index_(0); ImageSpace::ImageSpace(const std::string& name, MemMap* mem_map, accounting::SpaceBitmap* live_bitmap) - : MemMapSpace(name, mem_map, mem_map->Size(), kGcRetentionPolicyNeverCollect) { - DCHECK(live_bitmap != NULL); + : MemMapSpace(name, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(), + kGcRetentionPolicyNeverCollect) { + DCHECK(live_bitmap != nullptr); live_bitmap_.reset(live_bitmap); } @@ -332,7 +333,7 @@ OatFile* ImageSpace::ReleaseOatFile() { void ImageSpace::Dump(std::ostream& os) const { os << GetType() - << "begin=" << reinterpret_cast<void*>(Begin()) + << " begin=" << reinterpret_cast<void*>(Begin()) << ",end=" << reinterpret_cast<void*>(End()) << ",size=" << PrettySize(Size()) << ",name=\"" << GetName() << "\"]"; diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h index ef889d4..07fb288 100644 --- a/runtime/gc/space/large_object_space.h +++ b/runtime/gc/space/large_object_space.h @@ -59,6 +59,14 @@ class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace { size_t FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs); + virtual bool IsAllocSpace() const { + return true; + } + + virtual AllocSpace* AsAllocSpace() { + return this; + } + protected: explicit LargeObjectSpace(const std::string& name); diff --git a/runtime/gc/space/space-inl.h b/runtime/gc/space/space-inl.h index 2c3b93c..f1031ff 100644 --- a/runtime/gc/space/space-inl.h +++ b/runtime/gc/space/space-inl.h @@ -27,18 +27,28 @@ namespace gc { namespace space { inline ImageSpace* Space::AsImageSpace() { - DCHECK_EQ(GetType(), kSpaceTypeImageSpace); + DCHECK(IsImageSpace()); return down_cast<ImageSpace*>(down_cast<MemMapSpace*>(this)); } inline DlMallocSpace* Space::AsDlMallocSpace() { - DCHECK(GetType() == kSpaceTypeAllocSpace || GetType() == kSpaceTypeZygoteSpace); + DCHECK(IsDlMallocSpace()); return down_cast<DlMallocSpace*>(down_cast<MemMapSpace*>(this)); } inline LargeObjectSpace* Space::AsLargeObjectSpace() { - DCHECK_EQ(GetType(), kSpaceTypeLargeObjectSpace); - return reinterpret_cast<LargeObjectSpace*>(this); + DCHECK(IsLargeObjectSpace()); + return down_cast<LargeObjectSpace*>(this); +} + +inline ContinuousSpace* Space::AsContinuousSpace() { + DCHECK(IsContinuousSpace()); + return down_cast<ContinuousSpace*>(this); +} + +inline DiscontinuousSpace* Space::AsDiscontinuousSpace() { + DCHECK(IsDiscontinuousSpace()); + return down_cast<DiscontinuousSpace*>(this); } } // namespace space diff --git a/runtime/gc/space/space.cc b/runtime/gc/space/space.cc index de48b74..8eb17e0 100644 --- a/runtime/gc/space/space.cc +++ b/runtime/gc/space/space.cc @@ -34,7 +34,6 @@ std::ostream& operator<<(std::ostream& os, const Space& space) { return os; } - DiscontinuousSpace::DiscontinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy) : Space(name, gc_retention_policy), diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 6dd7952..4c05dde 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -42,7 +42,10 @@ class Heap; namespace space { +class AllocSpace; +class ContinuousSpace; class DlMallocSpace; +class DiscontinuousSpace; class ImageSpace; class LargeObjectSpace; @@ -64,6 +67,7 @@ enum SpaceType { kSpaceTypeImageSpace, kSpaceTypeAllocSpace, kSpaceTypeZygoteSpace, + kSpaceTypeBumpPointerSpace, kSpaceTypeLargeObjectSpace, }; std::ostream& operator<<(std::ostream& os, const SpaceType& space_type); @@ -113,12 +117,35 @@ class Space { return GetType() == kSpaceTypeZygoteSpace; } + // Is this space a bump pointer space? + bool IsBumpPointerSpace() const { + return GetType() == kSpaceTypeBumpPointerSpace; + } + // Does this space hold large objects and implement the large object space abstraction? bool IsLargeObjectSpace() const { return GetType() == kSpaceTypeLargeObjectSpace; } LargeObjectSpace* AsLargeObjectSpace(); + virtual bool IsContinuousSpace() const { + return false; + } + ContinuousSpace* AsContinuousSpace(); + + virtual bool IsDiscontinuousSpace() const { + return false; + } + DiscontinuousSpace* AsDiscontinuousSpace(); + + virtual bool IsAllocSpace() const { + return false; + } + virtual AllocSpace* AsAllocSpace() { + LOG(FATAL) << "Unimplemented"; + return nullptr; + } + virtual ~Space() {} protected: @@ -131,13 +158,13 @@ class Space { // Name of the space that may vary due to the Zygote fork. std::string name_; - private: + protected: // When should objects within this space be reclaimed? Not constant as we vary it in the case // of Zygote forking. GcRetentionPolicy gc_retention_policy_; + private: friend class art::gc::Heap; - DISALLOW_COPY_AND_ASSIGN(Space); }; std::ostream& operator<<(std::ostream& os, const Space& space); @@ -180,16 +207,31 @@ class AllocSpace { // continuous spaces can be marked in the card table. class ContinuousSpace : public Space { public: - // Address at which the space begins + // Address at which the space begins. byte* Begin() const { return begin_; } - // Address at which the space ends, which may vary as the space is filled. + // Current address at which the space ends, which may vary as the space is filled. byte* End() const { return end_; } + // The end of the address range covered by the space. + byte* Limit() const { + return limit_; + } + + // Change the end of the space. Be careful with use since changing the end of a space to an + // invalid value may break the GC. + void SetEnd(byte* end) { + end_ = end; + } + + void SetLimit(byte* limit) { + limit_ = limit; + } + // Current size of space size_t Size() const { return End() - Begin(); @@ -198,31 +240,42 @@ class ContinuousSpace : public Space { virtual accounting::SpaceBitmap* GetLiveBitmap() const = 0; virtual accounting::SpaceBitmap* GetMarkBitmap() const = 0; + // Maximum which the mapped space can grow to. + virtual size_t Capacity() const { + return Limit() - Begin(); + } + // Is object within this space? We check to see if the pointer is beyond the end first as // continuous spaces are iterated over from low to high. bool HasAddress(const mirror::Object* obj) const { const byte* byte_ptr = reinterpret_cast<const byte*>(obj); - return byte_ptr < End() && byte_ptr >= Begin(); + return byte_ptr >= Begin() && byte_ptr < Limit(); } bool Contains(const mirror::Object* obj) const { return HasAddress(obj); } + virtual bool IsContinuousSpace() const { + return true; + } + virtual ~ContinuousSpace() {} protected: ContinuousSpace(const std::string& name, GcRetentionPolicy gc_retention_policy, - byte* begin, byte* end) : - Space(name, gc_retention_policy), begin_(begin), end_(end) { + byte* begin, byte* end, byte* limit) : + Space(name, gc_retention_policy), begin_(begin), end_(end), limit_(limit) { } - // The beginning of the storage for fast access. - byte* const begin_; + byte* begin_; // Current end of the space. - byte* end_; + byte* volatile end_; + + // Limit of the space. + byte* limit_; private: DISALLOW_COPY_AND_ASSIGN(ContinuousSpace); @@ -241,6 +294,10 @@ class DiscontinuousSpace : public Space { return mark_objects_.get(); } + virtual bool IsDiscontinuousSpace() const { + return true; + } + virtual ~DiscontinuousSpace() {} protected: @@ -255,25 +312,12 @@ class DiscontinuousSpace : public Space { class MemMapSpace : public ContinuousSpace { public: - // Maximum which the mapped space can grow to. - virtual size_t Capacity() const { - return mem_map_->Size(); - } - // Size of the space without a limit on its growth. By default this is just the Capacity, but // for the allocation space we support starting with a small heap and then extending it. virtual size_t NonGrowthLimitCapacity() const { return Capacity(); } - protected: - MemMapSpace(const std::string& name, MemMap* mem_map, size_t initial_size, - GcRetentionPolicy gc_retention_policy) - : ContinuousSpace(name, gc_retention_policy, - mem_map->Begin(), mem_map->Begin() + initial_size), - mem_map_(mem_map) { - } - MemMap* GetMemMap() { return mem_map_.get(); } @@ -282,13 +326,45 @@ class MemMapSpace : public ContinuousSpace { return mem_map_.get(); } - private: + protected: + MemMapSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end, byte* limit, + GcRetentionPolicy gc_retention_policy) + : ContinuousSpace(name, gc_retention_policy, begin, end, limit), + mem_map_(mem_map) { + } + // Underlying storage of the space UniquePtr<MemMap> mem_map_; + private: DISALLOW_COPY_AND_ASSIGN(MemMapSpace); }; +// Used by the heap compaction interface to enable copying from one type of alloc space to another. +class ContinuousMemMapAllocSpace : public MemMapSpace, public AllocSpace { + public: + virtual bool IsAllocSpace() const { + return true; + } + + virtual AllocSpace* AsAllocSpace() { + return this; + } + + virtual void Clear() { + LOG(FATAL) << "Unimplemented"; + } + + protected: + ContinuousMemMapAllocSpace(const std::string& name, MemMap* mem_map, byte* begin, + byte* end, byte* limit, GcRetentionPolicy gc_retention_policy) + : MemMapSpace(name, mem_map, begin, end, limit, gc_retention_policy) { + } + + private: + DISALLOW_COPY_AND_ASSIGN(ContinuousMemMapAllocSpace); +}; + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc index 455168c..383714b 100644 --- a/runtime/gc/space/space_test.cc +++ b/runtime/gc/space/space_test.cc @@ -33,8 +33,8 @@ class SpaceTest : public CommonTest { int round, size_t growth_limit); void SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size); - void AddContinuousSpace(ContinuousSpace* space) { - Runtime::Current()->GetHeap()->AddContinuousSpace(space); + void AddSpace(ContinuousSpace* space) { + Runtime::Current()->GetHeap()->AddSpace(space); } }; @@ -91,7 +91,7 @@ TEST_F(SpaceTest, ZygoteSpace) { ASSERT_TRUE(space != NULL); // Make space findable to the heap, will also delete space when runtime is cleaned up - AddContinuousSpace(space); + AddSpace(space); Thread* self = Thread::Current(); // Succeeds, fits without adjusting the footprint limit. @@ -136,7 +136,7 @@ TEST_F(SpaceTest, ZygoteSpace) { space = space->CreateZygoteSpace("alloc space"); // Make space findable to the heap, will also delete space when runtime is cleaned up - AddContinuousSpace(space); + AddSpace(space); // Succeeds, fits without adjusting the footprint limit. ptr1 = space->Alloc(self, 1 * MB, &dummy); @@ -164,7 +164,7 @@ TEST_F(SpaceTest, AllocAndFree) { Thread* self = Thread::Current(); // Make space findable to the heap, will also delete space when runtime is cleaned up - AddContinuousSpace(space); + AddSpace(space); // Succeeds, fits without adjusting the footprint limit. mirror::Object* ptr1 = space->Alloc(self, 1 * MB, &dummy); @@ -270,7 +270,7 @@ TEST_F(SpaceTest, AllocAndFreeList) { ASSERT_TRUE(space != NULL); // Make space findable to the heap, will also delete space when runtime is cleaned up - AddContinuousSpace(space); + AddSpace(space); Thread* self = Thread::Current(); // Succeeds, fits without adjusting the max allowed footprint. @@ -467,7 +467,7 @@ void SpaceTest::SizeFootPrintGrowthLimitAndTrimDriver(size_t object_size) { EXPECT_EQ(space->NonGrowthLimitCapacity(), capacity); // Make space findable to the heap, will also delete space when runtime is cleaned up - AddContinuousSpace(space); + AddSpace(space); // In this round we don't allocate with growth and therefore can't grow past the initial size. // This effectively makes the growth_limit the initial_size, so assert this. diff --git a/runtime/globals.h b/runtime/globals.h index 31574ff..10426b0 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -73,6 +73,15 @@ const bool kIsTargetBuild = true; const bool kIsTargetBuild = false; #endif +// Garbage collector constants. +static constexpr bool kMovingCollector = false; +// True if we allow moving classes. +static constexpr bool kMovingClasses = false; +// True if we allow moving fields. +static constexpr bool kMovingFields = false; +// True if we allow moving methods. +static constexpr bool kMovingMethods = false; + } // namespace art #endif // ART_RUNTIME_GLOBALS_H_ diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index 8f9e072..a829e97 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -48,7 +48,7 @@ void InternTable::VisitRoots(RootVisitor* visitor, void* arg, MutexLock mu(Thread::Current(), intern_table_lock_); if (!only_dirty || is_dirty_) { for (auto& strong_intern : strong_interns_) { - strong_intern.second = reinterpret_cast<mirror::String*>(visitor(strong_intern.second, arg)); + strong_intern.second = down_cast<mirror::String*>(visitor(strong_intern.second, arg)); DCHECK(strong_intern.second != nullptr); } @@ -59,8 +59,7 @@ void InternTable::VisitRoots(RootVisitor* visitor, void* arg, // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots. } -mirror::String* InternTable::Lookup(Table& table, mirror::String* s, - uint32_t hash_code) { +mirror::String* InternTable::Lookup(Table& table, mirror::String* s, uint32_t hash_code) { intern_table_lock_.AssertHeld(Thread::Current()); for (auto it = table.find(hash_code), end = table.end(); it != end; ++it) { mirror::String* existing_string = it->second; @@ -71,8 +70,7 @@ mirror::String* InternTable::Lookup(Table& table, mirror::String* s, return NULL; } -mirror::String* InternTable::Insert(Table& table, mirror::String* s, - uint32_t hash_code) { +mirror::String* InternTable::Insert(Table& table, mirror::String* s, uint32_t hash_code) { intern_table_lock_.AssertHeld(Thread::Current()); table.insert(std::make_pair(hash_code, s)); return s; diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index d7555dd..9938478 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -430,8 +430,8 @@ extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh if (method->IsStatic()) { Class* declaringClass = method->GetDeclaringClass(); if (UNLIKELY(!declaringClass->IsInitializing())) { - if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass, - true, true))) { + if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaringClass, true, + true))) { DCHECK(Thread::Current()->IsExceptionPending()); self->PopShadowFrame(); return; diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 19f55d2..08221b7 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -29,7 +29,7 @@ static inline void AssignRegister(ShadowFrame& new_shadow_frame, const ShadowFra size_t dest_reg, size_t src_reg) { // If both register locations contains the same value, the register probably holds a reference. int32_t src_value = shadow_frame.GetVReg(src_reg); - mirror::Object* o = shadow_frame.GetVRegReference(src_reg); + mirror::Object* o = shadow_frame.GetVRegReference<false>(src_reg); if (src_value == reinterpret_cast<int32_t>(o)) { new_shadow_frame.SetVRegReference(dest_reg, o); } else { @@ -193,7 +193,7 @@ bool DoFilledNewArray(const Instruction* inst, const ShadowFrame& shadow_frame, } return false; } - Object* newArray = Array::Alloc(self, arrayClass, length); + Object* newArray = Array::Alloc<kMovingCollector, true>(self, arrayClass, length); if (UNLIKELY(newArray == NULL)) { DCHECK(self->IsExceptionPending()); return false; @@ -233,7 +233,8 @@ static void UnstartedRuntimeInvoke(Thread* self, MethodHelper& mh, std::string name(PrettyMethod(shadow_frame->GetMethod())); if (name == "java.lang.Class java.lang.Class.forName(java.lang.String)") { std::string descriptor(DotToDescriptor(shadow_frame->GetVRegReference(arg_offset)->AsString()->ToModifiedUtf8().c_str())); - ClassLoader* class_loader = NULL; // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); + + SirtRef<ClassLoader> class_loader(self, nullptr); // shadow_frame.GetMethod()->GetDeclaringClass()->GetClassLoader(); Class* found = Runtime::Current()->GetClassLinker()->FindClass(descriptor.c_str(), class_loader); CHECK(found != NULL) << "Class.forName failed in un-started runtime for class: " diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index ec717c1..466edeb 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -22,6 +22,7 @@ #include <utility> #include <vector> +#include "atomic_integer.h" #include "base/logging.h" #include "base/mutex.h" #include "base/stl_util.h" @@ -292,8 +293,8 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con Class* field_type; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (sig[1] != '\0') { - ClassLoader* cl = GetClassLoader(soa); - field_type = class_linker->FindClass(sig, cl); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), GetClassLoader(soa)); + field_type = class_linker->FindClass(sig, class_loader); } else { field_type = class_linker->FindPrimitiveClass(*sig); } @@ -646,8 +647,8 @@ class JNI { ScopedObjectAccess soa(env); Class* c = NULL; if (runtime->IsStarted()) { - ClassLoader* cl = GetClassLoader(soa); - c = class_linker->FindClass(descriptor.c_str(), cl); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), GetClassLoader(soa)); + c = class_linker->FindClass(descriptor.c_str(), class_loader); } else { c = class_linker->FindSystemClass(descriptor.c_str()); } @@ -2002,14 +2003,22 @@ class JNI { String* s = soa.Decode<String*>(java_string); CharArray* chars = s->GetCharArray(); PinPrimitiveArray(soa, chars); - if (is_copy != NULL) { - *is_copy = JNI_FALSE; + if (is_copy != nullptr) { + *is_copy = JNI_TRUE; + } + int32_t char_count = s->GetLength(); + int32_t offset = s->GetOffset(); + jchar* bytes = new jchar[char_count + 1]; + for (int32_t i = 0; i < char_count; i++) { + bytes[i] = chars->Get(i + offset); } - return chars->GetData() + s->GetOffset(); + bytes[char_count] = '\0'; + return bytes; } - static void ReleaseStringChars(JNIEnv* env, jstring java_string, const jchar*) { + static void ReleaseStringChars(JNIEnv* env, jstring java_string, const jchar* chars) { CHECK_NON_NULL_ARGUMENT(GetStringUTFRegion, java_string); + delete[] chars; ScopedObjectAccess soa(env); UnpinPrimitiveArray(soa, soa.Decode<String*>(java_string)->GetCharArray()); } @@ -2120,8 +2129,8 @@ class JNI { // Find the class. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Class* array_class = class_linker->FindClass(descriptor.c_str(), - element_class->GetClassLoader()); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), element_class->GetClassLoader()); + Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); if (array_class == NULL) { return NULL; } @@ -2146,16 +2155,23 @@ class JNI { CHECK_NON_NULL_ARGUMENT(GetPrimitiveArrayCritical, java_array); ScopedObjectAccess soa(env); Array* array = soa.Decode<Array*>(java_array); + gc::Heap* heap = Runtime::Current()->GetHeap(); + if (heap->IsMovableObject(array)) { + heap->IncrementDisableGC(soa.Self()); + // Re-decode in case the object moved since IncrementDisableGC waits for GC to complete. + array = soa.Decode<Array*>(java_array); + } PinPrimitiveArray(soa, array); - if (is_copy != NULL) { + if (is_copy != nullptr) { *is_copy = JNI_FALSE; } - return array->GetRawData(array->GetClass()->GetComponentSize()); + void* address = array->GetRawData(array->GetClass()->GetComponentSize());; + return address; } - static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void*, jint mode) { + static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* elements, jint mode) { CHECK_NON_NULL_ARGUMENT(ReleasePrimitiveArrayCritical, array); - ReleasePrimitiveArray(env, array, mode); + ReleasePrimitiveArray(env, array, elements, mode); } static jboolean* GetBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* is_copy) { @@ -2206,36 +2222,40 @@ class JNI { return GetPrimitiveArray<jshortArray, jshort*, ShortArray>(soa, array, is_copy); } - static void ReleaseBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseBooleanArrayElements(JNIEnv* env, jbooleanArray array, jboolean* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseByteArrayElements(JNIEnv* env, jbyteArray array, jbyte*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseByteArrayElements(JNIEnv* env, jbyteArray array, jbyte* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseCharArrayElements(JNIEnv* env, jcharArray array, jchar*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseCharArrayElements(JNIEnv* env, jcharArray array, jchar* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseDoubleArrayElements(JNIEnv* env, jdoubleArray array, jdouble*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseDoubleArrayElements(JNIEnv* env, jdoubleArray array, jdouble* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseFloatArrayElements(JNIEnv* env, jfloatArray array, jfloat*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseFloatArrayElements(JNIEnv* env, jfloatArray array, jfloat* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseIntArrayElements(JNIEnv* env, jintArray array, jint*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseIntArrayElements(JNIEnv* env, jintArray array, jint* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseLongArrayElements(JNIEnv* env, jlongArray array, jlong*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseLongArrayElements(JNIEnv* env, jlongArray array, jlong* elements, jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } - static void ReleaseShortArrayElements(JNIEnv* env, jshortArray array, jshort*, jint mode) { - ReleasePrimitiveArray(env, array, mode); + static void ReleaseShortArrayElements(JNIEnv* env, jshortArray array, jshort* elements, + jint mode) { + ReleasePrimitiveArray(env, array, elements, mode); } static void GetBooleanArrayRegion(JNIEnv* env, jbooleanArray array, jsize start, jsize length, @@ -2551,19 +2571,49 @@ class JNI { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ArtArrayT* array = soa.Decode<ArtArrayT*>(java_array); PinPrimitiveArray(soa, array); - if (is_copy != NULL) { - *is_copy = JNI_FALSE; + // Only make a copy if necessary. + if (Runtime::Current()->GetHeap()->IsMovableObject(array)) { + if (is_copy != nullptr) { + *is_copy = JNI_TRUE; + } + static const size_t component_size = array->GetClass()->GetComponentSize(); + size_t size = array->GetLength() * component_size; + void* data = new uint64_t[RoundUp(size, 8) / 8]; + memcpy(data, array->GetData(), size); + return reinterpret_cast<CArrayT>(data); + } else { + if (is_copy != nullptr) { + *is_copy = JNI_FALSE; + } + return reinterpret_cast<CArrayT>(array->GetData()); } - return array->GetData(); } - template <typename ArrayT> - static void ReleasePrimitiveArray(JNIEnv* env, ArrayT java_array, jint mode) { + template <typename ArrayT, typename ElementT> + static void ReleasePrimitiveArray(JNIEnv* env, ArrayT java_array, ElementT* elements, jint mode) { + ScopedObjectAccess soa(env); + Array* array = soa.Decode<Array*>(java_array); + size_t component_size = array->GetClass()->GetComponentSize(); + void* array_data = array->GetRawData(component_size); + gc::Heap* heap = Runtime::Current()->GetHeap(); + bool is_copy = array_data != reinterpret_cast<void*>(elements); + size_t bytes = array->GetLength() * component_size; + VLOG(heap) << "Release primitive array " << env << " array_data " << array_data + << " elements " << reinterpret_cast<void*>(elements); + if (!is_copy && heap->IsMovableObject(array)) { + heap->DecrementDisableGC(soa.Self()); + } + // Don't need to copy if we had a direct pointer. + if (mode != JNI_ABORT && is_copy) { + memcpy(array_data, elements, bytes); + } if (mode != JNI_COMMIT) { - ScopedObjectAccess soa(env); - Array* array = soa.Decode<Array*>(java_array); - UnpinPrimitiveArray(soa, array); + if (is_copy) { + delete[] reinterpret_cast<uint64_t*>(elements); + } } + // TODO: Do we always unpin primitive array? + UnpinPrimitiveArray(soa, array); } template <typename JavaArrayT, typename JavaT, typename ArrayT> @@ -2854,6 +2904,18 @@ JNIEnvExt::JNIEnvExt(Thread* self, JavaVMExt* vm) JNIEnvExt::~JNIEnvExt() { } +jobject JNIEnvExt::NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (obj == nullptr) { + return nullptr; + } + return reinterpret_cast<jobject>(locals.Add(local_ref_cookie, obj)); +} + +void JNIEnvExt::DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (obj != nullptr) { + locals.Remove(local_ref_cookie, reinterpret_cast<IndirectRef>(obj)); + } +} void JNIEnvExt::SetCheckJniEnabled(bool enabled) { check_jni = enabled; functions = enabled ? GetCheckJniNativeInterface() : &gJniNativeInterface; @@ -3199,7 +3261,7 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo // the comments in the JNI FindClass function.) typedef int (*JNI_OnLoadFn)(JavaVM*, void*); JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym); - ClassLoader* old_class_loader = self->GetClassLoaderOverride(); + SirtRef<ClassLoader> old_class_loader(self, self->GetClassLoaderOverride()); self->SetClassLoaderOverride(class_loader); int version = 0; @@ -3209,7 +3271,7 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_lo version = (*jni_on_load)(this, NULL); } - self->SetClassLoaderOverride(old_class_loader); + self->SetClassLoaderOverride(old_class_loader.get()); if (version == JNI_ERR) { StringAppendF(detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str()); diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h index 888d5e5..96f7ae0 100644 --- a/runtime/jni_internal.h +++ b/runtime/jni_internal.h @@ -162,6 +162,9 @@ struct JNIEnvExt : public JNIEnv { return Offset(OFFSETOF_MEMBER(JNIEnvExt, self)); } + jobject NewLocalRef(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void DeleteLocalRef(jobject obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + Thread* const self; JavaVMExt* vm; diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index c389580..26b1836 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -86,19 +86,19 @@ class JniInternalTest : public CommonTest { const char* class_name = is_static ? "StaticLeafMethods" : "NonStaticLeafMethods"; jobject jclass_loader(LoadDex(class_name)); Thread* self = Thread::Current(); + SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr); SirtRef<mirror::ClassLoader> class_loader(self, ScopedObjectAccessUnchecked(self).Decode<mirror::ClassLoader*>(jclass_loader)); if (is_static) { - CompileDirectMethod(class_loader.get(), class_name, method_name, method_signature); + CompileDirectMethod(class_loader, class_name, method_name, method_signature); } else { - CompileVirtualMethod(NULL, "java.lang.Class", "isFinalizable", "()Z"); - CompileDirectMethod(NULL, "java.lang.Object", "<init>", "()V"); - CompileVirtualMethod(class_loader.get(), class_name, method_name, method_signature); + CompileVirtualMethod(null_class_loader, "java.lang.Class", "isFinalizable", "()Z"); + CompileDirectMethod(null_class_loader, "java.lang.Object", "<init>", "()V"); + CompileVirtualMethod(class_loader, class_name, method_name, method_signature); } - mirror::Class* c = class_linker_->FindClass(DotToDescriptor(class_name).c_str(), - class_loader.get()); + mirror::Class* c = class_linker_->FindClass(DotToDescriptor(class_name).c_str(), class_loader); CHECK(c != NULL); method = is_static ? c->FindDirectMethod(method_name, method_signature) @@ -1081,7 +1081,6 @@ TEST_F(JniInternalTest, RegisterNatives) { EXPECT_EQ(memcmp(&src_buf[0], xs, size * sizeof(scalar_type)), 0) \ << # get_elements_fn " not equal"; \ env_->release_elements_fn(a, xs, 0); \ - EXPECT_EQ(reinterpret_cast<uintptr_t>(v), reinterpret_cast<uintptr_t>(xs)) TEST_F(JniInternalTest, BooleanArrays) { EXPECT_PRIMITIVE_ARRAY(NewBooleanArray, GetBooleanArrayRegion, SetBooleanArrayRegion, @@ -1337,7 +1336,7 @@ TEST_F(JniInternalTest, GetStringChars_ReleaseStringChars) { jboolean is_copy = JNI_FALSE; chars = env_->GetStringChars(s, &is_copy); - EXPECT_EQ(JNI_FALSE, is_copy); + EXPECT_EQ(JNI_TRUE, is_copy); EXPECT_EQ(expected[0], chars[0]); EXPECT_EQ(expected[1], chars[1]); EXPECT_EQ(expected[2], chars[2]); @@ -1361,7 +1360,8 @@ TEST_F(JniInternalTest, GetStringCritical_ReleaseStringCritical) { jboolean is_copy = JNI_FALSE; chars = env_->GetStringCritical(s, &is_copy); - EXPECT_EQ(JNI_FALSE, is_copy); + // TODO: Fix GetStringCritical to use the same mechanism as GetPrimitiveArrayElementsCritical. + EXPECT_EQ(JNI_TRUE, is_copy); EXPECT_EQ(expected[0], chars[0]); EXPECT_EQ(expected[1], chars[1]); EXPECT_EQ(expected[2], chars[2]); @@ -1669,9 +1669,9 @@ TEST_F(JniInternalTest, StaticMainMethod) { jobject jclass_loader = LoadDex("Main"); SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(jclass_loader)); - CompileDirectMethod(class_loader.get(), "Main", "main", "([Ljava/lang/String;)V"); + CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V"); - mirror::Class* klass = class_linker_->FindClass("LMain;", class_loader.get()); + mirror::Class* klass = class_linker_->FindClass("LMain;", class_loader); ASSERT_TRUE(klass != NULL); mirror::ArtMethod* method = klass->FindDirectMethod("main", "([Ljava/lang/String;)V"); diff --git a/runtime/lock_word-inl.h b/runtime/lock_word-inl.h index efd3d9d..aea10c2 100644 --- a/runtime/lock_word-inl.h +++ b/runtime/lock_word-inl.h @@ -36,6 +36,11 @@ inline Monitor* LockWord::FatLockMonitor() const { return reinterpret_cast<Monitor*>(value_ << kStateSize); } +inline size_t LockWord::ForwardingAddress() const { + DCHECK_EQ(GetState(), kForwardingAddress); + return static_cast<size_t>(value_ << kStateSize); +} + inline LockWord::LockWord() : value_(0) { DCHECK_EQ(GetState(), kUnlocked); } diff --git a/runtime/lock_word.h b/runtime/lock_word.h index 1882ae6..d24a3bb 100644 --- a/runtime/lock_word.h +++ b/runtime/lock_word.h @@ -21,6 +21,7 @@ #include <stdint.h> #include "base/logging.h" +#include "utils.h" namespace art { namespace mirror { @@ -73,6 +74,7 @@ class LockWord { kStateThinOrUnlocked = 0, kStateFat = 1, kStateHash = 2, + kStateForwardingAddress = 3, // When the state is kHashCode, the non-state bits hold the hashcode. kHashShift = 0, @@ -86,6 +88,11 @@ class LockWord { (kStateThinOrUnlocked << kStateShift)); } + static LockWord FromForwardingAddress(size_t target) { + DCHECK(IsAligned < 1 << kStateSize>(target)); + return LockWord((target >> kStateSize) | (kStateForwardingAddress << kStateShift)); + } + static LockWord FromHashCode(uint32_t hash_code) { CHECK_LE(hash_code, static_cast<uint32_t>(kHashMask)); return LockWord((hash_code << kHashShift) | (kStateHash << kStateShift)); @@ -96,19 +103,25 @@ class LockWord { kThinLocked, // Single uncontended owner. kFatLocked, // See associated monitor. kHashCode, // Lock word contains an identity hash. + kForwardingAddress, // Lock word contains the forwarding address of an object. }; LockState GetState() const { - uint32_t internal_state = (value_ >> kStateShift) & kStateMask; - if (value_ == 0) { + if (UNLIKELY(value_ == 0)) { return kUnlocked; - } else if (internal_state == kStateThinOrUnlocked) { - return kThinLocked; - } else if (internal_state == kStateHash) { - return kHashCode; } else { - DCHECK_EQ(internal_state, static_cast<uint32_t>(kStateFat)); - return kFatLocked; + uint32_t internal_state = (value_ >> kStateShift) & kStateMask; + switch (internal_state) { + case kStateThinOrUnlocked: + return kThinLocked; + case kStateHash: + return kHashCode; + case kStateForwardingAddress: + return kForwardingAddress; + default: + DCHECK_EQ(internal_state, static_cast<uint32_t>(kStateFat)); + return kFatLocked; + } } } @@ -121,6 +134,9 @@ class LockWord { // Return the Monitor encoded in a fat lock. Monitor* FatLockMonitor() const; + // Return the forwarding address stored in the monitor. + size_t ForwardingAddress() const; + // Default constructor with no lock ownership. LockWord(); diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index c60e714..ef73e4d 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -66,36 +66,36 @@ static inline Array* SetArrayLength(Array* array, size_t length) { return array; } -inline Array* Array::AllocInstrumented(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) { +template <bool kIsMovable, bool kIsInstrumented> +inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count, + size_t component_size) { size_t size = ComputeArraySize(self, array_class, component_count, component_size); if (UNLIKELY(size == 0)) { return NULL; } gc::Heap* heap = Runtime::Current()->GetHeap(); - Array* array = down_cast<Array*>(heap->AllocObjectInstrumented(self, array_class, size)); - return SetArrayLength(array, component_count); -} - -inline Array* Array::AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) { - size_t size = ComputeArraySize(self, array_class, component_count, component_size); - if (UNLIKELY(size == 0)) { - return NULL; + Array* array = nullptr; + if (kIsMovable) { + if (kIsInstrumented) { + array = down_cast<Array*>(heap->AllocMovableObjectInstrumented(self, array_class, size)); + } else { + array = down_cast<Array*>(heap->AllocMovableObjectUninstrumented(self, array_class, size)); + } + } else { + if (kIsInstrumented) { + array = down_cast<Array*>(heap->AllocNonMovableObjectInstrumented(self, array_class, size)); + } else { + array = down_cast<Array*>(heap->AllocNonMovableObjectUninstrumented(self, array_class, size)); + } } - gc::Heap* heap = Runtime::Current()->GetHeap(); - Array* array = down_cast<Array*>(heap->AllocObjectUninstrumented(self, array_class, size)); return SetArrayLength(array, component_count); } -inline Array* Array::AllocInstrumented(Thread* self, Class* array_class, int32_t component_count) { - DCHECK(array_class->IsArrayClass()); - return AllocInstrumented(self, array_class, component_count, array_class->GetComponentSize()); -} - -inline Array* Array::AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count) { +template <bool kIsMovable, bool kIsInstrumented> +inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_count) { DCHECK(array_class->IsArrayClass()); - return AllocUninstrumented(self, array_class, component_count, array_class->GetComponentSize()); + return Alloc<kIsMovable, kIsInstrumented>(self, array_class, component_count, + array_class->GetComponentSize()); } } // namespace mirror diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index 020085d..f8a2832 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -41,15 +41,16 @@ namespace mirror { // Recursively create an array with multiple dimensions. Elements may be // Objects or primitive types. static Array* RecursiveCreateMultiArray(Thread* self, Class* array_class, int current_dimension, - IntArray* dimensions) + SirtRef<mirror::IntArray>& dimensions) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { int32_t array_length = dimensions->Get(current_dimension); - SirtRef<Array> new_array(self, Array::Alloc(self, array_class, array_length)); + SirtRef<Array> new_array(self, Array::Alloc<kMovingCollector, true>(self, array_class, + array_length)); if (UNLIKELY(new_array.get() == NULL)) { CHECK(self->IsExceptionPending()); return NULL; } - if ((current_dimension + 1) < dimensions->GetLength()) { + if (current_dimension + 1 < dimensions->GetLength()) { // Create a new sub-array in every element of the array. for (int32_t i = 0; i < array_length; i++) { Array* sub_array = RecursiveCreateMultiArray(self, array_class->GetComponentType(), @@ -87,13 +88,15 @@ Array* Array::CreateMultiArray(Thread* self, Class* element_class, IntArray* dim // Find/generate the array class. ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - Class* array_class = class_linker->FindClass(descriptor.c_str(), element_class->GetClassLoader()); + SirtRef<mirror::ClassLoader> class_loader(self, element_class->GetClassLoader()); + Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); if (UNLIKELY(array_class == NULL)) { CHECK(self->IsExceptionPending()); return NULL; } // create the array - Array* new_array = RecursiveCreateMultiArray(self, array_class, 0, dimensions); + SirtRef<mirror::IntArray> sirt_dimensions(self, dimensions); + Array* new_array = RecursiveCreateMultiArray(self, array_class, 0, sirt_dimensions); if (UNLIKELY(new_array == NULL)) { CHECK(self->IsExceptionPending()); return NULL; @@ -112,7 +115,7 @@ void Array::ThrowArrayStoreException(Object* object) const { template<typename T> PrimitiveArray<T>* PrimitiveArray<T>::Alloc(Thread* self, size_t length) { DCHECK(array_class_ != NULL); - Array* raw_array = Array::Alloc(self, array_class_, length, sizeof(T)); + Array* raw_array = Array::Alloc<kMovingCollector, true>(self, array_class_, length, sizeof(T)); return down_cast<PrimitiveArray<T>*>(raw_array); } diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h index 570dcaa..584a4c0 100644 --- a/runtime/mirror/array.h +++ b/runtime/mirror/array.h @@ -24,28 +24,15 @@ namespace mirror { class MANAGED Array : public Object { public: - // A convenience for code that doesn't know the component size, - // and doesn't want to have to work it out itself. + // A convenience for code that doesn't know the component size, and doesn't want to have to work + // it out itself. + template <bool kIsMovable, bool kIsInstrumented> static Array* Alloc(Thread* self, Class* array_class, int32_t component_count) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocInstrumented(self, array_class, component_count); - } - static Array* AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static Array* AllocInstrumented(Thread* self, Class* array_class, int32_t component_count) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + template <bool kIsMovable, bool kIsInstrumented> static Array* Alloc(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocInstrumented(self, array_class, component_count, component_size); - } - static Array* AllocUninstrumented(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static Array* AllocInstrumented(Thread* self, Class* array_class, int32_t component_count, - size_t component_size) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + size_t component_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static Array* CreateMultiArray(Thread* self, Class* element_class, IntArray* dimensions) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 7f3a302..406ab1b 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -357,14 +357,23 @@ inline void Class::CheckObjectAlloc() { DCHECK_GE(this->object_size_, sizeof(Object)); } -inline Object* Class::AllocObjectInstrumented(Thread* self) { +template <bool kIsMovable, bool kIsInstrumented> +inline Object* Class::Alloc(Thread* self) { CheckObjectAlloc(); - return Runtime::Current()->GetHeap()->AllocObjectInstrumented(self, this, this->object_size_); -} - -inline Object* Class::AllocObjectUninstrumented(Thread* self) { - CheckObjectAlloc(); - return Runtime::Current()->GetHeap()->AllocObjectUninstrumented(self, this, this->object_size_); + gc::Heap* heap = Runtime::Current()->GetHeap(); + if (kIsMovable) { + if (kIsInstrumented) { + return heap->AllocMovableObjectInstrumented(self, this, this->object_size_); + } else { + return heap->AllocMovableObjectUninstrumented(self, this, this->object_size_); + } + } else { + if (kIsInstrumented) { + return heap->AllocNonMovableObjectInstrumented(self, this, this->object_size_); + } else { + return heap->AllocNonMovableObjectUninstrumented(self, this, this->object_size_); + } + } } } // namespace mirror diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index f3cb54a..cdc5ab2 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -52,7 +52,8 @@ void Class::ResetClass() { void Class::SetStatus(Status new_status, Thread* self) { Status old_status = GetStatus(); - bool class_linker_initialized = Runtime::Current()->GetClassLinker() != nullptr; + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + bool class_linker_initialized = class_linker != nullptr && class_linker->IsInitialized(); if (LIKELY(class_linker_initialized)) { if (UNLIKELY(new_status <= old_status && new_status != kStatusError)) { LOG(FATAL) << "Unexpected change back of class status for " << PrettyClass(this) << " " @@ -588,7 +589,6 @@ ArtField* Class::FindDeclaredStaticField(const DexCache* dex_cache, uint32_t dex ArtField* Class::FindStaticField(const StringPiece& name, const StringPiece& type) { // Is the field in this class (or its interfaces), or any of its // superclasses (or their interfaces)? - ClassHelper kh; for (Class* k = this; k != NULL; k = k->GetSuperClass()) { // Is the field in this class? ArtField* f = k->FindDeclaredStaticField(name, type); @@ -596,7 +596,7 @@ ArtField* Class::FindStaticField(const StringPiece& name, const StringPiece& typ return f; } // Is this field in any of this class' interfaces? - kh.ChangeClass(k); + ClassHelper kh(k); for (uint32_t i = 0; i < kh.NumDirectInterfaces(); ++i) { Class* interface = kh.GetDirectInterface(i); f = interface->FindStaticField(name, type); @@ -609,7 +609,6 @@ ArtField* Class::FindStaticField(const StringPiece& name, const StringPiece& typ } ArtField* Class::FindStaticField(const DexCache* dex_cache, uint32_t dex_field_idx) { - ClassHelper kh; for (Class* k = this; k != NULL; k = k->GetSuperClass()) { // Is the field in this class? ArtField* f = k->FindDeclaredStaticField(dex_cache, dex_field_idx); @@ -617,7 +616,7 @@ ArtField* Class::FindStaticField(const DexCache* dex_cache, uint32_t dex_field_i return f; } // Is this field in any of this class' interfaces? - kh.ChangeClass(k); + ClassHelper kh(k); for (uint32_t i = 0; i < kh.NumDirectInterfaces(); ++i) { Class* interface = kh.GetDirectInterface(i); f = interface->FindStaticField(dex_cache, dex_field_idx); @@ -631,7 +630,6 @@ ArtField* Class::FindStaticField(const DexCache* dex_cache, uint32_t dex_field_i ArtField* Class::FindField(const StringPiece& name, const StringPiece& type) { // Find a field using the JLS field resolution order - ClassHelper kh; for (Class* k = this; k != NULL; k = k->GetSuperClass()) { // Is the field in this class? ArtField* f = k->FindDeclaredInstanceField(name, type); @@ -643,7 +641,7 @@ ArtField* Class::FindField(const StringPiece& name, const StringPiece& type) { return f; } // Is this field in any of this class' interfaces? - kh.ChangeClass(k); + ClassHelper kh(k); for (uint32_t i = 0; i < kh.NumDirectInterfaces(); ++i) { Class* interface = kh.GetDirectInterface(i); f = interface->FindStaticField(name, type); diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index ed1aad3..82077dc 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -378,11 +378,12 @@ class MANAGED Class : public StaticStorageBase { // Creates a raw object instance but does not invoke the default constructor. Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return AllocObjectInstrumented(self); + return Alloc<kMovingCollector, true>(self); } - Object* AllocObjectUninstrumented(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - Object* AllocObjectInstrumented(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Creates a raw object instance but does not invoke the default constructor. + template <bool kIsMovable, bool kIsInstrumented> + Object* Alloc(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsVariableSize() const { // Classes and arrays vary in size, and so the object_size_ field cannot diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 87d02c9..385ef5f 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -39,38 +39,48 @@ namespace art { namespace mirror { -Object* Object::Clone(Thread* self) { - mirror::Class* c = GetClass(); - DCHECK(!c->IsClassClass()); - // Object::SizeOf gets the right size even if we're an array. - // Using c->AllocObject() here would be wrong. - size_t num_bytes = SizeOf(); - gc::Heap* heap = Runtime::Current()->GetHeap(); - SirtRef<mirror::Object> sirt_this(self, this); - Object* copy = heap->AllocObject(self, c, num_bytes); - if (UNLIKELY(copy == nullptr)) { - return nullptr; - } +static Object* CopyObject(Thread* self, mirror::Object* dest, mirror::Object* src, size_t num_bytes) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Copy instance data. We assume memcpy copies by words. // TODO: expose and use move32. - byte* src_bytes = reinterpret_cast<byte*>(sirt_this.get()); - byte* dst_bytes = reinterpret_cast<byte*>(copy); + byte* src_bytes = reinterpret_cast<byte*>(src); + byte* dst_bytes = reinterpret_cast<byte*>(dest); size_t offset = sizeof(Object); memcpy(dst_bytes + offset, src_bytes + offset, num_bytes - offset); + gc::Heap* heap = Runtime::Current()->GetHeap(); // Perform write barriers on copied object references. - c = copy->GetClass(); // Re-read Class in case it moved. + Class* c = src->GetClass(); if (c->IsArrayClass()) { if (!c->GetComponentType()->IsPrimitive()) { - const ObjectArray<Object>* array = copy->AsObjectArray<Object>(); - heap->WriteBarrierArray(copy, 0, array->GetLength()); + const ObjectArray<Object>* array = dest->AsObjectArray<Object>(); + heap->WriteBarrierArray(dest, 0, array->GetLength()); } } else { - heap->WriteBarrierEveryFieldOf(copy); + heap->WriteBarrierEveryFieldOf(dest); } if (c->IsFinalizable()) { - SirtRef<mirror::Object> sirt_copy(self, copy); - heap->AddFinalizerReference(self, copy); - return sirt_copy.get(); + SirtRef<Object> sirt_dest(self, dest); + heap->AddFinalizerReference(self, dest); + return sirt_dest.get(); + } + return dest; +} + +Object* Object::Clone(Thread* self) { + CHECK(!IsClass()) << "Can't clone classes."; + // Object::SizeOf gets the right size even if we're an array. Using c->AllocObject() here would + // be wrong. + gc::Heap* heap = Runtime::Current()->GetHeap(); + size_t num_bytes = SizeOf(); + SirtRef<Object> this_object(self, this); + Object* copy; + if (heap->IsMovableObject(this)) { + copy = heap->AllocObject(self, GetClass(), num_bytes); + } else { + copy = heap->AllocNonMovableObject(self, GetClass(), num_bytes); + } + if (LIKELY(copy != nullptr)) { + return CopyObject(self, copy, this_object.get(), num_bytes); } return copy; } @@ -87,8 +97,9 @@ int32_t Object::GenerateIdentityHashCode() { } int32_t Object::IdentityHashCode() const { + mirror::Object* current_this = const_cast<mirror::Object*>(this); while (true) { - LockWord lw = GetLockWord(); + LockWord lw = current_this->GetLockWord(); switch (lw.GetState()) { case LockWord::kUnlocked: { // Try to compare and swap in a new hash, if we succeed we will return the hash on the next @@ -103,7 +114,10 @@ int32_t Object::IdentityHashCode() const { case LockWord::kThinLocked: { // Inflate the thin lock to a monitor and stick the hash code inside of the monitor. Thread* self = Thread::Current(); - Monitor::InflateThinLocked(self, const_cast<Object*>(this), lw, GenerateIdentityHashCode()); + SirtRef<mirror::Object> sirt_this(self, current_this); + Monitor::InflateThinLocked(self, sirt_this, lw, GenerateIdentityHashCode()); + // A GC may have occurred when we switched to kBlocked. + current_this = sirt_this.get(); break; } case LockWord::kFatLocked: { @@ -115,6 +129,10 @@ int32_t Object::IdentityHashCode() const { case LockWord::kHashCode: { return lw.GetHashCode(); } + default: { + LOG(FATAL) << "Invalid state during hashcode " << lw.GetState(); + break; + } } } LOG(FATAL) << "Unreachable"; diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index e8ea3f2..0fb2039 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -30,6 +30,7 @@ class LockWord; class Monitor; struct ObjectOffsets; class Thread; +template <typename T> class SirtRef; namespace mirror { diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h index abc88a3..478f4ec 100644 --- a/runtime/mirror/object_array-inl.h +++ b/runtime/mirror/object_array-inl.h @@ -23,6 +23,7 @@ #include "mirror/art_field.h" #include "mirror/class.h" #include "runtime.h" +#include "sirt_ref.h" #include "thread.h" namespace art { @@ -30,7 +31,7 @@ namespace mirror { template<class T> inline ObjectArray<T>* ObjectArray<T>::Alloc(Thread* self, Class* object_array_class, int32_t length) { - Array* array = Array::Alloc(self, object_array_class, length, sizeof(Object*)); + Array* array = Array::Alloc<kMovingCollector, true>(self, object_array_class, length, sizeof(Object*)); if (UNLIKELY(array == NULL)) { return NULL; } else { @@ -134,9 +135,11 @@ inline void ObjectArray<T>::Copy(const ObjectArray<T>* src, int src_pos, template<class T> inline ObjectArray<T>* ObjectArray<T>::CopyOf(Thread* self, int32_t new_length) { + // We may get copied by a compacting GC. + SirtRef<ObjectArray<T> > sirt_this(self, this); ObjectArray<T>* new_array = Alloc(self, GetClass(), new_length); - if (LIKELY(new_array != NULL)) { - Copy(this, 0, new_array, 0, std::min(GetLength(), new_length)); + if (LIKELY(new_array != nullptr)) { + Copy(sirt_this.get(), 0, new_array, 0, std::min(sirt_this->GetLength(), new_length)); } return new_array; } diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index d0d1ee4..8530317 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -144,15 +144,15 @@ TEST_F(ObjectTest, AllocObjectArray) { TEST_F(ObjectTest, AllocArray) { ScopedObjectAccess soa(Thread::Current()); Class* c = class_linker_->FindSystemClass("[I"); - SirtRef<Array> a(soa.Self(), Array::Alloc(soa.Self(), c, 1)); + SirtRef<Array> a(soa.Self(), Array::Alloc<kMovingCollector, true>(soa.Self(), c, 1)); ASSERT_TRUE(c == a->GetClass()); c = class_linker_->FindSystemClass("[Ljava/lang/Object;"); - a.reset(Array::Alloc(soa.Self(), c, 1)); + a.reset(Array::Alloc<kMovingCollector, true>(soa.Self(), c, 1)); ASSERT_TRUE(c == a->GetClass()); c = class_linker_->FindSystemClass("[[Ljava/lang/Object;"); - a.reset(Array::Alloc(soa.Self(), c, 1)); + a.reset(Array::Alloc<kMovingCollector, true>(soa.Self(), c, 1)); ASSERT_TRUE(c == a->GetClass()); } @@ -269,8 +269,9 @@ TEST_F(ObjectTest, StaticFieldFromCode) { const DexFile* dex_file = Runtime::Current()->GetCompileTimeClassPath(class_loader)[0]; CHECK(dex_file != NULL); + SirtRef<mirror::ClassLoader> loader(soa.Self(), soa.Decode<ClassLoader*>(class_loader)); Class* klass = - class_linker_->FindClass("LStaticsFromCode;", soa.Decode<ClassLoader*>(class_loader)); + class_linker_->FindClass("LStaticsFromCode;", loader); ArtMethod* clinit = klass->FindClassInitializer(); const DexFile::StringId* klass_string_id = dex_file->FindStringId("LStaticsFromCode;"); ASSERT_TRUE(klass_string_id != NULL); @@ -392,6 +393,7 @@ TEST_F(ObjectTest, StringLength) { } TEST_F(ObjectTest, DescriptorCompare) { + // Two classloaders conflicts in compile_time_class_paths_. ScopedObjectAccess soa(Thread::Current()); ClassLinker* linker = class_linker_; @@ -400,9 +402,9 @@ TEST_F(ObjectTest, DescriptorCompare) { SirtRef<ClassLoader> class_loader_1(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader_1)); SirtRef<ClassLoader> class_loader_2(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader_2)); - Class* klass1 = linker->FindClass("LProtoCompare;", class_loader_1.get()); + Class* klass1 = linker->FindClass("LProtoCompare;", class_loader_1); ASSERT_TRUE(klass1 != NULL); - Class* klass2 = linker->FindClass("LProtoCompare2;", class_loader_2.get()); + Class* klass2 = linker->FindClass("LProtoCompare2;", class_loader_2); ASSERT_TRUE(klass2 != NULL); ArtMethod* m1_1 = klass1->GetVirtualMethod(0); @@ -468,8 +470,8 @@ TEST_F(ObjectTest, InstanceOf) { jobject jclass_loader = LoadDex("XandY"); SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader)); - Class* X = class_linker_->FindClass("LX;", class_loader.get()); - Class* Y = class_linker_->FindClass("LY;", class_loader.get()); + Class* X = class_linker_->FindClass("LX;", class_loader); + Class* Y = class_linker_->FindClass("LY;", class_loader); ASSERT_TRUE(X != NULL); ASSERT_TRUE(Y != NULL); @@ -501,8 +503,8 @@ TEST_F(ObjectTest, IsAssignableFrom) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("XandY"); SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader)); - Class* X = class_linker_->FindClass("LX;", class_loader.get()); - Class* Y = class_linker_->FindClass("LY;", class_loader.get()); + Class* X = class_linker_->FindClass("LX;", class_loader); + Class* Y = class_linker_->FindClass("LY;", class_loader); EXPECT_TRUE(X->IsAssignableFrom(X)); EXPECT_TRUE(X->IsAssignableFrom(Y)); @@ -538,17 +540,17 @@ TEST_F(ObjectTest, IsAssignableFromArray) { ScopedObjectAccess soa(Thread::Current()); jobject jclass_loader = LoadDex("XandY"); SirtRef<ClassLoader> class_loader(soa.Self(), soa.Decode<ClassLoader*>(jclass_loader)); - Class* X = class_linker_->FindClass("LX;", class_loader.get()); - Class* Y = class_linker_->FindClass("LY;", class_loader.get()); + Class* X = class_linker_->FindClass("LX;", class_loader); + Class* Y = class_linker_->FindClass("LY;", class_loader); ASSERT_TRUE(X != NULL); ASSERT_TRUE(Y != NULL); - Class* YA = class_linker_->FindClass("[LY;", class_loader.get()); - Class* YAA = class_linker_->FindClass("[[LY;", class_loader.get()); + Class* YA = class_linker_->FindClass("[LY;", class_loader); + Class* YAA = class_linker_->FindClass("[[LY;", class_loader); ASSERT_TRUE(YA != NULL); ASSERT_TRUE(YAA != NULL); - Class* XAA = class_linker_->FindClass("[[LX;", class_loader.get()); + Class* XAA = class_linker_->FindClass("[[LX;", class_loader); ASSERT_TRUE(XAA != NULL); Class* O = class_linker_->FindSystemClass("Ljava/lang/Object;"); diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc index 9d76c6b..32a50fe 100644 --- a/runtime/mirror/stack_trace_element.cc +++ b/runtime/mirror/stack_trace_element.cc @@ -39,19 +39,19 @@ void StackTraceElement::ResetClass() { } StackTraceElement* StackTraceElement::Alloc(Thread* self, - String* declaring_class, - String* method_name, - String* file_name, + SirtRef<String>& declaring_class, + SirtRef<String>& method_name, + SirtRef<String>& file_name, int32_t line_number) { StackTraceElement* trace = down_cast<StackTraceElement*>(GetStackTraceElement()->AllocObject(self)); if (LIKELY(trace != NULL)) { trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, declaring_class_), - const_cast<String*>(declaring_class), false); + declaring_class.get(), false); trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, method_name_), - const_cast<String*>(method_name), false); + method_name.get(), false); trace->SetFieldObject(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, file_name_), - const_cast<String*>(file_name), false); + file_name.get(), false); trace->SetField32(OFFSET_OF_OBJECT_MEMBER(StackTraceElement, line_number_), line_number, false); } diff --git a/runtime/mirror/stack_trace_element.h b/runtime/mirror/stack_trace_element.h index a9751f9..2af5128 100644 --- a/runtime/mirror/stack_trace_element.h +++ b/runtime/mirror/stack_trace_element.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_MIRROR_STACK_TRACE_ELEMENT_H_ #include "object.h" +#include "sirt_ref.h" namespace art { @@ -49,9 +50,9 @@ class MANAGED StackTraceElement : public Object { } static StackTraceElement* Alloc(Thread* self, - String* declaring_class, - String* method_name, - String* file_name, + SirtRef<String>& declaring_class, + SirtRef<String>& method_name, + SirtRef<String>& file_name, int32_t line_number) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index 9c93f17..b372fe7 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -123,8 +123,8 @@ String* String::AllocFromUtf16(Thread* self, int32_t hash_code) { CHECK(utf16_data_in != NULL || utf16_length == 0); String* string = Alloc(self, GetJavaLangString(), utf16_length); - if (string == NULL) { - return NULL; + if (UNLIKELY(string == nullptr)) { + return nullptr; } // TODO: use 16-bit wide memset variant CharArray* array = const_cast<CharArray*>(string->GetCharArray()); @@ -143,8 +143,8 @@ String* String::AllocFromUtf16(Thread* self, } String* String::AllocFromModifiedUtf8(Thread* self, const char* utf) { - if (utf == NULL) { - return NULL; + if (UNLIKELY(utf == nullptr)) { + return nullptr; } size_t char_count = CountModifiedUtf8Chars(utf); return AllocFromModifiedUtf8(self, char_count, utf); @@ -153,8 +153,8 @@ String* String::AllocFromModifiedUtf8(Thread* self, const char* utf) { String* String::AllocFromModifiedUtf8(Thread* self, int32_t utf16_length, const char* utf8_data_in) { String* string = Alloc(self, GetJavaLangString(), utf16_length); - if (string == NULL) { - return NULL; + if (UNLIKELY(string == nullptr)) { + return nullptr; } uint16_t* utf16_data_out = const_cast<uint16_t*>(string->GetCharArray()->GetData()); @@ -164,22 +164,21 @@ String* String::AllocFromModifiedUtf8(Thread* self, int32_t utf16_length, } String* String::Alloc(Thread* self, Class* java_lang_String, int32_t utf16_length) { - SirtRef<CharArray> array(self, CharArray::Alloc(self, utf16_length)); - if (array.get() == NULL) { - return NULL; + CharArray* array = CharArray::Alloc(self, utf16_length); + if (UNLIKELY(array == nullptr)) { + return nullptr; } - return Alloc(self, java_lang_String, array.get()); + return Alloc(self, java_lang_String, array); } String* String::Alloc(Thread* self, Class* java_lang_String, CharArray* array) { // Hold reference in case AllocObject causes GC. SirtRef<CharArray> array_ref(self, array); String* string = down_cast<String*>(java_lang_String->AllocObject(self)); - if (string == NULL) { - return NULL; + if (LIKELY(string != nullptr)) { + string->SetArray(array_ref.get()); + string->SetCount(array_ref->GetLength()); } - string->SetArray(array); - string->SetCount(array->GetLength()); return string; } diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 01d8f31..7520c4d 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -117,10 +117,8 @@ class MANAGED String : public Object { private: void SetHashCode(int32_t new_hash_code) { - DCHECK_EQ(0u, - GetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), false)); - SetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), - new_hash_code, false); + DCHECK_EQ(0u, GetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), false)); + SetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_), new_hash_code, false); } void SetCount(int32_t new_count) { diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 2abfd3d..7fada9e 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -128,6 +128,10 @@ bool Monitor::Install(Thread* self) { LOG(FATAL) << "Inflating unlocked lock word"; break; } + default: { + LOG(FATAL) << "Invalid monitor state " << lw.GetState(); + return false; + } } LockWord fat(this); // Publish the updated lock word, which may race with other threads. @@ -140,8 +144,7 @@ bool Monitor::Install(Thread* self) { } Monitor::~Monitor() { - CHECK(obj_ != NULL); - CHECK_EQ(obj_->GetLockWord().GetState(), LockWord::kFatLocked); + // Deflated monitors have a null object. } /* @@ -559,6 +562,43 @@ void Monitor::NotifyAll(Thread* self) { } } +bool Monitor::Deflate(Thread* self, mirror::Object* obj) { + DCHECK(obj != nullptr); + LockWord lw(obj->GetLockWord()); + // If the lock isn't an inflated monitor, then we don't need to deflate anything. + if (lw.GetState() == LockWord::kFatLocked) { + Monitor* monitor = lw.FatLockMonitor(); + CHECK(monitor != nullptr); + MutexLock mu(self, monitor->monitor_lock_); + Thread* owner = monitor->owner_; + if (owner != nullptr) { + // Can't deflate if we are locked and have a hash code. + if (monitor->HasHashCode()) { + return false; + } + // Can't deflate if our lock count is too high. + if (monitor->lock_count_ > LockWord::kThinLockMaxCount) { + return false; + } + // Can't deflate if we have anybody waiting on the CV. + if (monitor->monitor_contenders_.GetNumWaiters() > 0) { + return false; + } + // Deflate to a thin lock. + obj->SetLockWord(LockWord::FromThinLockId(owner->GetTid(), monitor->lock_count_)); + } else if (monitor->HasHashCode()) { + obj->SetLockWord(LockWord::FromHashCode(monitor->GetHashCode())); + } else { + // No lock and no hash, just put an empty lock word inside the object. + obj->SetLockWord(LockWord()); + } + // The monitor is deflated, mark the object as nullptr so that we know to delete it during the + // next GC. + monitor->obj_ = nullptr; + } + return true; +} + /* * Changes the shape of a monitor from thin to fat, preserving the internal lock state. The calling * thread must own the lock or the owner must be suspended. There's a race with other threads @@ -577,13 +617,13 @@ void Monitor::Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t } } -void Monitor::InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word, +void Monitor::InflateThinLocked(Thread* self, SirtRef<mirror::Object>& obj, LockWord lock_word, uint32_t hash_code) { DCHECK_EQ(lock_word.GetState(), LockWord::kThinLocked); uint32_t owner_thread_id = lock_word.ThinLockOwner(); if (owner_thread_id == self->GetThreadId()) { // We own the monitor, we can easily inflate it. - Inflate(self, self, obj, hash_code); + Inflate(self, self, obj.get(), hash_code); } else { ThreadList* thread_list = Runtime::Current()->GetThreadList(); // Suspend the owner, inflate. First change to blocked and give up mutator_lock_. @@ -598,7 +638,7 @@ void Monitor::InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock if (lock_word.GetState() == LockWord::kThinLocked && lock_word.ThinLockOwner() == owner_thread_id) { // Go ahead and inflate the lock. - Inflate(self, owner, obj, hash_code); + Inflate(self, owner, obj.get(), hash_code); } thread_list->Resume(owner, false); } @@ -611,12 +651,13 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { DCHECK(obj != NULL); uint32_t thread_id = self->GetThreadId(); size_t contention_count = 0; + SirtRef<mirror::Object> sirt_obj(self, obj); while (true) { - LockWord lock_word = obj->GetLockWord(); + LockWord lock_word = sirt_obj->GetLockWord(); switch (lock_word.GetState()) { case LockWord::kUnlocked: { LockWord thin_locked(LockWord::FromThinLockId(thread_id, 0)); - if (obj->CasLockWord(lock_word, thin_locked)) { + if (sirt_obj->CasLockWord(lock_word, thin_locked)) { return; // Success! } continue; // Go again. @@ -628,11 +669,11 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { uint32_t new_count = lock_word.ThinLockCount() + 1; if (LIKELY(new_count <= LockWord::kThinLockMaxCount)) { LockWord thin_locked(LockWord::FromThinLockId(thread_id, new_count)); - obj->SetLockWord(thin_locked); + sirt_obj->SetLockWord(thin_locked); return; // Success! } else { // We'd overflow the recursion count, so inflate the monitor. - InflateThinLocked(self, obj, lock_word, 0); + InflateThinLocked(self, sirt_obj, lock_word, 0); } } else { // Contention. @@ -642,7 +683,7 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { NanoSleep(1000); // Sleep for 1us and re-attempt. } else { contention_count = 0; - InflateThinLocked(self, obj, lock_word, 0); + InflateThinLocked(self, sirt_obj, lock_word, 0); } } continue; // Start from the beginning. @@ -654,9 +695,13 @@ void Monitor::MonitorEnter(Thread* self, mirror::Object* obj) { } case LockWord::kHashCode: { // Inflate with the existing hashcode. - Inflate(self, nullptr, obj, lock_word.GetHashCode()); + Inflate(self, nullptr, sirt_obj.get(), lock_word.GetHashCode()); break; } + default: { + LOG(FATAL) << "Invalid monitor state " << lock_word.GetState(); + return; + } } } } @@ -666,11 +711,12 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { DCHECK(obj != NULL); LockWord lock_word = obj->GetLockWord(); + SirtRef<mirror::Object> sirt_obj(self, obj); switch (lock_word.GetState()) { case LockWord::kHashCode: // Fall-through. case LockWord::kUnlocked: - FailedUnlock(obj, self, NULL, NULL); + FailedUnlock(sirt_obj.get(), self, NULL, NULL); return false; // Failure. case LockWord::kThinLocked: { uint32_t thread_id = self->GetThreadId(); @@ -679,16 +725,16 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { // TODO: there's a race here with the owner dying while we unlock. Thread* owner = Runtime::Current()->GetThreadList()->FindThreadByThreadId(lock_word.ThinLockOwner()); - FailedUnlock(obj, self, owner, NULL); + FailedUnlock(sirt_obj.get(), self, owner, NULL); return false; // Failure. } else { // We own the lock, decrease the recursion count. if (lock_word.ThinLockCount() != 0) { uint32_t new_count = lock_word.ThinLockCount() - 1; LockWord thin_locked(LockWord::FromThinLockId(thread_id, new_count)); - obj->SetLockWord(thin_locked); + sirt_obj->SetLockWord(thin_locked); } else { - obj->SetLockWord(LockWord()); + sirt_obj->SetLockWord(LockWord()); } return true; // Success! } @@ -697,9 +743,10 @@ bool Monitor::MonitorExit(Thread* self, mirror::Object* obj) { Monitor* mon = lock_word.FatLockMonitor(); return mon->Unlock(self); } - default: - LOG(FATAL) << "Unreachable"; + default: { + LOG(FATAL) << "Invalid monitor state " << lock_word.GetState(); return false; + } } } @@ -733,6 +780,10 @@ void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns, } case LockWord::kFatLocked: break; // Already set for a wait. + default: { + LOG(FATAL) << "Invalid monitor state " << lock_word.GetState(); + return; + } } Monitor* mon = lock_word.FatLockMonitor(); mon->Wait(self, ms, ns, interruptShouldThrow, why); @@ -769,6 +820,10 @@ void Monitor::DoNotify(Thread* self, mirror::Object* obj, bool notify_all) { } return; // Success. } + default: { + LOG(FATAL) << "Invalid monitor state " << lock_word.GetState(); + return; + } } } @@ -787,9 +842,10 @@ uint32_t Monitor::GetLockOwnerThreadId(mirror::Object* obj) { Monitor* mon = lock_word.FatLockMonitor(); return mon->GetOwnerThreadId(); } - default: + default: { LOG(FATAL) << "Unreachable"; return ThreadList::kInvalidThreadId; + } } } @@ -1011,7 +1067,8 @@ void MonitorList::SweepMonitorList(RootVisitor visitor, void* arg) { for (auto it = list_.begin(); it != list_.end(); ) { Monitor* m = *it; mirror::Object* obj = m->GetObject(); - mirror::Object* new_obj = visitor(obj, arg); + // The object of a monitor can be null if we have deflated it. + mirror::Object* new_obj = obj != nullptr ? visitor(obj, arg) : nullptr; if (new_obj == nullptr) { VLOG(monitor) << "freeing monitor " << m << " belonging to unmarked object " << m->GetObject(); @@ -1031,6 +1088,8 @@ MonitorInfo::MonitorInfo(mirror::Object* obj) : owner_(NULL), entry_count_(0) { switch (lock_word.GetState()) { case LockWord::kUnlocked: // Fall-through. + case LockWord::kForwardingAddress: + // Fall-through. case LockWord::kHashCode: break; case LockWord::kThinLocked: diff --git a/runtime/monitor.h b/runtime/monitor.h index 09cfafa..d7de8a5 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -27,6 +27,7 @@ #include "atomic_integer.h" #include "base/mutex.h" #include "root_visitor.h" +#include "sirt_ref.h" #include "thread_state.h" namespace art { @@ -107,9 +108,12 @@ class Monitor { return hash_code_.load() != 0; } - static void InflateThinLocked(Thread* self, mirror::Object* obj, LockWord lock_word, + static void InflateThinLocked(Thread* self, SirtRef<mirror::Object>& obj, LockWord lock_word, uint32_t hash_code) NO_THREAD_SAFETY_ANALYSIS; + static bool Deflate(Thread* self, mirror::Object* obj) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: explicit Monitor(Thread* owner, mirror::Object* obj, int32_t hash_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index ab5eab3..c9e0e83 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -161,7 +161,7 @@ static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, j ScopedObjectAccess soa(env); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); class_linker->RegisterDexFile(*dex_file); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(javaLoader)); mirror::Class* result = class_linker->DefineClass(descriptor.c_str(), class_loader, *dex_file, *dex_class_def); VLOG(class_linker) << "DexFile_defineClassNative returning " << result; diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index aef000c..f0efdc2 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -53,18 +53,9 @@ static void VMRuntime_startJitCompilation(JNIEnv*, jobject) { static void VMRuntime_disableJitCompilation(JNIEnv*, jobject) { } -static jobject VMRuntime_newNonMovableArray(JNIEnv* env, - jobject, - jclass javaElementClass, +static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) { ScopedFastNativeObjectAccess soa(env); -#ifdef MOVING_GARBAGE_COLLECTOR - // TODO: right now, we don't have a copying collector, so there's no need - // to do anything special here, but we ought to pass the non-movability - // through to the allocator. - UNIMPLEMENTED(FATAL); -#endif - mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass); if (element_class == NULL) { ThrowNullPointerException(NULL, "element class == null"); @@ -74,13 +65,13 @@ static jobject VMRuntime_newNonMovableArray(JNIEnv* env, ThrowNegativeArraySizeException(length); return NULL; } - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); std::string descriptor; descriptor += "["; descriptor += ClassHelper(element_class).GetDescriptor(); - mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), NULL); - mirror::Array* result = mirror::Array::Alloc(soa.Self(), array_class, length); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr); + mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); + mirror::Array* result = mirror::Array::Alloc<false, true>(soa.Self(), array_class, length); return soa.AddLocalReference<jobject>(result); } @@ -94,7 +85,10 @@ static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) { ThrowIllegalArgumentException(NULL, "not an array"); return 0; } - // TODO: we should also check that this is a non-movable array. + if (Runtime::Current()->GetHeap()->IsMovableObject(array)) { + ThrowRuntimeException("Trying to get address of movable array object"); + return 0; + } return reinterpret_cast<uintptr_t>(array->GetRawData(array->GetClass()->GetComponentSize())); } @@ -172,28 +166,7 @@ static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) { } static void VMRuntime_trimHeap(JNIEnv*, jobject) { - uint64_t start_ns = NanoTime(); - - // Trim the managed heap. - gc::Heap* heap = Runtime::Current()->GetHeap(); - float managed_utilization = (static_cast<float>(heap->GetBytesAllocated()) / - heap->GetTotalMemory()); - size_t managed_reclaimed = heap->Trim(); - - uint64_t gc_heap_end_ns = NanoTime(); - - // Trim the native heap. - dlmalloc_trim(0); - size_t native_reclaimed = 0; - dlmalloc_inspect_all(DlmallocMadviseCallback, &native_reclaimed); - - uint64_t end_ns = NanoTime(); - - LOG(INFO) << "Heap trim of managed (duration=" << PrettyDuration(gc_heap_end_ns - start_ns) - << ", advised=" << PrettySize(managed_reclaimed) << ") and native (duration=" - << PrettyDuration(end_ns - gc_heap_end_ns) << ", advised=" << PrettySize(native_reclaimed) - << ") heaps. Managed heap utilization of " << static_cast<int>(100 * managed_utilization) - << "%."; + Runtime::Current()->GetHeap()->Trim(); } static void VMRuntime_concurrentGC(JNIEnv* env, jobject) { @@ -212,7 +185,7 @@ static mirror::Object* PreloadDexCachesStringsVisitor(mirror::Object* root, void } // Based on ClassLinker::ResolveString. -static void PreloadDexCachesResolveString(mirror::DexCache* dex_cache, +static void PreloadDexCachesResolveString(SirtRef<mirror::DexCache>& dex_cache, uint32_t string_idx, StringTable& strings) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -260,7 +233,7 @@ static void PreloadDexCachesResolveType(mirror::DexCache* dex_cache, uint32_t ty } // Based on ClassLinker::ResolveField. -static void PreloadDexCachesResolveField(mirror::DexCache* dex_cache, +static void PreloadDexCachesResolveField(SirtRef<mirror::DexCache>& dex_cache, uint32_t field_idx, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -275,9 +248,9 @@ static void PreloadDexCachesResolveField(mirror::DexCache* dex_cache, return; } if (is_static) { - field = klass->FindStaticField(dex_cache, field_idx); + field = klass->FindStaticField(dex_cache.get(), field_idx); } else { - field = klass->FindInstanceField(dex_cache, field_idx); + field = klass->FindInstanceField(dex_cache.get(), field_idx); } if (field == NULL) { return; @@ -287,7 +260,7 @@ static void PreloadDexCachesResolveField(mirror::DexCache* dex_cache, } // Based on ClassLinker::ResolveMethod. -static void PreloadDexCachesResolveMethod(mirror::DexCache* dex_cache, +static void PreloadDexCachesResolveMethod(SirtRef<mirror::DexCache>& dex_cache, uint32_t method_idx, InvokeType invoke_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -304,14 +277,14 @@ static void PreloadDexCachesResolveMethod(mirror::DexCache* dex_cache, switch (invoke_type) { case kDirect: case kStatic: - method = klass->FindDirectMethod(dex_cache, method_idx); + method = klass->FindDirectMethod(dex_cache.get(), method_idx); break; case kInterface: - method = klass->FindInterfaceMethod(dex_cache, method_idx); + method = klass->FindInterfaceMethod(dex_cache.get(), method_idx); break; case kSuper: case kVirtual: - method = klass->FindVirtualMethod(dex_cache, method_idx); + method = klass->FindVirtualMethod(dex_cache.get(), method_idx); break; default: LOG(FATAL) << "Unreachable - invocation type: " << invoke_type; @@ -430,6 +403,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { Runtime* runtime = Runtime::Current(); ClassLinker* linker = runtime->GetClassLinker(); + Thread* self = ThreadForEnv(env); // We use a std::map to avoid heap allocating StringObjects to lookup in gDvm.literalStrings StringTable strings; @@ -441,7 +415,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { for (size_t i = 0; i< boot_class_path.size(); i++) { const DexFile* dex_file = boot_class_path[i]; CHECK(dex_file != NULL); - mirror::DexCache* dex_cache = linker->FindDexCache(*dex_file); + SirtRef<mirror::DexCache> dex_cache(self, linker->FindDexCache(*dex_file)); if (kPreloadDexCachesStrings) { for (size_t i = 0; i < dex_cache->NumStrings(); i++) { @@ -451,7 +425,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { if (kPreloadDexCachesTypes) { for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) { - PreloadDexCachesResolveType(dex_cache, i); + PreloadDexCachesResolveType(dex_cache.get(), i); } } diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 3591611..3389107 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -61,7 +61,8 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean } std::string descriptor(DotToDescriptor(name.c_str())); - mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), + soa.Decode<mirror::ClassLoader*>(javaLoader)); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::Class* c = class_linker->FindClass(descriptor.c_str(), class_loader); if (c == NULL) { diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc index a2d6b18..808c917 100644 --- a/runtime/native/java_lang_reflect_Array.cc +++ b/runtime/native/java_lang_reflect_Array.cc @@ -52,13 +52,15 @@ static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementCl descriptor += ClassHelper(element_class).GetDescriptor(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), element_class->GetClassLoader()); + SirtRef<mirror::ClassLoader> class_loader(soa.Self(), element_class->GetClassLoader()); + mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); if (UNLIKELY(array_class == NULL)) { CHECK(soa.Self()->IsExceptionPending()); return NULL; } DCHECK(array_class->IsArrayClass()); - mirror::Array* new_array = mirror::Array::Alloc(soa.Self(), array_class, length); + mirror::Array* new_array = mirror::Array::Alloc<kMovingCollector, true>( + soa.Self(), array_class, length); return soa.AddLocalReference<jobject>(new_array); } diff --git a/runtime/native/java_lang_reflect_Proxy.cc b/runtime/native/java_lang_reflect_Proxy.cc index a92823a..809369a 100644 --- a/runtime/native/java_lang_reflect_Proxy.cc +++ b/runtime/native/java_lang_reflect_Proxy.cc @@ -23,20 +23,12 @@ namespace art { -static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring javaName, - jobjectArray javaInterfaces, jobject javaLoader, - jobjectArray javaMethods, jobjectArray javaThrows) { +static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring name, jobjectArray interfaces, + jobject loader, jobjectArray methods, jobjectArray throws) { ScopedObjectAccess soa(env); - mirror::String* name = soa.Decode<mirror::String*>(javaName); - mirror::ObjectArray<mirror::Class>* interfaces = - soa.Decode<mirror::ObjectArray<mirror::Class>*>(javaInterfaces); - mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(javaLoader); - mirror::ObjectArray<mirror::ArtMethod>* methods = - soa.Decode<mirror::ObjectArray<mirror::ArtMethod>*>(javaMethods); - mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >* throws = - soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class> >*>(javaThrows); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* result = class_linker->CreateProxyClass(name, interfaces, loader, methods, throws); + mirror::Class* result = class_linker->CreateProxyClass(soa, name, interfaces, loader, methods, + throws); return soa.AddLocalReference<jclass>(result); } diff --git a/runtime/native/scoped_fast_native_object_access.h b/runtime/native/scoped_fast_native_object_access.h index d941ec3..1658d96 100644 --- a/runtime/native/scoped_fast_native_object_access.h +++ b/runtime/native/scoped_fast_native_object_access.h @@ -63,10 +63,6 @@ class ScopedFastNativeObjectAccess { Locks::mutator_lock_->AssertSharedHeld(Self()); // Don't work with raw objects in non-runnable states. DCHECK_EQ(Self()->GetState(), kRunnable); -#ifdef MOVING_GARBAGE_COLLECTOR - // TODO: we should make these unique weak globals if Field instances can ever move. - UNIMPLEMENTED(WARNING); -#endif return reinterpret_cast<mirror::ArtField*>(fid); } @@ -83,6 +79,10 @@ class ScopedFastNativeObjectAccess { return NULL; } + if (kIsDebugBuild) { + Runtime::Current()->GetHeap()->VerifyObject(obj); + } + DCHECK_NE((reinterpret_cast<uintptr_t>(obj) & 0xffff0000), 0xebad0000); IndirectReferenceTable& locals = Env()->locals; diff --git a/runtime/object_utils.h b/runtime/object_utils.h index f724776..e37510c 100644 --- a/runtime/object_utils.h +++ b/runtime/object_utils.h @@ -67,12 +67,9 @@ class ObjectLock { class ClassHelper { public: - ClassHelper(const mirror::Class* c = NULL, ClassLinker* l = NULL) + explicit ClassHelper(const mirror::Class* c ) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : class_linker_(l), - dex_cache_(NULL), - dex_file_(NULL), - interface_type_list_(NULL), + : interface_type_list_(NULL), klass_(NULL) { if (c != NULL) { ChangeClass(c); @@ -82,13 +79,9 @@ class ClassHelper { void ChangeClass(const mirror::Class* new_c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { CHECK(new_c != NULL) << "klass_=" << klass_; // Log what we were changing from if any - CHECK(new_c->IsClass()) << "new_c=" << new_c; - if (dex_cache_ != NULL) { - mirror::DexCache* new_c_dex_cache = new_c->GetDexCache(); - if (new_c_dex_cache != dex_cache_) { - dex_cache_ = new_c_dex_cache; - dex_file_ = NULL; - } + if (!new_c->IsClass()) { + LOG(FATAL) << "new_c=" << new_c << " cc " << new_c->GetClass() << " ccc " + << ((new_c->GetClass() != nullptr) ? new_c->GetClass()->GetClass() : NULL); } klass_ = new_c; interface_type_list_ = NULL; @@ -201,20 +194,11 @@ class ClassHelper { } const DexFile& GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (dex_file_ == NULL) { - dex_file_ = GetDexCache()->GetDexFile(); - } - return *dex_file_; + return *GetDexCache()->GetDexFile(); } mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* result = dex_cache_; - if (result == NULL) { - DCHECK(klass_ != NULL); - result = klass_->GetDexCache(); - dex_cache_ = result; - } - return result; + return klass_->GetDexCache(); } private: @@ -231,18 +215,10 @@ class ClassHelper { return result; } - ClassLinker* GetClassLinker() { - ClassLinker* result = class_linker_; - if (result == NULL) { - result = Runtime::Current()->GetClassLinker(); - class_linker_ = result; - } - return result; + ClassLinker* GetClassLinker() ALWAYS_INLINE { + return Runtime::Current()->GetClassLinker(); } - ClassLinker* class_linker_; - mirror::DexCache* dex_cache_; - const DexFile* dex_file_; const DexFile::TypeList* interface_type_list_; const mirror::Class* klass_; std::string descriptor_; @@ -252,20 +228,11 @@ class ClassHelper { class FieldHelper { public: - FieldHelper() : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), field_(NULL) {} - explicit FieldHelper(const mirror::ArtField* f) : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), field_(f) {} - FieldHelper(const mirror::ArtField* f, ClassLinker* l) - : class_linker_(l), dex_cache_(NULL), dex_file_(NULL), field_(f) {} + FieldHelper() : field_(NULL) {} + explicit FieldHelper(const mirror::ArtField* f) : field_(f) {} void ChangeField(const mirror::ArtField* new_f) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(new_f != NULL); - if (dex_cache_ != NULL) { - mirror::DexCache* new_f_dex_cache = new_f->GetDeclaringClass()->GetDexCache(); - if (new_f_dex_cache != dex_cache_) { - dex_cache_ = new_f_dex_cache; - dex_file_ = NULL; - } - } field_ = new_f; } @@ -343,31 +310,14 @@ class FieldHelper { private: mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* result = dex_cache_; - if (result == NULL) { - result = field_->GetDeclaringClass()->GetDexCache(); - dex_cache_ = result; - } - return result; + return field_->GetDeclaringClass()->GetDexCache(); } - ClassLinker* GetClassLinker() { - ClassLinker* result = class_linker_; - if (result == NULL) { - result = Runtime::Current()->GetClassLinker(); - class_linker_ = result; - } - return result; + ClassLinker* GetClassLinker() ALWAYS_INLINE { + return Runtime::Current()->GetClassLinker(); } const DexFile& GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - if (dex_file_ == NULL) { - dex_file_ = GetDexCache()->GetDexFile(); - } - return *dex_file_; + return *GetDexCache()->GetDexFile(); } - - ClassLinker* class_linker_; - mirror::DexCache* dex_cache_; - const DexFile* dex_file_; const mirror::ArtField* field_; std::string declaring_class_descriptor_; @@ -377,38 +327,17 @@ class FieldHelper { class MethodHelper { public: MethodHelper() - : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), method_(NULL), shorty_(NULL), + : method_(NULL), shorty_(NULL), shorty_len_(0) {} explicit MethodHelper(const mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), method_(NULL), shorty_(NULL), - shorty_len_(0) { - SetMethod(m); - } - - MethodHelper(const mirror::ArtMethod* m, ClassLinker* l) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : class_linker_(l), dex_cache_(NULL), dex_file_(NULL), method_(NULL), shorty_(NULL), - shorty_len_(0) { + : method_(NULL), shorty_(NULL), shorty_len_(0) { SetMethod(m); } void ChangeMethod(mirror::ArtMethod* new_m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(new_m != NULL); - if (dex_cache_ != NULL) { - mirror::Class* klass = new_m->GetDeclaringClass(); - if (klass->IsProxyClass()) { - dex_cache_ = NULL; - dex_file_ = NULL; - } else { - mirror::DexCache* new_m_dex_cache = klass->GetDexCache(); - if (new_m_dex_cache != dex_cache_) { - dex_cache_ = new_m_dex_cache; - dex_file_ = NULL; - } - } - } SetMethod(new_m); shorty_ = NULL; } @@ -444,7 +373,8 @@ class MethodHelper { const DexFile& dex_file = GetDexFile(); uint32_t dex_method_idx = method_->GetDexMethodIndex(); const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx); - return GetClassLinker()->ResolveString(dex_file, method_id.name_idx_, GetDexCache()); + SirtRef<mirror::DexCache> dex_cache(Thread::Current(), GetDexCache()); + return GetClassLinker()->ResolveString(dex_file, method_id.name_idx_, dex_cache); } const char* GetShorty() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -622,28 +552,18 @@ class MethodHelper { } const DexFile& GetDexFile() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - const DexFile* result = dex_file_; - if (result == NULL) { - const mirror::DexCache* dex_cache = GetDexCache(); - result = dex_file_ = dex_cache->GetDexFile(); - } - return *result; + return *GetDexCache()->GetDexFile(); } mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::DexCache* result = dex_cache_; - if (result == NULL) { - mirror::Class* klass = method_->GetDeclaringClass(); - result = klass->GetDexCache(); - dex_cache_ = result; - } - return result; + return method_->GetDeclaringClass()->GetDexCache(); } mirror::String* ResolveString(uint32_t string_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { mirror::String* s = method_->GetDexCacheStrings()->Get(string_idx); if (UNLIKELY(s == NULL)) { - s = GetClassLinker()->ResolveString(GetDexFile(), string_idx, GetDexCache()); + SirtRef<mirror::DexCache> dex_cache(Thread::Current(), GetDexCache()); + s = GetClassLinker()->ResolveString(GetDexFile(), string_idx, dex_cache); } return s; } @@ -705,18 +625,10 @@ class MethodHelper { method_ = method; } - ClassLinker* GetClassLinker() { - ClassLinker* result = class_linker_; - if (result == NULL) { - result = Runtime::Current()->GetClassLinker(); - class_linker_ = result; - } - return result; + ClassLinker* GetClassLinker() ALWAYS_INLINE { + return Runtime::Current()->GetClassLinker(); } - ClassLinker* class_linker_; - mirror::DexCache* dex_cache_; - const DexFile* dex_file_; const mirror::ArtMethod* method_; const char* shorty_; uint32_t shorty_len_; diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc index e95fdb9..6f65bff 100644 --- a/runtime/reference_table.cc +++ b/runtime/reference_table.cc @@ -233,7 +233,7 @@ void ReferenceTable::Dump(std::ostream& os, const Table& entries) { void ReferenceTable::VisitRoots(RootVisitor* visitor, void* arg) { for (auto& ref : entries_) { - ref = visitor(const_cast<mirror::Object*>(ref), arg); + ref = visitor(ref, arg); } } diff --git a/runtime/root_visitor.h b/runtime/root_visitor.h index a2d898b..d52f351 100644 --- a/runtime/root_visitor.h +++ b/runtime/root_visitor.h @@ -23,11 +23,13 @@ class Object; } // namespace mirror class StackVisitor; +// Returns the new address of the object, returns root if it has not moved. typedef mirror::Object* (RootVisitor)(mirror::Object* root, void* arg) __attribute__((warn_unused_result)); typedef void (VerifyRootVisitor)(const mirror::Object* root, void* arg, size_t vreg, const StackVisitor* visitor); typedef bool (IsMarkedTester)(const mirror::Object* object, void* arg); +typedef void (ObjectVisitorCallback)(mirror::Object* obj, void* arg); } // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 53c9b2e..8e39023 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -121,7 +121,7 @@ Runtime::~Runtime() { Trace::Shutdown(); // Make sure to let the GC complete if it is running. - heap_->WaitForConcurrentGcToComplete(self); + heap_->WaitForGcToComplete(self); heap_->DeleteThreadPool(); // Make sure our internal threads are dead before we start tearing down things they're using. @@ -821,6 +821,11 @@ void Runtime::StartSignalCatcher() { } } +bool Runtime::IsShuttingDown(Thread* self) { + MutexLock mu(self, *Locks::runtime_shutdown_lock_); + return IsShuttingDownLocked(); +} + void Runtime::StartDaemonThreads() { VLOG(startup) << "Runtime::StartDaemonThreads entering"; @@ -861,7 +866,6 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { is_compiler_ = options->is_compiler_; is_zygote_ = options->is_zygote_; - is_concurrent_gc_enabled_ = options->is_concurrent_gc_enabled_; is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_; compiler_filter_ = options->compiler_filter_; @@ -926,12 +930,13 @@ bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) { GetHeap()->EnableObjectValidation(); CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U); - if (GetHeap()->GetContinuousSpaces()[0]->IsImageSpace()) { - class_linker_ = ClassLinker::CreateFromImage(intern_table_); + class_linker_ = new ClassLinker(intern_table_); + if (GetHeap()->HasImageSpace()) { + class_linker_->InitFromImage(); } else { CHECK(options->boot_class_path_ != NULL); CHECK_NE(options->boot_class_path_->size(), 0U); - class_linker_ = ClassLinker::CreateFromCompiler(*options->boot_class_path_, intern_table_); + class_linker_->InitFromCompiler(*options->boot_class_path_); } CHECK(class_linker_ != NULL); verifier::MethodVerifier::Init(); @@ -1174,16 +1179,20 @@ void Runtime::VisitNonThreadRoots(RootVisitor* visitor, void* arg) { visitor(pre_allocated_OutOfMemoryError_, arg)); DCHECK(pre_allocated_OutOfMemoryError_ != nullptr); } - resolution_method_ = reinterpret_cast<mirror::ArtMethod*>(visitor(resolution_method_, arg)); + resolution_method_ = down_cast<mirror::ArtMethod*>(visitor(resolution_method_, arg)); DCHECK(resolution_method_ != nullptr); - imt_conflict_method_ = reinterpret_cast<mirror::ArtMethod*>(visitor(imt_conflict_method_, arg)); - DCHECK(imt_conflict_method_ != nullptr); - default_imt_ = reinterpret_cast<mirror::ObjectArray<mirror::ArtMethod>*>(visitor(default_imt_, arg)); - DCHECK(default_imt_ != nullptr); + if (HasImtConflictMethod()) { + imt_conflict_method_ = down_cast<mirror::ArtMethod*>(visitor(imt_conflict_method_, arg)); + } + if (HasDefaultImt()) { + default_imt_ = down_cast<mirror::ObjectArray<mirror::ArtMethod>*>(visitor(default_imt_, arg)); + } + for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { - callee_save_methods_[i] = reinterpret_cast<mirror::ArtMethod*>( - visitor(callee_save_methods_[i], arg)); - DCHECK(callee_save_methods_[i] != nullptr); + if (callee_save_methods_[i] != nullptr) { + callee_save_methods_[i] = down_cast<mirror::ArtMethod*>( + visitor(callee_save_methods_[i], arg)); + } } } @@ -1201,49 +1210,45 @@ mirror::ObjectArray<mirror::ArtMethod>* Runtime::CreateDefaultImt(ClassLinker* c Thread* self = Thread::Current(); SirtRef<mirror::ObjectArray<mirror::ArtMethod> > imtable(self, cl->AllocArtMethodArray(self, 64)); mirror::ArtMethod* imt_conflict_method = Runtime::Current()->GetImtConflictMethod(); - for (size_t i = 0; i < 64; i++) { + for (size_t i = 0; i < static_cast<size_t>(imtable->GetLength()); i++) { imtable->Set(i, imt_conflict_method); } return imtable.get(); } mirror::ArtMethod* Runtime::CreateImtConflictMethod() { - mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod(); Thread* self = Thread::Current(); - SirtRef<mirror::ArtMethod> - method(self, down_cast<mirror::ArtMethod*>(method_class->AllocObject(self))); - method->SetDeclaringClass(method_class); + Runtime* r = Runtime::Current(); + ClassLinker* cl = r->GetClassLinker(); + SirtRef<mirror::ArtMethod> method(self, cl->AllocArtMethod(self)); + method->SetDeclaringClass(mirror::ArtMethod::GetJavaLangReflectArtMethod()); // TODO: use a special method for imt conflict method saves method->SetDexMethodIndex(DexFile::kDexNoIndex); // When compiling, the code pointer will get set later when the image is loaded. - Runtime* r = Runtime::Current(); - ClassLinker* cl = r->GetClassLinker(); method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetImtConflictTrampoline(cl)); return method.get(); } mirror::ArtMethod* Runtime::CreateResolutionMethod() { - mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod(); Thread* self = Thread::Current(); - SirtRef<mirror::ArtMethod> - method(self, down_cast<mirror::ArtMethod*>(method_class->AllocObject(self))); - method->SetDeclaringClass(method_class); + Runtime* r = Runtime::Current(); + ClassLinker* cl = r->GetClassLinker(); + SirtRef<mirror::ArtMethod> method(self, cl->AllocArtMethod(self)); + method->SetDeclaringClass(mirror::ArtMethod::GetJavaLangReflectArtMethod()); // TODO: use a special method for resolution method saves method->SetDexMethodIndex(DexFile::kDexNoIndex); // When compiling, the code pointer will get set later when the image is loaded. - Runtime* r = Runtime::Current(); - ClassLinker* cl = r->GetClassLinker(); method->SetEntryPointFromCompiledCode(r->IsCompiler() ? NULL : GetResolutionTrampoline(cl)); return method.get(); } mirror::ArtMethod* Runtime::CreateCalleeSaveMethod(InstructionSet instruction_set, - CalleeSaveType type) { - mirror::Class* method_class = mirror::ArtMethod::GetJavaLangReflectArtMethod(); + CalleeSaveType type) { Thread* self = Thread::Current(); - SirtRef<mirror::ArtMethod> - method(self, down_cast<mirror::ArtMethod*>(method_class->AllocObject(self))); - method->SetDeclaringClass(method_class); + Runtime* r = Runtime::Current(); + ClassLinker* cl = r->GetClassLinker(); + SirtRef<mirror::ArtMethod> method(self, cl->AllocArtMethod(self)); + method->SetDeclaringClass(mirror::ArtMethod::GetJavaLangReflectArtMethod()); // TODO: use a special method for callee saves method->SetDexMethodIndex(DexFile::kDexNoIndex); method->SetEntryPointFromCompiledCode(NULL); diff --git a/runtime/runtime.h b/runtime/runtime.h index 24b4c87..d025d47 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -149,10 +149,6 @@ class Runtime { return is_zygote_; } - bool IsConcurrentGcEnabled() const { - return is_concurrent_gc_enabled_; - } - bool IsExplicitGcDisabled() const { return is_explicit_gc_disabled_; } @@ -203,7 +199,8 @@ class Runtime { // Starts a runtime, which may cause threads to be started and code to run. bool Start() UNLOCK_FUNCTION(Locks::mutator_lock_); - bool IsShuttingDown() const EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_) { + bool IsShuttingDown(Thread* self); + bool IsShuttingDownLocked() const EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_) { return shutting_down_; } diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h index c39cdb2..1ca6c4e 100644 --- a/runtime/scoped_thread_state_change.h +++ b/runtime/scoped_thread_state_change.h @@ -34,9 +34,8 @@ class ScopedThreadStateChange { if (UNLIKELY(self_ == NULL)) { // Value chosen arbitrarily and won't be used in the destructor since thread_ == NULL. old_thread_state_ = kTerminated; - MutexLock mu(NULL, *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDown()); + CHECK(runtime == NULL || !runtime->IsStarted() || runtime->IsShuttingDown(self_)); } else { bool runnable_transition; DCHECK_EQ(self, Thread::Current()); @@ -63,9 +62,8 @@ class ScopedThreadStateChange { ~ScopedThreadStateChange() LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) ALWAYS_INLINE { if (UNLIKELY(self_ == NULL)) { if (!expected_has_no_thread_) { - MutexLock mu(NULL, *Locks::runtime_shutdown_lock_); Runtime* runtime = Runtime::Current(); - bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown(); + bool shutting_down = (runtime == NULL) || runtime->IsShuttingDown(nullptr); CHECK(shutting_down); } } else { @@ -167,6 +165,10 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { return NULL; } + if (kIsDebugBuild) { + Runtime::Current()->GetHeap()->VerifyObject(obj); + } + DCHECK_NE((reinterpret_cast<uintptr_t>(obj) & 0xffff0000), 0xebad0000); IndirectReferenceTable& locals = Env()->locals; @@ -185,7 +187,6 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { } } #endif - if (Vm()->work_around_app_jni_bugs) { // Hand out direct pointers to support broken old apps. return reinterpret_cast<T>(obj); @@ -206,10 +207,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. -#ifdef MOVING_GARBAGE_COLLECTOR - // TODO: we should make these unique weak globals if Field instances can ever move. - UNIMPLEMENTED(WARNING); -#endif + CHECK(!kMovingFields); return reinterpret_cast<mirror::ArtField*>(fid); } @@ -217,9 +215,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(WARNING); -#endif + CHECK(!kMovingFields); return reinterpret_cast<jfieldID>(field); } @@ -227,10 +223,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. -#ifdef MOVING_GARBAGE_COLLECTOR - // TODO: we should make these unique weak globals if Method instances can ever move. - UNIMPLEMENTED(WARNING); -#endif + CHECK(!kMovingMethods); return reinterpret_cast<mirror::ArtMethod*>(mid); } @@ -238,9 +231,7 @@ class ScopedObjectAccessUnchecked : public ScopedThreadStateChange { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Locks::mutator_lock_->AssertSharedHeld(Self()); DCHECK_EQ(thread_state_, kRunnable); // Don't work with raw objects in non-runnable states. -#ifdef MOVING_GARBAGE_COLLECTOR - UNIMPLEMENTED(WARNING); -#endif + CHECK(!kMovingMethods); return reinterpret_cast<jmethodID>(method); } diff --git a/runtime/sirt_ref.h b/runtime/sirt_ref.h index a1f8a66..56d81ec 100644 --- a/runtime/sirt_ref.h +++ b/runtime/sirt_ref.h @@ -17,6 +17,7 @@ #ifndef ART_RUNTIME_SIRT_REF_H_ #define ART_RUNTIME_SIRT_REF_H_ +#include "base/casts.h" #include "base/logging.h" #include "base/macros.h" #include "thread.h" diff --git a/runtime/stack.cc b/runtime/stack.cc index 5d3a9a5..a505383 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -22,12 +22,17 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "object_utils.h" +#include "runtime.h" #include "thread_list.h" #include "throw_location.h" #include "vmap_table.h" namespace art { +bool ShadowFrame::VerifyReference(const mirror::Object* val) const { + return !Runtime::Current()->GetHeap()->IsInTempSpace(val); +} + mirror::Object* ShadowFrame::GetThisObject() const { mirror::ArtMethod* m = GetMethod(); if (m->IsStatic()) { diff --git a/runtime/stack.h b/runtime/stack.h index a4b93bc..3d6b06a 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -150,10 +150,15 @@ class ShadowFrame { return *reinterpret_cast<unaligned_double*>(vreg); } + template <bool kChecked = false> mirror::Object* GetVRegReference(size_t i) const { DCHECK_LT(i, NumberOfVRegs()); if (HasReferenceArray()) { mirror::Object* ref = References()[i]; + if (kChecked) { + CHECK(VerifyReference(ref)) << "VReg " << i << "(" << ref + << ") is in protected space, reference array " << true; + } // If the vreg reference is not equal to the vreg then the vreg reference is stale. if (reinterpret_cast<uint32_t>(ref) != vregs_[i]) { return nullptr; @@ -161,7 +166,12 @@ class ShadowFrame { return ref; } else { const uint32_t* vreg = &vregs_[i]; - return *reinterpret_cast<mirror::Object* const*>(vreg); + mirror::Object* ref = *reinterpret_cast<mirror::Object* const*>(vreg); + if (kChecked) { + CHECK(VerifyReference(ref)) << "VReg " << i + << "(" << ref << ") is in protected space, reference array " << false; + } + return ref; } } @@ -174,12 +184,22 @@ class ShadowFrame { DCHECK_LT(i, NumberOfVRegs()); uint32_t* vreg = &vregs_[i]; *reinterpret_cast<int32_t*>(vreg) = val; + // This is needed for moving collectors since these can update the vreg references if they + // happen to agree with references in the reference array. + if (kMovingCollector && HasReferenceArray()) { + References()[i] = nullptr; + } } void SetVRegFloat(size_t i, float val) { DCHECK_LT(i, NumberOfVRegs()); uint32_t* vreg = &vregs_[i]; *reinterpret_cast<float*>(vreg) = val; + // This is needed for moving collectors since these can update the vreg references if they + // happen to agree with references in the reference array. + if (kMovingCollector && HasReferenceArray()) { + References()[i] = nullptr; + } } void SetVRegLong(size_t i, int64_t val) { @@ -188,6 +208,12 @@ class ShadowFrame { // Alignment attribute required for GCC 4.8 typedef int64_t unaligned_int64 __attribute__ ((aligned (4))); *reinterpret_cast<unaligned_int64*>(vreg) = val; + // This is needed for moving collectors since these can update the vreg references if they + // happen to agree with references in the reference array. + if (kMovingCollector && HasReferenceArray()) { + References()[i] = nullptr; + References()[i + 1] = nullptr; + } } void SetVRegDouble(size_t i, double val) { @@ -196,10 +222,18 @@ class ShadowFrame { // Alignment attribute required for GCC 4.8 typedef double unaligned_double __attribute__ ((aligned (4))); *reinterpret_cast<unaligned_double*>(vreg) = val; + // This is needed for moving collectors since these can update the vreg references if they + // happen to agree with references in the reference array. + if (kMovingCollector && HasReferenceArray()) { + References()[i] = nullptr; + References()[i + 1] = nullptr; + } } void SetVRegReference(size_t i, mirror::Object* val) { DCHECK_LT(i, NumberOfVRegs()); + DCHECK(!kMovingCollector || VerifyReference(val)) + << "VReg " << i << "(" << val << ") is in protected space"; uint32_t* vreg = &vregs_[i]; *reinterpret_cast<mirror::Object**>(vreg) = val; if (HasReferenceArray()) { @@ -280,6 +314,8 @@ class ShadowFrame { return reinterpret_cast<mirror::Object* const*>(vreg_end); } + bool VerifyReference(const mirror::Object* val) const; + mirror::Object** References() { return const_cast<mirror::Object**>(const_cast<const ShadowFrame*>(this)->References()); } diff --git a/runtime/thread.cc b/runtime/thread.cc index 9751076..1f6dd69 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -152,7 +152,7 @@ void* Thread::CreateCallback(void* arg) { MutexLock mu(NULL, *Locks::runtime_shutdown_lock_); // Check that if we got here we cannot be shutting down (as shutdown should never have started // while threads are being born). - CHECK(!runtime->IsShuttingDown()); + CHECK(!runtime->IsShuttingDownLocked()); self->Init(runtime->GetThreadList(), runtime->GetJavaVM()); Runtime::Current()->EndThreadBirth(); } @@ -241,7 +241,7 @@ void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_siz bool thread_start_during_shutdown = false; { MutexLock mu(self, *Locks::runtime_shutdown_lock_); - if (runtime->IsShuttingDown()) { + if (runtime->IsShuttingDownLocked()) { thread_start_during_shutdown = true; } else { runtime->StartThreadBirth(); @@ -328,7 +328,7 @@ Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_g } { MutexLock mu(NULL, *Locks::runtime_shutdown_lock_); - if (runtime->IsShuttingDown()) { + if (runtime->IsShuttingDownLocked()) { LOG(ERROR) << "Thread attaching while runtime is shutting down: " << thread_name; return NULL; } else { @@ -1352,13 +1352,12 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job *stack_depth = depth; } - MethodHelper mh; for (int32_t i = 0; i < depth; ++i) { mirror::ObjectArray<mirror::Object>* method_trace = soa.Decode<mirror::ObjectArray<mirror::Object>*>(internal); // Prepare parameters for StackTraceElement(String cls, String method, String file, int line) mirror::ArtMethod* method = down_cast<mirror::ArtMethod*>(method_trace->Get(i)); - mh.ChangeMethod(method); + MethodHelper mh(method); mirror::IntArray* pc_trace = down_cast<mirror::IntArray*>(method_trace->Get(depth)); uint32_t dex_pc = pc_trace->Get(i); int32_t line_number = mh.GetLineNumFromDexPC(dex_pc); @@ -1385,11 +1384,8 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job SirtRef<mirror::String> source_name_object(soa.Self(), mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); - mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc(soa.Self(), - class_name_object.get(), - method_name_object.get(), - source_name_object.get(), - line_number); + mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc( + soa.Self(), class_name_object, method_name_object, source_name_object, line_number); if (obj == NULL) { return NULL; } @@ -1437,8 +1433,10 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, if (throw_location.GetMethod() != NULL) { cl = throw_location.GetMethod()->GetDeclaringClass()->GetClassLoader(); } + SirtRef<mirror::ClassLoader> class_loader(this, cl); SirtRef<mirror::Class> - exception_class(this, runtime->GetClassLinker()->FindClass(exception_class_descriptor, cl)); + exception_class(this, runtime->GetClassLinker()->FindClass(exception_class_descriptor, + class_loader)); if (UNLIKELY(exception_class.get() == NULL)) { CHECK(IsExceptionPending()); LOG(ERROR) << "No exception class " << PrettyDescriptor(exception_class_descriptor); @@ -1453,6 +1451,12 @@ void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location, SirtRef<mirror::Throwable> exception(this, down_cast<mirror::Throwable*>(exception_class->AllocObject(this))); + // If we couldn't allocate the exception, throw the pre-allocated out of memory exception. + if (exception.get() == nullptr) { + SetException(throw_location, Runtime::Current()->GetPreAllocatedOutOfMemoryError()); + return; + } + // Choose an appropriate constructor and set up the arguments. const char* signature; SirtRef<mirror::String> msg_string(this, NULL); @@ -1741,18 +1745,21 @@ class CatchBlockStackVisitor : public StackVisitor { return true; // Continue stack walk. } - bool HandleDeoptimization(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + bool HandleDeoptimization(mirror::ArtMethod* m) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { MethodHelper mh(m); const DexFile::CodeItem* code_item = mh.GetCodeItem(); CHECK(code_item != NULL); - uint16_t num_regs = code_item->registers_size_; + uint16_t num_regs = code_item->registers_size_; uint32_t dex_pc = GetDexPc(); const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc); uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits(); ShadowFrame* new_frame = ShadowFrame::Create(num_regs, NULL, m, new_dex_pc); - verifier::MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - &mh.GetClassDef(), code_item, - m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, true); + SirtRef<mirror::DexCache> dex_cache(self_, mh.GetDexCache()); + SirtRef<mirror::ClassLoader> class_loader(self_, mh.GetClassLoader()); + verifier::MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, + &mh.GetClassDef(), code_item, m->GetDexMethodIndex(), m, + m->GetAccessFlags(), false, true); verifier.Verify(); std::vector<int32_t> kinds = verifier.DescribeVRegs(dex_pc); for (uint16_t reg = 0; reg < num_regs; reg++) { @@ -2088,6 +2095,13 @@ class VerifyCallbackVisitor { void* const arg_; }; +void Thread::SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) { + if (kIsDebugBuild) { + Runtime::Current()->GetHeap()->VerifyObject(class_loader_override); + } + class_loader_override_ = class_loader_override; +} + void Thread::VisitRoots(RootVisitor* visitor, void* arg) { if (opeer_ != nullptr) { opeer_ = visitor(opeer_, arg); @@ -2115,10 +2129,9 @@ void Thread::VisitRoots(RootVisitor* visitor, void* arg) { for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) { if (frame.this_object_ != nullptr) { frame.this_object_ = visitor(frame.this_object_, arg); - DCHECK(frame.this_object_ != nullptr); } - frame.method_ = reinterpret_cast<mirror::ArtMethod*>(visitor(frame.method_, arg)); DCHECK(frame.method_ != nullptr); + frame.method_ = reinterpret_cast<mirror::ArtMethod*>(visitor(frame.method_, arg)); } } diff --git a/runtime/thread.h b/runtime/thread.h index 3aa1373..6bd3607 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -177,34 +177,27 @@ class PACKED(4) Thread { ALWAYS_INLINE; // Once called thread suspension will cause an assertion failure. -#ifndef NDEBUG const char* StartAssertNoThreadSuspension(const char* cause) { - CHECK(cause != NULL); - const char* previous_cause = last_no_thread_suspension_cause_; - no_thread_suspension_++; - last_no_thread_suspension_cause_ = cause; - return previous_cause; - } -#else - const char* StartAssertNoThreadSuspension(const char* cause) { - CHECK(cause != NULL); - return NULL; + if (kIsDebugBuild) { + CHECK(cause != NULL); + const char* previous_cause = last_no_thread_suspension_cause_; + no_thread_suspension_++; + last_no_thread_suspension_cause_ = cause; + return previous_cause; + } else { + return nullptr; + } } -#endif // End region where no thread suspension is expected. -#ifndef NDEBUG void EndAssertNoThreadSuspension(const char* old_cause) { - CHECK(old_cause != NULL || no_thread_suspension_ == 1); - CHECK_GT(no_thread_suspension_, 0U); - no_thread_suspension_--; - last_no_thread_suspension_cause_ = old_cause; - } -#else - void EndAssertNoThreadSuspension(const char*) { + if (kIsDebugBuild) { + CHECK(old_cause != NULL || no_thread_suspension_ == 1); + CHECK_GT(no_thread_suspension_, 0U); + no_thread_suspension_--; + last_no_thread_suspension_cause_ = old_cause; + } } -#endif - void AssertThreadSuspensionIsAllowable(bool check_locks = true) const; @@ -370,9 +363,7 @@ class PACKED(4) Thread { return class_loader_override_; } - void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) { - class_loader_override_ = class_loader_override; - } + void SetClassLoaderOverride(mirror::ClassLoader* class_loader_override); // Create the internal representation of a stack trace, that is more time // and space efficient to compute than the StackTraceElement[] diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index ff1ed2a..dd3f11c 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -74,6 +74,15 @@ pid_t ThreadList::GetLockOwner() { return Locks::thread_list_lock_->GetExclusiveOwnerTid(); } +void ThreadList::DumpNativeStacks(std::ostream& os) { + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + for (const auto& thread : list_) { + os << "DUMPING THREAD " << thread->tid_ << "\n"; + DumpNativeStack(os, thread->tid_, "\t", true); + os << "\n"; + } +} + void ThreadList::DumpForSigQuit(std::ostream& os) { { MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); @@ -413,7 +422,7 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension, return thread; } if (total_delay_us >= kTimeoutUs) { - ThreadSuspendByPeerWarning(self, ERROR, "Thread suspension timed out", peer); + ThreadSuspendByPeerWarning(self, FATAL, "Thread suspension timed out", peer); if (did_suspend_request) { thread->ModifySuspendCount(soa.Self(), -1, debug_suspension); } @@ -477,7 +486,7 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspe return thread; } if (total_delay_us >= kTimeoutUs) { - ThreadSuspendByThreadIdWarning(ERROR, "Thread suspension timed out", thread_id); + ThreadSuspendByThreadIdWarning(WARNING, "Thread suspension timed out", thread_id); if (did_suspend_request) { thread->ModifySuspendCount(soa.Self(), -1, debug_suspension); } @@ -626,7 +635,7 @@ void ThreadList::WaitForOtherNonDaemonThreadsToExit() { { // No more threads can be born after we start to shutdown. MutexLock mu(self, *Locks::runtime_shutdown_lock_); - CHECK(Runtime::Current()->IsShuttingDown()); + CHECK(Runtime::Current()->IsShuttingDownLocked()); CHECK_EQ(Runtime::Current()->NumberOfThreadsBeingBorn(), 0U); } all_threads_are_daemons = true; diff --git a/runtime/thread_list.h b/runtime/thread_list.h index b1b3e88..45994ae 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -124,6 +124,9 @@ class ThreadList { return list_; } + void DumpNativeStacks(std::ostream& os) + LOCKS_EXCLUDED(Locks::thread_list_lock_); + private: uint32_t AllocThreadId(Thread* self); void ReleaseThreadId(Thread* self, uint32_t id) LOCKS_EXCLUDED(allocated_ids_lock_); diff --git a/runtime/utils.h b/runtime/utils.h index 6850e8b..4b39acd 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -122,7 +122,7 @@ struct TypeStaticIf<false, A, B> { // For rounding integers. template<typename T> static inline T RoundDown(T x, int n) { - CHECK(IsPowerOfTwo(n)); + DCHECK(IsPowerOfTwo(n)); return (x & -n); } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 9f98061..9cd8f73 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -39,6 +39,7 @@ #include "object_utils.h" #include "register_line-inl.h" #include "runtime.h" +#include "scoped_thread_state_change.h" #include "verifier/dex_gc_map.h" namespace art { @@ -113,17 +114,15 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const mirror::Class* kla *error += dex_file.GetLocation(); return kHardFailure; } - return VerifyClass(&dex_file, - kh.GetDexCache(), - klass->GetClassLoader(), - class_def, - allow_soft_failures, - error); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, kh.GetDexCache()); + SirtRef<mirror::ClassLoader> class_loader(self, klass->GetClassLoader()); + return VerifyClass(&dex_file, dex_cache, class_loader, class_def, allow_soft_failures, error); } MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader, const DexFile::ClassDef* class_def, bool allow_soft_failures, std::string* error) { @@ -233,8 +232,8 @@ MethodVerifier::FailureKind MethodVerifier::VerifyClass(const DexFile* dex_file, MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx, const DexFile* dex_file, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader, const DexFile::ClassDef* class_def, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, @@ -243,8 +242,8 @@ MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx, MethodVerifier::FailureKind result = kNoFailure; uint64_t start_ns = NanoTime(); - MethodVerifier verifier_(dex_file, dex_cache, class_loader, class_def, code_item, method_idx, - method, method_access_flags, true, allow_soft_failures); + MethodVerifier verifier_(dex_file, &dex_cache, &class_loader, class_def, code_item, + method_idx, method, method_access_flags, true, allow_soft_failures); if (verifier_.Verify()) { // Verification completed, however failures may be pending that didn't cause the verification // to hard fail. @@ -277,13 +276,14 @@ MethodVerifier::FailureKind MethodVerifier::VerifyMethod(uint32_t method_idx, } void MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_idx, - const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + const DexFile* dex_file, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader, const DexFile::ClassDef* class_def, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, uint32_t method_access_flags) { - MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def, code_item, + MethodVerifier verifier(dex_file, &dex_cache, &class_loader, class_def, code_item, dex_method_idx, method, method_access_flags, true, true); verifier.Verify(); verifier.DumpFailures(os); @@ -291,13 +291,12 @@ void MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_i verifier.Dump(os); } -MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, +MethodVerifier::MethodVerifier(const DexFile* dex_file, SirtRef<mirror::DexCache>* dex_cache, + SirtRef<mirror::ClassLoader>* class_loader, const DexFile::ClassDef* class_def, - const DexFile::CodeItem* code_item, - uint32_t dex_method_idx, mirror::ArtMethod* method, - uint32_t method_access_flags, bool can_load_classes, - bool allow_soft_failures) + const DexFile::CodeItem* code_item, uint32_t dex_method_idx, + mirror::ArtMethod* method, uint32_t method_access_flags, + bool can_load_classes, bool allow_soft_failures) : reg_types_(can_load_classes), work_insn_idx_(-1), dex_method_idx_(dex_method_idx), @@ -323,12 +322,19 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca DCHECK(class_def != nullptr); } +MethodVerifier::~MethodVerifier() { + STLDeleteElements(&failure_messages_); +} + void MethodVerifier::FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc, std::vector<uint32_t>& monitor_enter_dex_pcs) { MethodHelper mh(m); - MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - &mh.GetClassDef(), mh.GetCodeItem(), m->GetDexMethodIndex(), - m, m->GetAccessFlags(), false, true); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, mh.GetDexCache()); + SirtRef<mirror::ClassLoader> class_loader(self, mh.GetClassLoader()); + MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, &mh.GetClassDef(), + mh.GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, + true); verifier.interesting_dex_pc_ = dex_pc; verifier.monitor_enter_dex_pcs_ = &monitor_enter_dex_pcs; verifier.FindLocksAtDexPc(); @@ -348,9 +354,12 @@ void MethodVerifier::FindLocksAtDexPc() { mirror::ArtField* MethodVerifier::FindAccessedFieldAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc) { MethodHelper mh(m); - MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - &mh.GetClassDef(), mh.GetCodeItem(), m->GetDexMethodIndex(), - m, m->GetAccessFlags(), false, true); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, mh.GetDexCache()); + SirtRef<mirror::ClassLoader> class_loader(self, mh.GetClassLoader()); + MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, &mh.GetClassDef(), + mh.GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, + true); return verifier.FindAccessedFieldAtDexPc(dex_pc); } @@ -374,11 +383,14 @@ mirror::ArtField* MethodVerifier::FindAccessedFieldAtDexPc(uint32_t dex_pc) { } mirror::ArtMethod* MethodVerifier::FindInvokedMethodAtDexPc(mirror::ArtMethod* m, - uint32_t dex_pc) { + uint32_t dex_pc) { MethodHelper mh(m); - MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(), - &mh.GetClassDef(), mh.GetCodeItem(), m->GetDexMethodIndex(), - m, m->GetAccessFlags(), false, true); + Thread* self = Thread::Current(); + SirtRef<mirror::DexCache> dex_cache(self, mh.GetDexCache()); + SirtRef<mirror::ClassLoader> class_loader(self, mh.GetClassLoader()); + MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader, &mh.GetClassDef(), + mh.GetCodeItem(), m->GetDexMethodIndex(), m, m->GetAccessFlags(), false, + true); return verifier.FindInvokedMethodAtDexPc(dex_pc); } @@ -589,7 +601,7 @@ bool MethodVerifier::ScanTryCatchBlocks() { if (iterator.GetHandlerTypeIndex() != DexFile::kDexNoIndex16) { mirror::Class* exception_type = linker->ResolveType(*dex_file_, iterator.GetHandlerTypeIndex(), - dex_cache_, class_loader_); + *dex_cache_, *class_loader_); if (exception_type == NULL) { DCHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); @@ -1211,7 +1223,8 @@ bool MethodVerifier::SetTypesFromSignature() { // it's effectively considered initialized the instant we reach here (in the sense that we // can return without doing anything or call virtual methods). { - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, + false); reg_line->SetRegisterType(arg_start + cur_arg, reg_type); } break; @@ -1853,7 +1866,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with array type " << array_type; } else { - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type, + class_loader_->get()); DCHECK(!component_type.IsConflict()); if (component_type.IsNonZeroReferenceTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type " @@ -2168,7 +2182,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; const char* descriptor = dex_file_->StringByTypeIdx(return_type_idx); - return_type = ®_types_.FromDescriptor(class_loader_, descriptor, false); + return_type = ®_types_.FromDescriptor(class_loader_->get(), descriptor, false); } if (!return_type->IsLowHalf()) { work_line_->SetResultRegisterType(*return_type); @@ -2235,8 +2249,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { */ work_line_->MarkRefsAsInitialized(this_type); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor, - false); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_->get(), + return_type_descriptor, false); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(return_type); } else { @@ -2257,11 +2271,12 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); uint32_t return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - descriptor = dex_file_->StringByTypeIdx(return_type_idx); + descriptor = dex_file_->StringByTypeIdx(return_type_idx); } else { descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, + false); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(return_type); } else { @@ -2318,7 +2333,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor(); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, + false); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(return_type); } else { @@ -2584,7 +2600,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { mirror::ArtMethod* called_method = VerifyInvokeVirtualQuickArgs(inst, is_range); if (called_method != NULL) { const char* descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, + false); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(return_type); } else { @@ -2850,18 +2867,18 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) { const char* descriptor = dex_file_->StringByTypeIdx(class_idx); const RegType& referrer = GetDeclaringClass(); - mirror::Class* klass = dex_cache_->GetResolvedType(class_idx); + mirror::Class* klass = (*dex_cache_)->GetResolvedType(class_idx); const RegType& result = klass != NULL ? reg_types_.FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()) - : reg_types_.FromDescriptor(class_loader_, descriptor, false); + : reg_types_.FromDescriptor(class_loader_->get(), descriptor, false); if (result.IsConflict()) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor << "' in " << referrer; return result; } if (klass == NULL && !result.IsUnresolvedTypes()) { - dex_cache_->SetResolvedType(class_idx, result.GetClass()); + (*dex_cache_)->SetResolvedType(class_idx, result.GetClass()); } // Check if access is allowed. Unresolved types use xxxWithAccessCheck to // check at runtime if access is allowed and so pass here. If result is @@ -2935,7 +2952,7 @@ mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_meth } mirror::Class* klass = klass_type.GetClass(); const RegType& referrer = GetDeclaringClass(); - mirror::ArtMethod* res_method = dex_cache_->GetResolvedMethod(dex_method_idx); + mirror::ArtMethod* res_method = (*dex_cache_)->GetResolvedMethod(dex_method_idx); if (res_method == NULL) { const char* name = dex_file_->GetMethodName(method_id); const Signature signature = dex_file_->GetMethodSignature(method_id); @@ -2948,7 +2965,7 @@ mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_meth res_method = klass->FindVirtualMethod(name, signature); } if (res_method != NULL) { - dex_cache_->SetResolvedMethod(dex_method_idx, res_method); + (*dex_cache_)->SetResolvedMethod(dex_method_idx, res_method); } else { // If a virtual or interface method wasn't found with the expected type, look in // the direct methods. This can happen when the wrong invoke type is used or when @@ -3112,7 +3129,7 @@ mirror::ArtMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst, << " missing signature component"; return NULL; } - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, false); uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args]; if (reg_type.IsIntegralTypes()) { const RegType& src_type = work_line_->GetRegisterType(get_reg); @@ -3136,8 +3153,7 @@ mirror::ArtMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst, } mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst, - RegisterLine* reg_line, - bool is_range) { + RegisterLine* reg_line, bool is_range) { DCHECK(inst->Opcode() == Instruction::INVOKE_VIRTUAL_QUICK || inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE_QUICK); const RegType& actual_arg_type = reg_line->GetInvocationThis(inst, is_range); @@ -3152,11 +3168,13 @@ mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst } else { const std::string& descriptor(actual_arg_type.GetDescriptor()); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - this_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + this_class = class_linker->FindClass(descriptor.c_str(), *class_loader_); if (this_class == NULL) { - Thread::Current()->ClearException(); + Thread* self = Thread::Current(); + self->ClearException(); // Look for a system class - this_class = class_linker->FindClass(descriptor.c_str(), NULL); + SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr); + this_class = class_linker->FindClass(descriptor.c_str(), null_class_loader); } } if (this_class == NULL) { @@ -3246,7 +3264,7 @@ mirror::ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instructio << " missing signature component"; return NULL; } - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_->get(), descriptor, false); uint32_t get_reg = is_range ? inst->VRegC_3rc() + actual_args : arg[actual_args]; if (!work_line_->VerifyRegisterType(get_reg, reg_type)) { return res_method; @@ -3290,7 +3308,7 @@ void MethodVerifier::VerifyNewArray(const Instruction* inst, bool is_filled, boo } else { // Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of // the list and fail. It's legal, if silly, for arg_count to be zero. - const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_); + const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_->get()); uint32_t arg_count = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); uint32_t arg[5]; if (!is_range) { @@ -3332,7 +3350,7 @@ void MethodVerifier::VerifyAGet(const Instruction* inst, Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget"; } else { /* verify the class */ - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_->get()); if (!component_type.IsReferenceTypes() && !is_primitive) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type << " source for aget-object"; @@ -3409,7 +3427,7 @@ void MethodVerifier::VerifyAPut(const Instruction* inst, } else if (!array_type.IsArrayTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput"; } else { - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_->get()); const uint32_t vregA = inst->VRegA_23x(); if (is_primitive) { VerifyPrimitivePut(component_type, insn_type, vregA); @@ -3441,10 +3459,9 @@ mirror::ArtField* MethodVerifier::GetStaticField(int field_idx) { if (klass_type.IsUnresolvedTypes()) { return NULL; // Can't resolve Class so no more to do here, will do checking at runtime. } - mirror::ArtField* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, - field_idx, - dex_cache_, - class_loader_); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, *dex_cache_, + *class_loader_); if (field == NULL) { VLOG(verifier) << "Unable to resolve static field " << field_idx << " (" << dex_file_->GetFieldName(field_id) << ") in " @@ -3460,9 +3477,8 @@ mirror::ArtField* MethodVerifier::GetStaticField(int field_idx) { } else if (!field->IsStatic()) { Fail(VERIFY_ERROR_CLASS_CHANGE) << "expected field " << PrettyField(field) << " to be static"; return NULL; - } else { - return field; } + return field; } mirror::ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_idx) { @@ -3478,10 +3494,9 @@ mirror::ArtField* MethodVerifier::GetInstanceField(const RegType& obj_type, int if (klass_type.IsUnresolvedTypes()) { return NULL; // Can't resolve Class so no more to do here } - mirror::ArtField* field = Runtime::Current()->GetClassLinker()->ResolveFieldJLS(*dex_file_, - field_idx, - dex_cache_, - class_loader_); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + mirror::ArtField* field = class_linker->ResolveFieldJLS(*dex_file_, field_idx, *dex_cache_, + *class_loader_); if (field == NULL) { VLOG(verifier) << "Unable to resolve instance field " << field_idx << " (" << dex_file_->GetFieldName(field_id) << ") in " @@ -3550,8 +3565,7 @@ void MethodVerifier::VerifyISGet(const Instruction* inst, const RegType& insn_ty if (field_type == nullptr) { const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - mirror::ClassLoader* loader = class_loader_; - field_type = ®_types_.FromDescriptor(loader, descriptor, false); + field_type = ®_types_.FromDescriptor(class_loader_->get(), descriptor, false); } const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); if (is_primitive) { @@ -3613,8 +3627,7 @@ void MethodVerifier::VerifyISPut(const Instruction* inst, const RegType& insn_ty if (field_type == nullptr) { const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - mirror::ClassLoader* loader = class_loader_; - field_type = ®_types_.FromDescriptor(loader, descriptor, false); + field_type = ®_types_.FromDescriptor(class_loader_->get(), descriptor, false); } const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); if (is_primitive) { @@ -3671,11 +3684,13 @@ mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, // We need to resolve the class from its descriptor. const std::string& descriptor(object_type.GetDescriptor()); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - object_class = class_linker->FindClass(descriptor.c_str(), class_loader_); + Thread* self = Thread::Current(); + object_class = class_linker->FindClass(descriptor.c_str(), *class_loader_); if (object_class == NULL) { - Thread::Current()->ClearException(); + self->ClearException(); // Look for a system class - object_class = class_linker->FindClass(descriptor.c_str(), NULL); + SirtRef<mirror::ClassLoader> null_class_loader(self, nullptr); + object_class = class_linker->FindClass(descriptor.c_str(), null_class_loader); } } if (object_class == NULL) { @@ -3881,8 +3896,8 @@ const RegType& MethodVerifier::GetMethodReturnType() { MethodHelper mh(mirror_method_); mirror::Class* return_type_class = mh.GetReturnType(); if (return_type_class != nullptr) { - return_type_ =®_types_.FromClass(mh.GetReturnTypeDescriptor(), return_type_class, - return_type_class->CannotBeAssignedFromOtherTypes()); + return_type_ = ®_types_.FromClass(mh.GetReturnTypeDescriptor(), return_type_class, + return_type_class->CannotBeAssignedFromOtherTypes()); } else { Thread* self = Thread::Current(); DCHECK(self->IsExceptionPending()); @@ -3894,7 +3909,7 @@ const RegType& MethodVerifier::GetMethodReturnType() { const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id); uint16_t return_type_idx = proto_id.return_type_idx_; const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx)); - return_type_ = ®_types_.FromDescriptor(class_loader_, descriptor, false); + return_type_ = ®_types_.FromDescriptor(class_loader_->get(), descriptor, false); } } return *return_type_; @@ -3910,7 +3925,7 @@ const RegType& MethodVerifier::GetDeclaringClass() { declaring_class_ = ®_types_.FromClass(descriptor, klass, klass->CannotBeAssignedFromOtherTypes()); } else { - declaring_class_ = ®_types_.FromDescriptor(class_loader_, descriptor, false); + declaring_class_ = ®_types_.FromDescriptor(class_loader_->get(), descriptor, false); } } return *declaring_class_; @@ -3969,7 +3984,8 @@ MethodVerifier::MethodSafeCastSet* MethodVerifier::GenerateSafeCastSet() { // String[] in which case the stores need to be of Strings. if (array_type.IsPreciseReference()) { const RegType& value_type(line->GetRegisterType(inst->VRegA_23x())); - const RegType& component_type(reg_types_.GetComponentType(array_type, class_loader_)); + const RegType& component_type(reg_types_.GetComponentType(array_type, + class_loader_->get())); is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type); } } @@ -4026,8 +4042,8 @@ MethodVerifier::PcToConcreteMethodMap* MethodVerifier::GenerateDevirtMap() { // We can't devirtualize abstract classes except on arrays of abstract classes. continue; } - mirror::ArtMethod* abstract_method = - dex_cache_->GetResolvedMethod(is_range ? inst->VRegB_3rc() : inst->VRegB_35c()); + mirror::ArtMethod* abstract_method = (*dex_cache_)->GetResolvedMethod( + is_range ? inst->VRegB_3rc() : inst->VRegB_35c()); if (abstract_method == NULL) { // If the method is not found in the cache this means that it was never found // by ResolveMethodAndCheckAccess() called when verifying invoke_*. diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 57fde1d..8a663f9 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -33,6 +33,7 @@ #include "reg_type_cache-inl.h" #include "register_line.h" #include "safe_map.h" +#include "sirt_ref.h" #include "UniquePtr.h" namespace art { @@ -142,14 +143,15 @@ class MethodVerifier { static FailureKind VerifyClass(const mirror::Class* klass, bool allow_soft_failures, std::string* error) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static FailureKind VerifyClass(const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + static FailureKind VerifyClass(const DexFile* dex_file, SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader, const DexFile::ClassDef* class_def, bool allow_soft_failures, std::string* error) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void VerifyMethodAndDump(std::ostream& os, uint32_t method_idx, const DexFile* dex_file, - mirror::DexCache* dex_cache, mirror::ClassLoader* class_loader, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader, const DexFile::ClassDef* class_def, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, uint32_t method_access_flags) @@ -217,16 +219,13 @@ class MethodVerifier { return can_load_classes_; } - MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, const DexFile::ClassDef* class_def, - const DexFile::CodeItem* code_item, - uint32_t method_idx, mirror::ArtMethod* method, + MethodVerifier(const DexFile* dex_file, SirtRef<mirror::DexCache>* dex_cache, + SirtRef<mirror::ClassLoader>* class_loader, const DexFile::ClassDef* class_def, + const DexFile::CodeItem* code_item, uint32_t method_idx, mirror::ArtMethod* method, uint32_t access_flags, bool can_load_classes, bool allow_soft_failures) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - ~MethodVerifier() { - STLDeleteElements(&failure_messages_); - } + ~MethodVerifier(); // Run verification on the method. Returns true if verification completes and false if the input // has an irrecoverable corruption. @@ -257,8 +256,8 @@ class MethodVerifier { * for code flow problems. */ static FailureKind VerifyMethod(uint32_t method_idx, const DexFile* dex_file, - mirror::DexCache* dex_cache, - mirror::ClassLoader* class_loader, + SirtRef<mirror::DexCache>& dex_cache, + SirtRef<mirror::ClassLoader>& class_loader, const DexFile::ClassDef* class_def_idx, const DexFile::CodeItem* code_item, mirror::ArtMethod* method, uint32_t method_access_flags, @@ -685,9 +684,9 @@ class MethodVerifier { const RegType* return_type_; // Lazily computed return type of the method. const DexFile* const dex_file_; // The dex file containing the method. // The dex_cache for the declaring class of the method. - mirror::DexCache* dex_cache_ GUARDED_BY(Locks::mutator_lock_); + SirtRef<mirror::DexCache>* dex_cache_ GUARDED_BY(Locks::mutator_lock_); // The class loader for the declaring class of the method. - mirror::ClassLoader* class_loader_ GUARDED_BY(Locks::mutator_lock_); + SirtRef<mirror::ClassLoader>* class_loader_ GUARDED_BY(Locks::mutator_lock_); const DexFile::ClassDef* const class_def_; // The class def of the declaring class of the method. const DexFile::CodeItem* const code_item_; // The code item containing the code for the method. const RegType* declaring_class_; // Lazily computed reg type of the method's declaring class. diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 50d1583..d82e75d 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -928,7 +928,8 @@ mirror::Class* RegType::ClassJoin(mirror::Class* s, mirror::Class* t) { } mirror::Class* common_elem = ClassJoin(s_ct, t_ct); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - mirror::ClassLoader* class_loader = s->GetClassLoader(); + Thread* self = Thread::Current(); + SirtRef<mirror::ClassLoader> class_loader(self, s->GetClassLoader()); std::string descriptor("["); descriptor += ClassHelper(common_elem).GetDescriptor(); mirror::Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader); diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index 446dd00..a62e835 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -140,9 +140,10 @@ mirror::Class* RegTypeCache::ResolveClass(const char* descriptor, mirror::ClassL // Class was not found, must create new type. // Try resolving class ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + SirtRef<mirror::ClassLoader> class_loader(Thread::Current(), loader); mirror::Class* klass = NULL; if (can_load_classes_) { - klass = class_linker->FindClass(descriptor, loader); + klass = class_linker->FindClass(descriptor, class_loader); } else { klass = class_linker->LookupClass(descriptor, loader); if (klass != NULL && !klass->IsLoaded()) { |