< prev index next >

src/hotspot/share/oops/instanceKlass.cpp

Print this page
@@ -23,10 +23,11 @@
   */
  
  #include "precompiled.hpp"
  #include "cds/archiveUtils.hpp"
  #include "cds/cdsConfig.hpp"
+ #include "cds/cdsEnumKlass.hpp"
  #include "cds/classListWriter.hpp"
  #include "cds/heapShared.hpp"
  #include "cds/metaspaceShared.hpp"
  #include "classfile/classFileParser.hpp"
  #include "classfile/classFileStream.hpp"

@@ -42,10 +43,11 @@
  #include "code/codeCache.hpp"
  #include "code/dependencyContext.hpp"
  #include "compiler/compilationPolicy.hpp"
  #include "compiler/compileBroker.hpp"
  #include "gc/shared/collectedHeap.inline.hpp"
+ #include "interpreter/bytecodeHistogram.hpp"
  #include "interpreter/bytecodeStream.hpp"
  #include "interpreter/oopMapCache.hpp"
  #include "interpreter/rewriter.hpp"
  #include "jvm.h"
  #include "jvmtifiles/jvmti.h"

@@ -69,10 +71,11 @@
  #include "oops/klass.inline.hpp"
  #include "oops/method.hpp"
  #include "oops/oop.inline.hpp"
  #include "oops/recordComponent.hpp"
  #include "oops/symbol.hpp"
+ #include "oops/trainingData.hpp"
  #include "prims/jvmtiExport.hpp"
  #include "prims/jvmtiRedefineClasses.hpp"
  #include "prims/jvmtiThreadState.hpp"
  #include "prims/methodComparator.hpp"
  #include "runtime/arguments.hpp"

@@ -516,11 +519,12 @@
    _itable_len(parser.itable_size()),
    _nest_host_index(0),
    _init_state(allocated),
    _reference_type(reference_type),
    _init_monitor(create_init_monitor("InstanceKlassInitMonitor_lock")),
