blob: 649124e700d384e0800cf8a687dd99e381db961c [file]
/*
* Copyright (C) 2023 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 "startup_completed_task.h"
#include "base/systrace.h"
#include "class_linker.h"
#include "gc/heap.h"
#include "gc/scoped_gc_critical_section.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
#include "handle_scope-inl.h"
#include "linear_alloc-inl.h"
#include "mirror/dex_cache.h"
#include "mirror/object-inl.h"
#include "obj_ptr.h"
#include "runtime_image.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "thread_list.h"
namespace art {
class CollectStartupDexCacheVisitor : public DexCacheVisitor {
public:
explicit CollectStartupDexCacheVisitor(VariableSizedHandleScope& handles) : handles_(handles) {}
void Visit(ObjPtr<mirror::DexCache> dex_cache)
REQUIRES_SHARED(Locks::dex_lock_, Locks::mutator_lock_) override {
handles_.NewHandle(dex_cache);
}
private:
VariableSizedHandleScope& handles_;
};
class UnlinkVisitor {
public:
UnlinkVisitor() {}
void VisitRootIfNonNull(StackReference<mirror::Object>* ref)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (!ref->IsNull()) {
ref->AsMirrorPtr()->AsDexCache()->UnlinkStartupCaches();
}
}
};
void StartupCompletedTask::Run(Thread* self) {
VLOG(startup) << "StartupCompletedTask running";
Runtime* const runtime = Runtime::Current();
if (!runtime->NotifyStartupCompleted()) {
return;
}
// Maybe generate a runtime app image. If the runtime is debuggable, boot
// classpath classes can be dynamically changed, so don't bother generating an
// image.
if (!runtime->IsJavaDebuggable()) {
std::string compiler_filter;
std::string compilation_reason;
runtime->GetAppInfo()->GetPrimaryApkOptimizationStatus(&compiler_filter, &compilation_reason);
CompilerFilter::Filter filter;
if (CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter) &&
!CompilerFilter::IsAotCompilationEnabled(filter)) {
std::string error_msg;
if (!RuntimeImage::WriteImageToDisk(&error_msg)) {
LOG(DEBUG) << "Could not write temporary image to disk " << error_msg;
}
}
}
// Fetch the startup linear alloc before the checkpoint to play nice with
// 1002-notify-startup test which resets the startup state.
std::unique_ptr<LinearAlloc> startup_linear_alloc(runtime->ReleaseStartupLinearAlloc());
{
ScopedTrace trace("Releasing dex caches and app image spaces metadata");
ScopedObjectAccess soa(Thread::Current());
// Collect dex caches that were allocated with the startup linear alloc.
VariableSizedHandleScope handles(soa.Self());
{
CollectStartupDexCacheVisitor visitor(handles);
ReaderMutexLock mu(self, *Locks::dex_lock_);
runtime->GetClassLinker()->VisitDexCaches(&visitor);
}
// Request empty checkpoints to make sure no threads are:
// - accessing the image space metadata section when we madvise it
// - accessing dex caches when we free them
//
// Use GC exclusion to prevent deadlocks that may happen if
// multiple threads are attempting to run empty checkpoints at the same time.
{
// Avoid using ScopedGCCriticalSection since that does not allow thread suspension. This is
// not allowed to prevent allocations, but it's still safe to suspend temporarily for the
// checkpoint.
gc::ScopedInterruptibleGCCriticalSection sigcs(self,
gc::kGcCauseRunEmptyCheckpoint,
gc::kCollectorTypeCriticalSection);
// Do the unlinking of dex cache arrays in the GC critical section to
// avoid GC not seeing these arrays. We do it before the checkpoint so
// we know after the checkpoint, no thread is holding onto the array.
UnlinkVisitor visitor;
handles.VisitRoots(visitor);
runtime->GetThreadList()->RunEmptyCheckpoint();
}
for (gc::space::ContinuousSpace* space : runtime->GetHeap()->GetContinuousSpaces()) {
if (space->IsImageSpace()) {
gc::space::ImageSpace* image_space = space->AsImageSpace();
if (image_space->GetImageHeader().IsAppImage()) {
image_space->ReleaseMetadata();
}
}
}
}
{
// Delete the thread pool used for app image loading since startup is assumed to be completed.
ScopedTrace trace2("Delete thread pool");
runtime->DeleteThreadPool();
}
if (startup_linear_alloc != nullptr) {
// We know that after the checkpoint, there is no thread that can hold
// the startup linear alloc, so it's safe to delete it now.
ScopedTrace trace2("Delete startup linear alloc");
ArenaPool* arena_pool = startup_linear_alloc->GetArenaPool();
startup_linear_alloc.reset();
arena_pool->TrimMaps();
}
}
} // namespace art