diff a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -27,26 +27,32 @@
 #include "cds/aotLinkedClassBulkLoader.hpp"
 #include "cds/cds_globals.hpp"
 #include "cds/cdsConfig.hpp"
 #include "cds/heapShared.hpp"
 #include "cds/metaspaceShared.hpp"
+#include "cds/methodProfiler.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/javaThreadStatus.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
 #include "classfile/vmClasses.hpp"
 #include "classfile/vmSymbols.hpp"
+#include "code/SCCache.hpp"
+#include "compiler/compilationPolicy.hpp"
 #include "compiler/compileBroker.hpp"
 #include "compiler/compileTask.hpp"
 #include "compiler/compilerThread.hpp"
+#include "compiler/precompiler.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/barrierSetNMethod.hpp"
 #include "gc/shared/gcVMOperations.hpp"
 #include "gc/shared/oopStorage.hpp"
 #include "gc/shared/oopStorageSet.hpp"
 #include "gc/shared/stringdedup/stringDedup.hpp"
+#include "interpreter/interpreterRuntime.hpp"
 #include "jfr/jfrEvents.hpp"
 #include "jvm.h"
 #include "jvmtifiles/jvmtiEnv.hpp"
 #include "logging/log.hpp"
 #include "logging/logAsyncWriter.hpp"
@@ -108,12 +114,16 @@
 #include "utilities/vmError.hpp"
 #if INCLUDE_JVMCI
 #include "jvmci/jvmci.hpp"
 #include "jvmci/jvmciEnv.hpp"
 #endif
+#ifdef COMPILER1
+#include "c1/c1_Runtime1.hpp"
+#endif
 #ifdef COMPILER2
 #include "opto/idealGraphPrinter.hpp"
+#include "opto/runtime.hpp"
 #endif
 #if INCLUDE_JFR
 #include "jfr/jfr.hpp"
 #endif
 
@@ -324,10 +334,34 @@
   if (result.get_jint() != JNI_OK) {
     vm_exit_during_initialization(); // no message or exception
   }
 
   universe_post_module_init();
+
+#if 0
+  if (CDSConfig::is_using_aot_linked_classes()) {
+    AOTLinkedClassBulkLoader::load_non_javabase_boot_classes(THREAD); 
+    if (CDSConfig::is_using_full_module_graph()) {
+      assert(SystemDictionary::java_platform_loader() != nullptr, "must be");
+      assert(SystemDictionary::java_system_loader() != nullptr,   "must be");
+      AOTLinkedClassBulkLoader::load_platform_classes(THREAD);
+      AOTLinkedClassBulkLoader::load_app_classes(THREAD);
+    } else {
+      // Special case -- we assume that the final archive has the same module graph
+      // as the training run.
+      // AOTLinkedClassBulkLoader will be called for the platform/system loaders
+      // inside SystemDictionary::compute_java_loaders().
+      assert(CDSConfig::is_dumping_final_static_archive(), "must be");
+      assert(SystemDictionary::java_platform_loader() == nullptr, "must be");
+      assert(SystemDictionary::java_system_loader() == nullptr,   "must be");
+    }
+  }
+
+#ifndef PRODUCT
+  HeapShared::initialize_test_class_from_archive(THREAD);
+#endif
+#endif
 }
 
 // Phase 3. final setup - set security manager, system class loader and TCCL
 //
 //     This will instantiate and set the security manager, set the system class
@@ -410,10 +444,42 @@
   initialize_class(vmSymbols::java_lang_IllegalMonitorStateException(), CHECK);
   initialize_class(vmSymbols::java_lang_IllegalArgumentException(), CHECK);
   initialize_class(vmSymbols::java_lang_InternalError(), CHECK);
 }
 
