diff a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp
--- a/src/hotspot/share/runtime/java.cpp
+++ b/src/hotspot/share/runtime/java.cpp
@@ -21,26 +21,33 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "cds/aotLinkedClassBulkLoader.hpp"
 #include "cds/cds_globals.hpp"
+#include "cds/cdsConfig.hpp"
 #include "cds/classListWriter.hpp"
 #include "cds/dynamicArchive.hpp"
+#include "cds/methodProfiler.hpp"
+#include "cds/metaspaceShared.hpp"
 #include "classfile/classLoader.hpp"
 #include "classfile/classLoaderDataGraph.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/stringTable.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "code/codeCache.hpp"
+#include "code/SCCache.hpp"
 #include "compiler/compilationMemoryStatistic.hpp"
+#include "compiler/compilationPolicy.hpp"
 #include "compiler/compileBroker.hpp"
 #include "compiler/compilerOracle.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/stringdedup/stringDedup.hpp"
 #include "interpreter/bytecodeHistogram.hpp"
+#include "interpreter/interpreterRuntime.hpp"
 #include "jfr/jfrEvents.hpp"
 #include "jfr/support/jfrThreadId.hpp"
 #include "jvm.h"
 #include "logging/log.hpp"
 #include "logging/logStream.hpp"
@@ -57,15 +64,18 @@
 #include "oops/klassVtable.hpp"
 #include "oops/method.hpp"
 #include "oops/objArrayOop.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/symbol.hpp"
+#include "oops/trainingData.hpp"
 #include "prims/jvmtiAgentList.hpp"
 #include "prims/jvmtiExport.hpp"
+#include "prims/methodHandles.hpp"
 #include "runtime/continuation.hpp"
 #include "runtime/deoptimization.hpp"
 #include "runtime/flags/flagSetting.hpp"
+#include "runtime/globals_extension.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/init.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/javaThread.hpp"
@@ -157,10 +167,95 @@
       tty->print_cr("Total MDO size: %d bytes", total_size);
     }
   }
 }
 
+void perf_jvm_print_on(outputStream* st);
+
+void log_vm_init_stats(bool use_tty) {
+  LogStreamHandle(Info, init) log;
+  if (log.is_enabled()) {
+    outputStream* out = use_tty ? tty : &log;
+    SharedRuntime::print_counters_on(out);
+    LogStreamHandle(Info, perf, class, link) log2;
+    outputStream* out2 = use_tty ? tty : &log2;
+    ClassLoader::print_counters(out2);
+    AOTLinkedClassBulkLoader::print_counters();
+    out->cr();
+    // FIXME: intermittent crashes
+//    if (CountBytecodesPerThread) {
+//      log.print_cr("Thread info:");
+//      class PrintThreadInfo : public ThreadClosure {
+//        outputStream* _st;
+//      public:
+//        PrintThreadInfo(outputStream* st) : ThreadClosure(), _st(st) {}
+//        void do_thread(Thread* thread) {
+//          JavaThread* jt = JavaThread::cast(thread);
+//          if (jt->bc_counter_value() > 0) {
+//            _st->print_cr("  Thread " INTPTR_FORMAT ": %ld bytecodes executed (clinit: %ld)",
+//                          p2i(jt), /*jt->name(),*/ jt->bc_counter_value(), jt->clinit_bc_counter_value());
+//          }
+//        }
+//      };
+//      PrintThreadInfo cl(&log);
+//      Threads::java_threads_do(&cl);
+//    }
+//    log.cr();
+    out->print_cr("Deoptimization events: ");
+    Deoptimization::print_statistics_on(out);
+    out->cr();
+
+    out->print("Compilation statistics: ");
+    CompileBroker::print_statistics_on(out);
+    out->cr();
+
+    {
+      MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+      out->print("Code cache statistics: ");
+      CodeCache::print_nmethod_statistics_on(out);
+      out->cr();
+    }
+
+    if (SCCache::is_on_for_read()) {
+      out->print_cr("Startup Code Cache: ");
+      SCCache::print_statistics_on(out);
+      out->cr();
+      SCCache::print_timers_on(out);
+    }
+
+    VMThread::print_counters_on(out);
+    out->cr();
+    MutexLockerImpl::print_counters_on(out);
+    out->cr();
+    out->print("Runtime events for thread \"main\"");
+    if (ProfileRuntimeCalls) {
+      out->print_cr(" (%d nested events):", ProfileVMCallContext::nested_runtime_calls_count());
+
+      InterpreterRuntime::print_counters_on(out);
+#ifdef COMPILER1
+      Runtime1::print_counters_on(out);
+#endif
+#ifdef COMPILER2
+      OptoRuntime::print_counters_on(out);
+      Deoptimization::print_counters_on(out);
+#endif
+    } else {
+      out->print_cr(": no info (%s is disabled)", (UsePerfData ? "ProfileRuntimeCalls" : "UsePerfData"));
+    }
+    out->cr();
+    perf_jvm_print_on(out);
+    out->cr();
+    MethodHandles::print_counters_on(out);
+  }
+}
+
+void print_bytecode_count() {
+  if (CountBytecodes || TraceBytecodes || StopInterpreterAt) {
+    tty->print_cr("[BytecodeCounter::counter_value = " JLONG_FORMAT "]", BytecodeCounter::counter_value());
+  }
+}
+
 #ifndef PRODUCT
 
 // Statistics printing (method invocation histogram)
 
 GrowableArray<Method*>* collected_invoked_methods;