-   _init_thread(nullptr)
+   _init_thread(nullptr),
+   _training_data(nullptr)
  {
    set_vtable_length(parser.vtable_size());
    set_access_flags(parser.access_flags());
    if (parser.is_hidden()) set_is_hidden();
    set_layout_helper(Klass::instance_layout_helper(parser.layout_size(),

@@ -757,10 +761,101 @@
    } else {
      assert(is_initialized(), "sanity check");
    }
  }
  
+ static bool are_super_types_initialized(InstanceKlass* ik) {
+   InstanceKlass* s = ik->java_super();
+   if (s != nullptr && !s->is_initialized()) {
+     return false;
+   }
+ 
+   if (ik->has_nonstatic_concrete_methods()) {
+     // Only need to recurse if has_nonstatic_concrete_methods which includes declaring and
+     // having a superinterface that declares, non-static, concrete methods
+     Array<InstanceKlass*>* interfaces = ik->local_interfaces();
+     int len = interfaces->length();
+     for (int i = 0; i < len; i++) {
+       InstanceKlass* intf = interfaces->at(i);
+       if (!intf->is_initialized()) {
+         return false;
+       }
+     }
+   }
+ 
+   return true;
+ }
+ 
+ static void log_class_init_start(outputStream* st, JavaThread* current, InstanceKlass* ik, int init_id) {
+   ResourceMark rm;
+   const char* info = "";
+   if (ik->has_preinitialized_mirror() && CDSConfig::is_loading_heap()) {
+     info = " (preinitialized)";
+   } else if (ik->class_initializer() == nullptr) {
+     info = " (no method)";
+   }
+   st->print("%d Initializing ", init_id);
+   ik->name()->print_value_on(st);
+   st->print_cr("%s (" PTR_FORMAT ") by thread " PTR_FORMAT " \"%s\"", info, p2i(ik), p2i(current), current->name());
+ }
+ 
+ static int log_class_init(JavaThread* current, InstanceKlass* ik) {
+   int init_id = -1;
+   LogStreamHandle(Info,  init, class) lsh1;
+   LogStreamHandle(Debug, init)        lsh2;
+   if (lsh1.is_enabled() || lsh2.is_enabled()) {
+     static int call_class_initializer_counter = 0;  // for debugging
+     init_id = Atomic::fetch_then_add(&call_class_initializer_counter, 1);
+     if (lsh1.is_enabled()) {
+       log_class_init_start(&lsh1, current, ik, init_id);
+     }
+     if (lsh2.is_enabled() && ik->class_initializer() != nullptr && !ik->has_preinitialized_mirror()) {
+       log_class_init_start(&lsh2, current, ik, init_id);
+     }
+   }
+   return init_id;
+ }
+ 
+ void InstanceKlass::initialize_from_cds(TRAPS) {
+   if (is_initialized()) {
+     return;
+   }
+ 
+   if (has_preinitialized_mirror() && CDSConfig::is_loading_heap() &&
+       !ForceProfiling &&
+       !RecordTraining &&
+       are_super_types_initialized(this)) {
+     // FIXME: also check for events listeners such as JVMTI, JFR, etc
+     if (log_is_enabled(Info, cds, init)) {
+       ResourceMark rm;
+       log_info(cds, init)("%s (quickest)", external_name());
+     }
+ 
+     link_class(CHECK);
+ 
+ #ifdef AZZERT
+     {
+       MonitorLocker ml(THREAD, _init_monitor);
+       assert(!initialized(), "sanity");
+       assert(!is_being_initialized(), "sanity");
+       assert(!is_in_error_state(), "sanity");
+     }
+ #endif
+ 
+     log_class_init(THREAD, this);
+     set_init_thread(THREAD);
+     set_initialization_state_and_notify(fully_initialized, CHECK);
+     return;
+   }
+ 
+   if (log_is_enabled(Info, cds, init)) {
+     ResourceMark rm;
+     log_info(cds, init)("%s%s", external_name(),
+                         (has_preinitialized_mirror() && CDSConfig::is_loading_heap()) ? " (quicker)" : "");
+   }
+   initialize(THREAD);
+ }
  
  bool InstanceKlass::verify_code(TRAPS) {
    // 1) Verify the bytecodes
    return Verifier::verify(this, should_verify_class(), THREAD);
  }

@@ -976,34 +1071,43 @@
  
  // Now relocate and link method entry points after class is rewritten.
  // This is outside is_rewritten flag. In case of an exception, it can be
  // executed more than once.
  void InstanceKlass::link_methods(TRAPS) {
+   PerfTraceElapsedTime timer(ClassLoader::perf_ik_link_methods_time());
+ 
    int len = methods()->length();
    for (int i = len-1; i >= 0; i--) {
      methodHandle m(THREAD, methods()->at(i));
  
      // Set up method entry points for compiler and interpreter    .
      m->link_method(m, CHECK);
    }
  }
  
  // Eagerly initialize superinterfaces that declare default methods (concrete instance: any access)
- void InstanceKlass::initialize_super_interfaces(TRAPS) {
+ void InstanceKlass::initialize_super_interfaces(Klass* requester, TRAPS) {
    assert (has_nonstatic_concrete_methods(), "caller should have checked this");
    for (int i = 0; i < local_interfaces()->length(); ++i) {
      InstanceKlass* ik = local_interfaces()->at(i);
  
      // Initialization is depth first search ie. we start with top of the inheritance tree
      // has_nonstatic_concrete_methods drives searching superinterfaces since it
      // means has_nonstatic_concrete_methods in its superinterface hierarchy
      if (ik->has_nonstatic_concrete_methods()) {
-       ik->initialize_super_interfaces(CHECK);
+       ik->initialize_super_interfaces(requester, CHECK);
      }
  
      // Only initialize() interfaces that "declare" concrete methods.
      if (ik->should_be_initialized() && ik->declares_nonstatic_concrete_methods()) {
+       if (RecordTraining && requester != nullptr) {
+         ik->record_initialization_touch("super", nullptr, nullptr, requester,
+                                         nullptr, THREAD);
+         if (HAS_PENDING_EXCEPTION) {
+           CLEAR_PENDING_EXCEPTION;  // could not allocate training data
+         }
+       }
        ik->initialize(CHECK);
      }
    }
  }
  

@@ -1084,10 +1188,25 @@
    bool wait = false;
    bool throw_error = false;
  
    JavaThread* jt = THREAD;
  
+   if (ForceProfiling) {
+     // Preallocate MDOs.
+     for (int i = 0; i < methods()->length(); i++) {
+       assert(!HAS_PENDING_EXCEPTION, "");
+       methodHandle m(THREAD, methods()->at(i));
+       Method::build_profiling_method_data(m, THREAD);
+       if (HAS_PENDING_EXCEPTION) {
+         ResourceMark rm;
+         log_warning(cds)("MDO preallocation failed for %s", external_name());
+         CLEAR_PENDING_EXCEPTION;
+         break;
+       }
+     }
+   }
+ 
    bool debug_logging_enabled = log_is_enabled(Debug, class, init);
  
    // refer to the JVM book page 47 for description of steps
    // Step 1
    {

@@ -1170,18 +1289,29 @@
    // Next, if C is a class rather than an interface, initialize it's super class and super
    // interfaces.
    if (!is_interface()) {
      Klass* super_klass = super();
      if (super_klass != nullptr && super_klass->should_be_initialized()) {
+       // do not bother to report touches from an untouched subclass
+       if (RecordTraining && has_initialization_touch()) {
+         InstanceKlass::cast(super_klass)
+           ->record_initialization_touch("super", nullptr, nullptr, this,
+                                         nullptr, THREAD);
+         if (HAS_PENDING_EXCEPTION) {
+           CLEAR_PENDING_EXCEPTION;  // could not allocate training data
+         }
+       }
        super_klass->initialize(THREAD);
      }
      // If C implements any interface that declares a non-static, concrete method,
      // the initialization of C triggers initialization of its super interfaces.
      // Only need to recurse if has_nonstatic_concrete_methods which includes declaring and
      // having a superinterface that declares, non-static, concrete methods
      if (!HAS_PENDING_EXCEPTION && has_nonstatic_concrete_methods()) {
-       initialize_super_interfaces(THREAD);
+       // do not bother to report touches from an untouched implementer
+       Klass* requester = has_initialization_touch() ? this : nullptr;
+       initialize_super_interfaces(requester, THREAD);
      }
  
      // If any exceptions, complete abruptly, throwing the same exception as above.
      if (HAS_PENDING_EXCEPTION) {
        Handle e(THREAD, PENDING_EXCEPTION);

@@ -1251,12 +1381,13 @@
                  vmSymbols::throwable_void_signature(),
                  &args);
      }
    }
    DTRACE_CLASSINIT_PROBE_WAIT(end, -1, wait);
- }
  
+   CompilationPolicy::replay_training_at_init(this, THREAD);
+ }
  
  void InstanceKlass::set_initialization_state_and_notify(ClassState state, JavaThread* current) {
    MonitorLocker ml(current, _init_monitor);
  
    if (state == linked && UseVtableBasedCHA && Universe::is_fully_initialized()) {

@@ -1511,18 +1642,23 @@
      i = register_finalizer(i, CHECK_NULL);
    }
    return i;
  }
  
