diff a/src/hotspot/share/opto/runtime.cpp b/src/hotspot/share/opto/runtime.cpp
--- a/src/hotspot/share/opto/runtime.cpp
+++ b/src/hotspot/share/opto/runtime.cpp
@@ -30,10 +30,11 @@
 #include "code/nmethod.hpp"
 #include "code/pcDesc.hpp"
 #include "code/scopeDesc.hpp"
 #include "code/vtableStubs.hpp"
 #include "compiler/compileBroker.hpp"
+#include "compiler/compilerDefinitions.inline.hpp"
 #include "compiler/oopMap.hpp"
 #include "gc/g1/g1HeapRegion.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/gcLocker.hpp"
@@ -63,20 +64,23 @@
 #include "prims/jvmtiExport.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/frame.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/stackWatermarkSet.hpp"
 #include "runtime/synchronizer.hpp"
 #include "runtime/threadCritical.hpp"
 #include "runtime/threadWXSetters.inline.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vframeArray.hpp"
 #include "runtime/vframe_hp.hpp"
+#include "services/management.hpp"
 #include "utilities/copy.hpp"
 #include "utilities/preserveException.hpp"
 
 
 // For debugging purposes:
@@ -98,20 +102,25 @@
 C2_STUBS_DO(C2_BLOB_FIELD_DEFINE, C2_STUB_FIELD_DEFINE, C2_JVMTI_STUB_FIELD_DEFINE)
 #undef C2_BLOB_FIELD_DEFINE
 #undef C2_STUB_FIELD_DEFINE
 #undef C2_JVMTI_STUB_FIELD_DEFINE
 
+
 #define C2_BLOB_NAME_DEFINE(name, type)  "C2 Runtime " # name "_blob",
 #define C2_STUB_NAME_DEFINE(name, f, t, r)  "C2 Runtime " # name,
 #define C2_JVMTI_STUB_NAME_DEFINE(name)  "C2 Runtime " # name,
 const char* OptoRuntime::_stub_names[] = {
   C2_STUBS_DO(C2_BLOB_NAME_DEFINE, C2_STUB_NAME_DEFINE, C2_JVMTI_STUB_NAME_DEFINE)
 };
 #undef C2_BLOB_NAME_DEFINE
 #undef C2_STUB_NAME_DEFINE
 #undef C2_JVMTI_STUB_NAME_DEFINE
 
+address OptoRuntime::_vtable_must_compile_Java                    = nullptr;
+
+PerfCounter* _perf_OptoRuntime_class_init_barrier_redundant_count = nullptr;
+
 // This should be called in an assertion at the start of OptoRuntime routines
 // which are entered from compiled code (all of them)
 #ifdef ASSERT
 static bool check_compiled_frame(JavaThread* thread) {
   assert(thread->last_frame().is_runtime_frame(), "cannot call runtime directly from compiled code");
@@ -172,10 +181,11 @@
                   true,                                                 \
                   false);                                               \
   if (STUB_FIELD_NAME(name) == nullptr) { return false; }               \
 
 bool OptoRuntime::generate(ciEnv* env) {
+  init_counters();
 
   C2_STUBS_DO(GEN_C2_BLOB, GEN_C2_STUB, GEN_C2_JVMTI_STUB)
 
   return true;
 }
@@ -199,11 +209,11 @@
                                    const char *name, int is_fancy_jump,
                                    bool pass_tls,
                                    bool return_pc) {
 
   // Matching the default directive, we currently have no method to match.
-  DirectiveSet* directive = DirectivesStack::getDefaultDirective(CompileBroker::compiler(CompLevel_full_optimization));
+  DirectiveSet* directive = DirectivesStack::getDefaultDirective(CompilerThread::current()->compiler());
   ResourceMark rm;
   Compile C(env, gen, C_function, name, is_fancy_jump, pass_tls, return_pc, directive);
   DirectivesStack::release(directive);
   return  C.stub_entry_point();
 }
@@ -242,11 +252,11 @@
 //=============================allocation======================================
 // We failed the fast-path allocation.  Now we need to do a scavenge or GC
 // and try allocation again.
 
 // object allocation