@@ -224,37 +319,35 @@
   tty->print_cr("\t" UINT64_FORMAT_W(12) " (%4.1f%%)    |- final",       final_total,   100.0 * (double)final_total  / total_div);
   tty->print_cr("\t" UINT64_FORMAT_W(12) " (%4.1f%%)    |- static",      static_total,  100.0 * (double)static_total / total_div);
   tty->print_cr("\t" UINT64_FORMAT_W(12) " (%4.1f%%)    |- native",      native_total,  100.0 * (double)native_total / total_div);
   tty->print_cr("\t" UINT64_FORMAT_W(12) " (%4.1f%%)    |- accessor",    access_total,  100.0 * (double)access_total / total_div);
   tty->cr();
-  SharedRuntime::print_call_statistics(comp_total);
-}
-
-static void print_bytecode_count() {
-  if (CountBytecodes || TraceBytecodes || StopInterpreterAt) {
-    tty->print_cr("[BytecodeCounter::counter_value = %d]", BytecodeCounter::counter_value());
-  }
+  SharedRuntime::print_call_statistics_on(tty);
 }
 
 #else
 
 static void print_method_invocation_histogram() {}
-static void print_bytecode_count() {}
 
 #endif // PRODUCT
 
 
 // General statistics printing (profiling ...)
 void print_statistics() {
+#if INCLUDE_CDS
+  if (ReplayTraining && PrintTrainingInfo) {
+    TrainingData::print_archived_training_data_on(tty);
+  }
+#endif
   if (CITime) {
     CompileBroker::print_times();
   }
 
 #ifdef COMPILER1
   if ((PrintC1Statistics || LogVMOutput || LogCompilation) && UseCompiler) {
     FlagSetting fs(DisplayVMOutput, DisplayVMOutput && PrintC1Statistics);
-    Runtime1::print_statistics();
+    Runtime1::print_statistics_on(tty);
     SharedRuntime::print_statistics();
   }
 #endif /* COMPILER1 */
 
 #ifdef COMPILER2
@@ -317,10 +410,16 @@
   // CodeHeap State Analytics.
   if (PrintCodeHeapAnalytics) {
     CompileBroker::print_heapinfo(nullptr, "all", 4096); // details
   }
 
+  LogStreamHandle(Debug, codecache, nmethod) log;
+  if (log.is_enabled()) {
+    MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
+    CodeCache::print_nmethods_on(&log);
+  }
+
 #ifndef PRODUCT
   if (PrintCodeCache2) {
     MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
     CodeCache::print_internals();
   }
@@ -357,11 +456,11 @@
     CompilationMemoryStatistic::print_all_by_size(tty, false, 0);
   }
 
   ThreadsSMRSupport::log_statistics();
 
-  ClassLoader::print_counters(tty);
+  log_vm_init_stats(true /* use_tty */);
 }
 
 // Note: before_exit() can be executed only once, if more than one threads
 //       are trying to shutdown the VM at the same time, only one thread
 //       can run before_exit() and all other threads must wait.
@@ -418,29 +517,38 @@
     static_cast<void>(LSAN_DO_RECOVERABLE_LEAK_CHECK());
   }
 #endif
 
 #if INCLUDE_CDS
-  // Dynamic CDS dumping must happen whilst we can still reliably
-  // run Java code.
-  DynamicArchive::dump_at_exit(thread, ArchiveClassesAtExit);
-  assert(!thread->has_pending_exception(), "must be");
+  MethodProfiler::process_method_hotness();
 #endif
 
-
   // Actual shutdown logic begins here.
 
 #if INCLUDE_JVMCI
   if (EnableJVMCI) {
     JVMCI::shutdown(thread);
   }
 #endif
 
 #if INCLUDE_CDS
   ClassListWriter::write_resolved_constants();
+  ClassListWriter::write_reflection_data();
+  ClassListWriter::write_loader_negative_lookup_cache();
+  // Dynamic CDS dumping must happen whilst we can still reliably
+  // run Java code.
+  if (CDSConfig::is_dumping_preimage_static_archive()) {
+    // Creating the hotspot.cds.preimage file
+    MetaspaceShared::preload_and_dump(thread);
+  } else {
+    DynamicArchive::dump_at_exit(thread, ArchiveClassesAtExit);
+  }
+  assert(!thread->has_pending_exception(), "must be");
 #endif
 
+  SCCache::close(); // Write final data and close archive
+
   // Hang forever on exit if we're reporting an error.
   if (ShowMessageBoxOnError && VMError::is_error_reported()) {
     os::infinite_sleep();
   }
 
@@ -477,11 +585,11 @@
       ClassLoaderDataGraph::print_on(&ls_trace);
     }
   }
 
   if (PrintBytecodeHistogram) {
-    BytecodeHistogram::print();
+    BytecodeHistogram::print(PrintBytecodeHistogramCutoff);
   }
 
 #ifdef LINUX
   if (DumpPerfMapAtExit) {
     CodeCache::write_perf_map(nullptr, tty);
@@ -502,10 +610,15 @@
 
   // Terminate the signal thread
   // Note: we don't wait until it actually dies.
   os::terminate_signal_thread();
 
+  if (VerifyTrainingData) {
+    EXCEPTION_MARK;
+    CompilationPolicy::replay_training_at_init(true, THREAD); // implies TrainingData::verify()
+  }
+
   print_statistics();
   Universe::heap()->print_tracing_info();
 
   { MutexLocker ml(BeforeExit_lock);
     _before_exit_status = BEFORE_EXIT_DONE;