- instanceOop InstanceKlass::allocate_instance(oop java_class, TRAPS) {
+ instanceOop InstanceKlass::allocate_instance(oop java_class,
+                                              const char* who,
+                                              TRAPS) {
    Klass* k = java_lang_Class::as_Klass(java_class);
    if (k == nullptr) {
      ResourceMark rm(THREAD);
      THROW_(vmSymbols::java_lang_InstantiationException(), nullptr);
    }
    InstanceKlass* ik = cast(k);
    ik->check_valid_for_instantiation(false, CHECK_NULL);
+   if (RecordTraining) {
+     ik->record_initialization_touch("new", nullptr, nullptr, nullptr, who, CHECK_NULL);
+   }
    ik->initialize(CHECK_NULL);
    return ik->allocate_instance(THREAD);
  }
  
  instanceHandle InstanceKlass::allocate_instance_handle(TRAPS) {

@@ -1580,12 +1716,10 @@
  
  ArrayKlass* InstanceKlass::array_klass_or_null() {
    return array_klass_or_null(1);
  }
  
- static int call_class_initializer_counter = 0;   // for debugging
- 
  Method* InstanceKlass::class_initializer() const {
    Method* clinit = find_method(
        vmSymbols::class_initializer_name(), vmSymbols::void_method_signature());
    if (clinit != nullptr && clinit->has_valid_initializer_flags()) {
      return clinit;

@@ -1603,37 +1737,123 @@
  
  #if INCLUDE_CDS
    // This is needed to ensure the consistency of the archived heap objects.
    if (has_archived_enum_objs()) {
      assert(is_shared(), "must be");
-     bool initialized = HeapShared::initialize_enum_klass(this, CHECK);
+     bool initialized = CDSEnumKlass::initialize_enum_klass(this, CHECK);
      if (initialized) {
        return;
      }
+   } else if (has_preinitialized_mirror() && CDSConfig::is_loading_heap()) {
+     log_class_init(THREAD, this);
+     return;
    }
  #endif
  
    methodHandle h_method(THREAD, class_initializer());
    assert(!is_initialized(), "we cannot initialize twice");
+   int init_id = log_class_init(THREAD, this);
+   if (h_method() != nullptr) {
+     JavaCallArguments args; // No arguments
+     JavaValue result(T_VOID);
+     KlassTrainingData* tdata = nullptr;
+     if (RecordTraining) {
+       tdata = alloc_training_data(CHECK);
+       if (HAS_PENDING_EXCEPTION) {
+         CLEAR_PENDING_EXCEPTION;  // could not allocate training data
+       }
+     }
+     InstanceKlass* outer = THREAD->set_class_being_initialized(this);
+     if (tdata != nullptr) {
+       tdata->record_initialization_start();
+     }
+ 
+     jlong bc_start = (CountBytecodesPerThread ? THREAD->bc_counter_value() : BytecodeCounter::counter_value());
+ 
+     elapsedTimer timer;
+     {
+       PerfPauseTimer pt(THREAD->current_rt_call_timer(), THREAD->profile_rt_calls());
+       PauseRuntimeCallProfiling prcp(THREAD, THREAD->profile_rt_calls());
+ 
+       timer.start();
+       JavaCalls::call(&result, h_method, &args, THREAD); // Static call (no args)
+       timer.stop();
+     }
+ 
+     jlong bc_end = (CountBytecodesPerThread ? THREAD->bc_counter_value() : BytecodeCounter::counter_value());
+ 
+     jlong bc_executed = (bc_end - bc_start);
+     if (UsePerfData && outer == nullptr) { // outermost clinit
+       THREAD->inc_clinit_bc_counter_value(bc_executed);
+       ClassLoader::perf_class_init_bytecodes_count()->inc(bc_executed);
+     }
+ 
+     THREAD->set_class_being_initialized(outer);
+ 
+     if (tdata != nullptr) {
+       tdata->record_initialization_end();
+     }
+ 
+     LogStreamHandle(Debug, init) log;
+     if (log.is_enabled()) {
+       ResourceMark rm(THREAD);
+       log.print("%d Initialized in %.3fms (total: %ldms); ",
+                 init_id, timer.seconds() * 1000.0, ClassLoader::class_init_time_ms());
+       if (CountBytecodes || CountBytecodesPerThread) {
+         log.print("executed %ld bytecodes; ", bc_executed);
+       }
+       name()->print_value_on(&log);
+       log.print_cr(" by thread " PTR_FORMAT " \"%s\" (" PTR_FORMAT ")",
+                    p2i(THREAD), THREAD->name(), p2i(this));
+     }
+   }
    LogTarget(Info, class, init) lt;
    if (lt.is_enabled()) {
      ResourceMark rm(THREAD);
      LogStream ls(lt);
-     ls.print("%d Initializing ", call_class_initializer_counter++);
+     ls.print("%d Initialized ", init_id);
      name()->print_value_on(&ls);
-     ls.print_cr("%s (" PTR_FORMAT ") by thread \"%s\"",
-                 h_method() == nullptr ? "(no method)" : "", p2i(this),
-                 THREAD->name());
+     ls.print_cr("%s (" PTR_FORMAT ")", h_method() == nullptr ? "(no method)" : "", p2i(this));
    }
-   if (h_method() != nullptr) {
-     JavaCallArguments args; // No arguments
-     JavaValue result(T_VOID);
-     JavaCalls::call(&result, h_method, &args, CHECK); // Static call (no args)
+ }
+ 
+ void InstanceKlass::record_initialization_touch(const char* reason,
+                                                 Symbol* name,
+                                                 Symbol* sig,
+                                                 Klass* requesting_klass,
+                                                 const char* context,
+                                                 TRAPS) {
+   if (requesting_klass == this) {
+     return;  // self-initialization is never interesting
+   }
+   if (is_initialized() && !has_initialization_touch()) {
+     // initialized by some hardwired JVM logic; not interesting
+     return;
    }
+   KlassTrainingData* tdata = alloc_training_data(CHECK);
+   if (tdata == nullptr)  return;
+   tdata->record_initialization_touch(reason, name, sig,
+                                      requesting_klass, context, THREAD);
  }
  
  
+ bool InstanceKlass::has_initialization_touch() const {
+   KlassTrainingData* tdata = training_data_or_null();
+   if (tdata == nullptr)  return false;
+   return tdata->has_initialization_touch();
+ }
+ 
+ KlassTrainingData* InstanceKlass::alloc_training_data(TRAPS) {
+   guarantee(RecordTraining || ReplayTraining, "caller resp.");
+   KlassTrainingData* tdata = training_data_or_null();
+   if (tdata == nullptr) {
+     tdata = KlassTrainingData::make(this);
+     assert(tdata == training_data_or_null(), "");
+   }
+   return tdata;
+ }
+ 
  void InstanceKlass::mask_for(const methodHandle& method, int bci,
    InterpreterOopMap* entry_for) {
    // Lazily create the _oop_map_cache at first request
    // Lock-free access requires load_acquire.
    OopMapCache* oop_map_cache = Atomic::load_acquire(&_oop_map_cache);

@@ -2473,11 +2693,11 @@
    DependencyContext dep_context(&_dep_context, &_dep_context_last_cleaned);
    return dep_context;
  }
  
  void InstanceKlass::mark_dependent_nmethods(DeoptimizationScope* deopt_scope, KlassDepChange& changes) {
-   dependencies().mark_dependent_nmethods(deopt_scope, changes);
+   dependencies().mark_dependent_nmethods(deopt_scope, changes, this);
  }
  
  void InstanceKlass::add_dependent_nmethod(nmethod* nm) {
    dependencies().add_dependent_nmethod(nm);
  }

@@ -2602,10 +2822,11 @@
          }
        }
      }
    }
  