-JRT_BLOCK_ENTRY(void, OptoRuntime::new_instance_C(Klass* klass, JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, OptoRuntime, new_instance_C, OptoRuntime::new_instance_C(Klass* klass, JavaThread* current))
   JRT_BLOCK;
 #ifndef PRODUCT
   SharedRuntime::_new_instance_ctr++;         // new instance requires GC
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
@@ -280,11 +290,11 @@
   SharedRuntime::on_slowpath_allocation_exit(current);
 JRT_END
 
 
 // array allocation
-JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_C(Klass* array_type, int len, JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, OptoRuntime, new_array_C, OptoRuntime::new_array_C(Klass* array_type, int len, JavaThread* current))
   JRT_BLOCK;
 #ifndef PRODUCT
   SharedRuntime::_new_array_ctr++;            // new array requires GC
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
@@ -317,11 +327,11 @@
   // inform GC that we won't do card marks for initializing writes.
   SharedRuntime::on_slowpath_allocation_exit(current);
 JRT_END
 
 // array allocation without zeroing
-JRT_BLOCK_ENTRY(void, OptoRuntime::new_array_nozero_C(Klass* array_type, int len, JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, OptoRuntime, new_array_nozero_C, OptoRuntime::new_array_nozero_C(Klass* array_type, int len, JavaThread* current))
   JRT_BLOCK;
 #ifndef PRODUCT
   SharedRuntime::_new_array_ctr++;            // new array requires GC
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
@@ -369,11 +379,11 @@
 JRT_END
 
 // Note: multianewarray for one dimension is handled inline by GraphKit::new_array.
 
 // multianewarray for 2 dimensions
-JRT_ENTRY(void, OptoRuntime::multianewarray2_C(Klass* elem_type, int len1, int len2, JavaThread* current))
+JRT_ENTRY_PROF(void, OptoRuntime, multianewarray2_C, OptoRuntime::multianewarray2_C(Klass* elem_type, int len1, int len2, JavaThread* current))
 #ifndef PRODUCT
   SharedRuntime::_multi2_ctr++;                // multianewarray for 1 dimension
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
   assert(elem_type->is_klass(), "not a class");
@@ -385,11 +395,11 @@
   deoptimize_caller_frame(current, HAS_PENDING_EXCEPTION);
   current->set_vm_result(obj);
 JRT_END
 
 // multianewarray for 3 dimensions
-JRT_ENTRY(void, OptoRuntime::multianewarray3_C(Klass* elem_type, int len1, int len2, int len3, JavaThread* current))
+JRT_ENTRY_PROF(void, OptoRuntime, multianewarray3_C, OptoRuntime::multianewarray3_C(Klass* elem_type, int len1, int len2, int len3, JavaThread* current))
 #ifndef PRODUCT
   SharedRuntime::_multi3_ctr++;                // multianewarray for 1 dimension
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
   assert(elem_type->is_klass(), "not a class");
@@ -402,11 +412,11 @@
   deoptimize_caller_frame(current, HAS_PENDING_EXCEPTION);
   current->set_vm_result(obj);
 JRT_END
 
 // multianewarray for 4 dimensions
-JRT_ENTRY(void, OptoRuntime::multianewarray4_C(Klass* elem_type, int len1, int len2, int len3, int len4, JavaThread* current))
+JRT_ENTRY_PROF(void, OptoRuntime, multianewarray4_C, OptoRuntime::multianewarray4_C(Klass* elem_type, int len1, int len2, int len3, int len4, JavaThread* current))
 #ifndef PRODUCT
   SharedRuntime::_multi4_ctr++;                // multianewarray for 1 dimension
 #endif
   assert(check_compiled_frame(current), "incorrect caller");
   assert(elem_type->is_klass(), "not a class");
@@ -438,11 +448,11 @@
   oop obj = ArrayKlass::cast(elem_type)->multi_allocate(5, dims, THREAD);
   deoptimize_caller_frame(current, HAS_PENDING_EXCEPTION);
   current->set_vm_result(obj);
 JRT_END
 
-JRT_ENTRY(void, OptoRuntime::multianewarrayN_C(Klass* elem_type, arrayOopDesc* dims, JavaThread* current))
+JRT_ENTRY_PROF(void, OptoRuntime, multianewarrayN_C, OptoRuntime::multianewarrayN_C(Klass* elem_type, arrayOopDesc* dims, JavaThread* current))
   assert(check_compiled_frame(current), "incorrect caller");
   assert(elem_type->is_klass(), "not a class");
   assert(oop(dims)->is_typeArray(), "not an array");
 
   ResourceMark rm;
@@ -456,11 +466,11 @@
   oop obj = ArrayKlass::cast(elem_type)->multi_allocate(len, c_dims, THREAD);
   deoptimize_caller_frame(current, HAS_PENDING_EXCEPTION);
   current->set_vm_result(obj);
 JRT_END
 
-JRT_BLOCK_ENTRY(void, OptoRuntime::monitor_notify_C(oopDesc* obj, JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, OptoRuntime, monitor_notify_C, OptoRuntime::monitor_notify_C(oopDesc* obj, JavaThread* current))
 
   // Very few notify/notifyAll operations find any threads on the waitset, so
   // the dominant fast-path is to simply return.
   // Relatedly, it's critical that notify/notifyAll be fast in order to
   // reduce lock hold times.
@@ -478,11 +488,11 @@
   Handle h_obj(current, obj);
   ObjectSynchronizer::notify(h_obj, CHECK);
   JRT_BLOCK_END;
 JRT_END
 
-JRT_BLOCK_ENTRY(void, OptoRuntime::monitor_notifyAll_C(oopDesc* obj, JavaThread* current))
+JRT_BLOCK_ENTRY_PROF(void, OptoRuntime, monitor_notifyAll_C, OptoRuntime::monitor_notifyAll_C(oopDesc* obj, JavaThread* current))
 
   if (!SafepointSynchronize::is_synchronizing() ) {
     if (ObjectSynchronizer::quick_notify(obj, current, true)) {
       return;
     }
@@ -1542,11 +1552,11 @@
 static void trace_exception(outputStream* st, oop exception_oop, address exception_pc, const char* msg);
 
 // The method is an entry that is always called by a C++ method not
 // directly from compiled code. Compiled code will call the C++ method following.
 // We can't allow async exception to be installed during  exception processing.
-JRT_ENTRY_NO_ASYNC(address, OptoRuntime::handle_exception_C_helper(JavaThread* current, nmethod* &nm))
+JRT_ENTRY_NO_ASYNC_PROF(address, OptoRuntime, handle_exception_C_helper, OptoRuntime::handle_exception_C_helper(JavaThread* current, nmethod* &nm))
   // The frame we rethrow the exception to might not have been processed by the GC yet.
   // The stack watermark barrier takes care of detecting that and ensuring the frame
   // has updated oops.
   StackWatermarkSet::after_unwind(current);
 
@@ -1818,10 +1828,24 @@
   const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0,fields);
 
   return TypeFunc::make(domain,range);
 }
 
+const TypeFunc *OptoRuntime::class_init_barrier_Type() {
+  // create input type (domain)
+  const Type** fields = TypeTuple::fields(1);
+  fields[TypeFunc::Parms+0] = TypeKlassPtr::NOTNULL;
+  // // The JavaThread* is passed to each routine as the last argument
+  // fields[TypeFunc::Parms+1] = TypeRawPtr::NOTNULL;  // JavaThread *; Executing thread
+  const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+1, fields);
+
+  // create result type (range)
+  fields = TypeTuple::fields(0);
+  const TypeTuple *range = TypeTuple::make(TypeFunc::Parms+0, fields);
+  return TypeFunc::make(domain,range);
+}
+
 #if INCLUDE_JFR
 const TypeFunc *OptoRuntime::class_id_load_barrier_Type() {
   // create input type (domain)
   const Type **fields = TypeTuple::fields(1);
   fields[TypeFunc::Parms+0] = TypeInstPtr::KLASS;
@@ -1868,16 +1892,25 @@
 
   return TypeFunc::make(domain,range);
 }
 
 
-JRT_ENTRY_NO_ASYNC(void, OptoRuntime::register_finalizer_C(oopDesc* obj, JavaThread* current))
+JRT_ENTRY_NO_ASYNC_PROF(void, OptoRuntime, register_finalizer_C, OptoRuntime::register_finalizer_C(oopDesc* obj, JavaThread* current))
   assert(oopDesc::is_oop(obj), "must be a valid oop");
   assert(obj->klass()->has_finalizer(), "shouldn't be here otherwise");
   InstanceKlass::register_finalizer(instanceOop(obj), CHECK);
 JRT_END
 
+JRT_ENTRY_NO_ASYNC_PROF(void, OptoRuntime, class_init_barrier_C, OptoRuntime::class_init_barrier_C(Klass* k, JavaThread* current))
+  InstanceKlass* ik = InstanceKlass::cast(k);
+  if (ik->should_be_initialized()) {
+    ik->initialize(CHECK);
+  } else if (UsePerfData) {
+    _perf_OptoRuntime_class_init_barrier_redundant_count->inc();
+  }
+JRT_END
+
 //-----------------------------------------------------------------------------
 
 NamedCounter * volatile OptoRuntime::_named_counters = nullptr;
 
 //
@@ -1974,5 +2007,71 @@
   tempst.print(" at " INTPTR_FORMAT,  p2i(exception_pc));
   tempst.print("]");
 
   st->print_raw_cr(tempst.freeze());
 }