+bool Threads::initialize_compilation(TRAPS) {
+  // initialize compiler(s)
+  bool force_JVMCI_initialization = false;
+
+#if defined(COMPILER1) || COMPILER2_OR_JVMCI
+  bool init_compilation = true;
+#if INCLUDE_JVMCI
+  if (EnableJVMCI) {
+    // Initialize JVMCI eagerly when it is explicitly requested.
+    // Or when JVMCILibDumpJNIConfig or JVMCIPrintProperties is enabled.
+    force_JVMCI_initialization = EagerJVMCI || JVMCIPrintProperties || JVMCILibDumpJNIConfig;
+    if (!force_JVMCI_initialization && UseJVMCICompiler && !UseJVMCINativeLibrary && (!UseInterpreter || !BackgroundCompilation)) {
+      // Force initialization of jarjvmci otherwise requests for blocking
+      // compilations will not actually block until jarjvmci is initialized.
+      force_JVMCI_initialization = true;
+    }
+    if (JVMCIPrintProperties || JVMCILibDumpJNIConfig) {
+      // Both JVMCILibDumpJNIConfig and JVMCIPrintProperties exit the VM
+      // so compilation should be disabled. This prevents dumping or
+      // printing from happening more than once.
+      init_compilation = false;
+    }
+  }
+#endif
+  if (init_compilation) {
+    CompileBroker::compilation_init(CHECK_false);
+  }
+#endif
+
+  return force_JVMCI_initialization;
+}
+
 void Threads::initialize_jsr292_core_classes(TRAPS) {
   TraceTime timer("Initialize java.lang.invoke classes", TRACETIME_LOG(Info, startuptime));
 
   initialize_class(vmSymbols::java_lang_invoke_MethodHandle(), CHECK);
   initialize_class(vmSymbols::java_lang_invoke_ResolvedMethodName(), CHECK);
@@ -546,10 +612,12 @@
   main_thread->record_stack_base_and_size();
   main_thread->register_thread_stack_with_NMT();
   main_thread->set_active_handles(JNIHandleBlock::allocate_block());
   MACOS_AARCH64_ONLY(main_thread->init_wx());
 
+  MutexLockerImpl::init_counters(); // depends on mutex_init(), perfMemory_init(), and Thread::initialize_thread_current().
+
   // Set the _monitor_owner_id now since we will run Java code before the Thread instance
   // is even created. The same value will be assigned to the Thread instance on init.
   main_thread->set_monitor_owner_id(ThreadIdentifier::next());
 
   if (!Thread::set_as_starting_thread(main_thread)) {
@@ -568,17 +636,23 @@
 
   // Initialize Java-Level synchronization subsystem
   ObjectMonitor::Initialize();
   ObjectSynchronizer::initialize();
 
+  Deoptimization::init_counters();
+  VMThread::init_counters();
+
   // Initialize global modules
   jint status = init_globals();
   if (status != JNI_OK) {
     main_thread->smr_delete();
     *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
     return status;
   }
+  if (xtty != nullptr)
+    xtty->elem("vm_main_thread thread='%zu'",
+               (uintx) main_thread->osthread()->thread_id());
 
   // Create WatcherThread as soon as we can since we need it in case
   // of hangs during error reporting.
   WatcherThread::start();
 
@@ -719,37 +793,17 @@
   ServiceThread::initialize();
 
   // Start the monitor deflation thread:
   MonitorDeflationThread::initialize();
 
-  // initialize compiler(s)
-#if defined(COMPILER1) || COMPILER2_OR_JVMCI
-  bool init_compilation = true;
-#if INCLUDE_JVMCI
-  bool force_JVMCI_initialization = false;
-  if (EnableJVMCI) {
-    // Initialize JVMCI eagerly when it is explicitly requested.
-    // Or when JVMCILibDumpJNIConfig or JVMCIPrintProperties is enabled.
-    force_JVMCI_initialization = EagerJVMCI || JVMCIPrintProperties || JVMCILibDumpJNIConfig;
-    if (!force_JVMCI_initialization && UseJVMCICompiler && !UseJVMCINativeLibrary && (!UseInterpreter || !BackgroundCompilation)) {
-      // Force initialization of jarjvmci otherwise requests for blocking
-      // compilations will not actually block until jarjvmci is initialized.
-      force_JVMCI_initialization = true;
-    }
-    if (JVMCIPrintProperties || JVMCILibDumpJNIConfig) {
-      // Both JVMCILibDumpJNIConfig and JVMCIPrintProperties exit the VM
-      // so compilation should be disabled. This prevents dumping or
-      // printing from happening more than once.
-      init_compilation = false;
-    }
-  }
-#endif
-  if (init_compilation) {
-    CompileBroker::compilation_init(CHECK_JNI_ERR);
-  }
+#if INCLUDE_CDS
+  // Start the method sampler
+  MethodProfiler::initialize();
 #endif
 
+  bool force_JVMCI_initialization = initialize_compilation(CHECK_JNI_ERR);
+
   if (CDSConfig::is_using_aot_linked_classes()) {
     AOTLinkedClassBulkLoader::finish_loading_javabase_classes(CHECK_JNI_ERR);
     SystemDictionary::restore_archived_method_handle_intrinsics();
   }
 
@@ -766,11 +820,11 @@
 
   // This will initialize the module system.  Only java.base classes can be
   // loaded until phase 2 completes
   call_initPhase2(CHECK_JNI_ERR);
 
-  if (CDSConfig::is_using_aot_linked_classes()) {
+  if (CDSConfig::is_using_aot_linked_classes() && !CDSConfig::is_dumping_final_static_archive()) {
     AOTLinkedClassBulkLoader::load_non_javabase_classes(THREAD);
   }
 #ifndef PRODUCT
   HeapShared::initialize_test_class_from_archive(THREAD);
 #endif
@@ -788,10 +842,15 @@
   call_initPhase3(CHECK_JNI_ERR);
 
   // cache the system and platform class loaders
   SystemDictionary::compute_java_loaders(CHECK_JNI_ERR);
 
+  // Initiate replay training processing once preloading is over.
+  CompileBroker::init_training_replay();
+
+  AOTLinkedClassBulkLoader::replay_training_at_init_for_preloaded_classes(CHECK_JNI_ERR);
+
   if (Continuations::enabled()) {
     // Initialize Continuation class now so that failure to create enterSpecial/doYield
     // special nmethods due to limited CodeCache size can be treated as a fatal error at
     // startup with the proper message that CodeCache size is too small.
     initialize_class(vmSymbols::jdk_internal_vm_Continuation(), CHECK_JNI_ERR);
@@ -810,10 +869,24 @@
   if (force_JVMCI_initialization) {
     JVMCI::initialize_compiler(CHECK_JNI_ERR);
   }
 #endif
 
+  if (PrecompileCode) {
+    Precompiler::compile_cached_code(CHECK_JNI_ERR);
+    if (PrecompileOnlyAndExit) {
+      SCCache::close();
+      log_vm_init_stats();
+      vm_direct_exit(0, "Code precompiation is over");
+    }
+  }
+
+#if defined(COMPILER2)
+  // Pre-load cached compiled methods
+  SCCache::preload_code(CHECK_JNI_ERR);
+#endif
+
   if (NativeHeapTrimmer::enabled()) {
     NativeHeapTrimmer::initialize();
   }
 
   // Always call even when there are not JVMTI environments yet, since environments
@@ -858,18 +931,37 @@
   create_vm_timer.end();
 #ifdef ASSERT
   _vm_complete = true;
 #endif
 
-  if (CDSConfig::is_dumping_static_archive()) {
+  if (CDSConfig::is_dumping_classic_static_archive()) {
+    // Classic -Xshare:dump, aka "old workflow"
+    MetaspaceShared::preload_and_dump(CHECK_JNI_ERR);
+  } else if (CDSConfig::is_dumping_final_static_archive()) {
+    // TODO: copy the verification and loader constraints from preimage to final image
+    // TODO: load archived classes for custom loaders as well.
+    log_info(cds)("Dumping final image of CacheDataStore %s", CacheDataStore);
     MetaspaceShared::preload_and_dump(CHECK_JNI_ERR);
+    vm_direct_exit(0, "CacheDataStore dumping is complete");
   }
 
-  if (log_is_enabled(Info, perf, class, link)) {
-    LogStreamHandle(Info, perf, class, link) log;
-    log.print_cr("At VM initialization completion:");
-    ClassLoader::print_counters(&log);
+  log_info(init)("At VM initialization completion:");
+  log_vm_init_stats();
+
+  if (UsePerfData) {
+    if (ProfileVMLocks) {
+      main_thread->set_profile_vm_locks(true);
+    }
+    if (ProfileVMCalls) {
+      main_thread->set_profile_vm_calls(true);
+    }
+    if (ProfileRuntimeCalls) {
+      main_thread->set_profile_rt_calls(true);
+    }
+    if (ProfileVMOps) {
+      main_thread->set_profile_vm_ops(true);
+    }
   }
 
   return JNI_OK;
 }
 