+   it->push(&_nest_host);
    it->push(&_nest_members);
    it->push(&_permitted_subclasses);
    it->push(&_record_components);
  }
  

@@ -2636,11 +2857,12 @@
    { // Otherwise this needs to take out the Compile_lock.
      assert(SafepointSynchronize::is_at_safepoint(), "only called at safepoint");
      init_implementor();
    }
  
-   constants()->remove_unshareable_info();
+   // ConstantPool is cleaned separately. See ArchiveBuilder::make_klasses_shareable()
+   // constants()->remove_unshareable_info();
  
    for (int i = 0; i < methods()->length(); i++) {
      Method* m = methods()->at(i);
      m->remove_unshareable_info();
    }

@@ -2663,16 +2885,22 @@
  
    _init_thread = nullptr;
    _methods_jmethod_ids = nullptr;
    _jni_ids = nullptr;
    _oop_map_cache = nullptr;
-   // clear _nest_host to ensure re-load at runtime
-   _nest_host = nullptr;
+   if (ArchiveInvokeDynamic && HeapShared::is_lambda_proxy_klass(this)) {
+     // keep _nest_host
+   } else {
+     // clear _nest_host to ensure re-load at runtime
+     _nest_host = nullptr;
+   }
    init_shared_package_entry();
    _dep_context_last_cleaned = 0;
    _init_monitor = nullptr;