+
+#define DO_COUNTERS2(macro2, macro1) \
+  macro2(OptoRuntime, new_instance_C) \
+  macro2(OptoRuntime, new_array_C) \
+  macro2(OptoRuntime, new_array_nozero_C) \
+  macro2(OptoRuntime, multianewarray2_C) \
+  macro2(OptoRuntime, multianewarray3_C) \
+  macro2(OptoRuntime, multianewarray4_C) \
+  macro2(OptoRuntime, multianewarrayN_C) \
+  macro2(OptoRuntime, monitor_notify_C) \
+  macro2(OptoRuntime, monitor_notifyAll_C) \
+  macro2(OptoRuntime, handle_exception_C_helper) \
+  macro2(OptoRuntime, register_finalizer_C) \
+  macro2(OptoRuntime, class_init_barrier_C) \
+  macro1(OptoRuntime, class_init_barrier_redundant)
+
+#define INIT_COUNTER_TIME_AND_CNT(sub, name) \
+  NEWPERFTICKCOUNTERS(_perf_##sub##_##name##_timer, SUN_CI, #sub "::" #name); \
+  NEWPERFEVENTCOUNTER(_perf_##sub##_##name##_count, SUN_CI, #sub "::" #name "_count");
+
+#define INIT_COUNTER_CNT(sub, name) \
+  NEWPERFEVENTCOUNTER(_perf_##sub##_##name##_count, SUN_CI, #sub "::" #name "_count");
+
+void OptoRuntime::init_counters() {
+  assert(CompilerConfig::is_c2_enabled(), "");
+
+  if (UsePerfData) {
+    EXCEPTION_MARK;
+
+    DO_COUNTERS2(INIT_COUNTER_TIME_AND_CNT, INIT_COUNTER_CNT)
+
+    if (HAS_PENDING_EXCEPTION) {
+      vm_exit_during_initialization("jvm_perf_init failed unexpectedly");
+    }
+  }
+}
+#undef INIT_COUNTER_TIME_AND_CNT
+#undef INIT_COUNTER_CNT
+
+#define PRINT_COUNTER_TIME_AND_CNT(sub, name) { \
+  jlong count = _perf_##sub##_##name##_count->get_value(); \
+  if (count > 0) { \
+    st->print_cr("  %-50s = " JLONG_FORMAT_W(6) "us (elapsed) " JLONG_FORMAT_W(6) "us (thread) (" JLONG_FORMAT_W(5) " events)", #sub "::" #name, \
+                 _perf_##sub##_##name##_timer->elapsed_counter_value_us(), \
+                 _perf_##sub##_##name##_timer->thread_counter_value_us(), \
+                 count); \
+  }}
+
+#define PRINT_COUNTER_CNT(sub, name) { \
+  jlong count = _perf_##sub##_##name##_count->get_value(); \
+  if (count > 0) { \
+    st->print_cr("  %-30s = " JLONG_FORMAT_W(5) " events", #name, count); \
+  }}
+
+void OptoRuntime::print_counters_on(outputStream* st) {
+  if (UsePerfData && ProfileRuntimeCalls && CompilerConfig::is_c2_enabled()) {
+    DO_COUNTERS2(PRINT_COUNTER_TIME_AND_CNT, PRINT_COUNTER_CNT)
+  } else {
+    st->print_cr("  OptoRuntime: no info (%s is disabled)",
+                 (!CompilerConfig::is_c2_enabled() ? "C2" : (UsePerfData ? "ProfileRuntimeCalls" : "UsePerfData")));
+  }
+}
+
+#undef PRINT_COUNTER_TIME_AND_CNT
+#undef PRINT_COUNTER_CNT
+#undef DO_COUNTERS2