+   DEBUG_ONLY(_shared_class_load_count = 0);
  
+   _training_data = nullptr;
    remove_unshareable_flags();
  }
  
  void InstanceKlass::remove_unshareable_flags() {
    // clear all the flags/stats that shouldn't be in the archived version

@@ -2774,23 +3002,37 @@
      set_is_value_based();
    }
  
    // restore the monitor
    _init_monitor = create_init_monitor("InstanceKlassInitMonitorRestored_lock");
+ 
+   if (_training_data != nullptr) {
+     _training_data->restore_unshareable_info(CHECK);
+   }
  }
  
- // Check if a class or any of its supertypes has a version older than 50.
- // CDS will not perform verification of old classes during dump time because
- // without changing the old verifier, the verification constraint cannot be
- // retrieved during dump time.
- // Verification of archived old classes will be performed during run time.
  bool InstanceKlass::can_be_verified_at_dumptime() const {
+   if (CDSConfig::preserve_all_dumptime_verification_states(this)) {
+     return true;
+   }
+ 
    if (MetaspaceShared::is_in_shared_metaspace(this)) {
      // This is a class that was dumped into the base archive, so we know
      // it was verified at dump time.
      return true;
    }
+ 
+   if (ArchiveInvokeDynamic) {
+     // FIXME: this works around JDK-8315719
+     return true;
+   }
+ 
+   // Check if a class or any of its supertypes has a version older than 50.
+   // CDS will not perform verification of old classes during dump time because
+   // without changing the old verifier, the verification constraint cannot be
+   // retrieved during dump time.
+   // Verification of archived old classes will be performed during run time.
    if (major_version() < 50 /*JAVA_6_VERSION*/) {
      return false;
    }
    if (java_super() != nullptr && !java_super()->can_be_verified_at_dumptime()) {
      return false;

@@ -2817,10 +3059,22 @@
        }
      }
    }
    return false;
  }
+ 
+ int InstanceKlass::shared_class_loader_type() const {
+   if (is_shared_boot_class()) {
+     return ClassLoader::BOOT_LOADER;
+   } else if (is_shared_platform_class()) {
+     return ClassLoader::PLATFORM_LOADER;
+   } else if (is_shared_app_class()) {
+     return ClassLoader::APP_LOADER;
+   } else {
+     return ClassLoader::OTHER;
+   }
+ }
  #endif // INCLUDE_CDS
  
  #if INCLUDE_JVMTI
  static void clear_all_breakpoints(Method* m) {
    m->clear_all_breakpoints();

@@ -3586,10 +3840,14 @@
  
  const char* InstanceKlass::init_state_name() const {
    return state_names[init_state()];
  }
  
+ const char* InstanceKlass::state2name(ClassState s) {
+   return state_names[s];
+ }
+ 
  void InstanceKlass::print_on(outputStream* st) const {
    assert(is_klass(), "must be klass");
    Klass::print_on(st);
  
    st->print(BULLET"instance size:     %d", size_helper());                        st->cr();

@@ -3904,10 +4162,27 @@
      } else {
        info_stream.print(" source: shared objects file");
      }
    }
  
+   info_stream.print(" loader:");
+   if (is_shared()) {
+     info_stream.print(" %s", SystemDictionaryShared::class_loader_name_for_shared((Klass*)this));
+   } else if (loader_data == ClassLoaderData::the_null_class_loader_data()) {
+     info_stream.print(" boot_loader");
+   } else {
+     oop class_loader = loader_data->class_loader();
+     if (class_loader != nullptr) {
+       info_stream.print(" %s", class_loader->klass()->external_name());
+       oop cl_name_and_id = java_lang_ClassLoader::nameAndId(class_loader);
+       if (cl_name_and_id != nullptr) {
+         info_stream.print(" %s", java_lang_String::as_utf8_string(cl_name_and_id));
+       }
+     } else {
+       info_stream.print(" null");
+     }
+   }
    msg.info("%s", info_stream.as_string());
  
    if (log_is_enabled(Debug, class, load)) {
      stringStream debug_stream;
  
< prev index next >