JVM

new背后的故事

2019-01-19  本文已影响17人  AlanKim

对象创建

例子

首先看一个例子:

public class BBB {

    int id;

    String name;

    public BBB(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

public class AAA {
    public static void main(String[] args){
        BBB bb = new BBB(1,"jsy");
    }

}

编译之后,用javap -c AAA来看对应的字节码:

➜ javap -c AAA
Compiled from "AAA.java"
public class AAA {
  public AAA();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class BBB
       3: dup
       4: iconst_1
       5: ldc           #3                  // String jsy
       7: invokespecial #4                  // Method BBB."<init>":(ILjava/lang/String;)V
      10: astore_1
      11: return
}

可以看到,4-8行是构造函数,10-18是main方法。

其中在main方法中,#2 这里new了一个class BBB的对象,这个就是我们需要着重看到的对象创建。

new背后的故事

new关键字对应jvm中的_new指令,实现在interpreterRuntime.cpp中,代码如下:

IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index))
  Klass* k_oop = pool->klass_at(index, CHECK);
  instanceKlassHandle klass (THREAD, k_oop);

  // Make sure we are not instantiating an abstract klass
  klass->check_valid_for_instantiation(true, CHECK);

  // Make sure klass is initialized
  klass->initialize(CHECK);

  // At this point the class may not be fully initialized
  // because of recursive initialization. If it is fully
  // initialized & has_finalized is not set, we rewrite
  // it into its fast version (Note: no locking is needed
  // here since this is an atomic byte write and can be
  // done more than once).
  //
  // Note: In case of classes with has_finalized we don't
  //       rewrite since that saves us an extra check in
  //       the fast version which then would call the
  //       slow version anyway (and do a call back into
  //       Java).
  //       If we have a breakpoint, then we don't rewrite
  //       because the _breakpoint bytecode would be lost.
  oop obj = klass->allocate_instance(CHECK);
  thread->set_vm_result(obj);
IRT_END

其中:

  1. pool是指AAA类的constant pool,此时AAA的class已经加载JVM中

  2. new之后的#2 代表BBB类的全限定名的符号引用在constant pool中的位置

  3. pool —>class_at()负责返回new的对象---BBB类对应的klass对象,实现如下:

    Klass* ConstantPool::klass_at_impl(const constantPoolHandle& this_cp, int which,
                                       bool save_resolution_error, TRAPS) {
      assert(THREAD->is_Java_thread(), "must be a Java thread");
    
      // A resolved constantPool entry will contain a Klass*, otherwise a Symbol*.
      // It is not safe to rely on the tag bit's here, since we don't have a lock, and
      // the entry and tag is not updated atomicly.
      CPSlot entry = this_cp->slot_at(which);
      if (entry.is_resolved()) {
        assert(entry.get_klass()->is_klass(), "must be");
        // Already resolved - return entry.
        return entry.get_klass();
      }
    
      // This tag doesn't change back to unresolved class unless at a safepoint.
      if (this_cp->tag_at(which).is_unresolved_klass_in_error()) {
        // The original attempt to resolve this constant pool entry failed so find the
        // class of the original error and throw another error of the same class
        // (JVMS 5.4.3).
        // If there is a detail message, pass that detail message to the error.
        // The JVMS does not strictly require us to duplicate the same detail message,
        // or any internal exception fields such as cause or stacktrace.  But since the
        // detail message is often a class name or other literal string, we will repeat it
        // if we can find it in the symbol table.
        throw_resolution_error(this_cp, which, CHECK_0);
        ShouldNotReachHere();
      }
    

    可以看到,如果常量池中指定位置 #2 的数据已经resolve(分析)并且是klass类型,直接通过entry.get_class返回klass。否则抛出异常。注意这里跟jdk7的实现不同,jdk7的实现如下:

2184951-513c69b79cccd607.png

可以看到,这里的klassOop已经被Klass所代替,而且is_oop也被is_resolved替代。

而且如果是unresolve状态,直接抛出异常,不会继续下去,这个跟JDK7 也完全不同

  1. klass->check_valid_for_instantiation 防止抽象类被实例化

  2. klass->initialize初始化,具体实现在InstanceKlass.cpp中如下:

    // See "The Virtual Machine Specification" section 2.16.5 for a detailed explanation of the class initialization
    // process. The step comments refers to the procedure described in that section.
    // Note: implementation moved to static method to expose the this pointer.
    void InstanceKlass::initialize(TRAPS) {
      if (this->should_be_initialized()) {
        HandleMark hm(THREAD);
        instanceKlassHandle this_k(THREAD, this);
        initialize_impl(this_k, CHECK);
        // Note: at this point the class may be initialized
        //       OR it may be in the state of being initialized
        //       in case of recursive initialization!
      } else {
        assert(is_initialized(), "sanity check");
      }
    }
    

    可看到,如果BBB的instanceKlass对象已经初始化完成,则直接返回。

    否则,通过initialize_impl方法进行初始化。代码如下:

    void InstanceKlass::initialize_impl(instanceKlassHandle this_k, TRAPS) {
      // Make sure klass is linked (verified) before initialization
      // A class could already be verified, since it has been reflected upon.
      this_k->link_class(CHECK);
    
      DTRACE_CLASSINIT_PROBE(required, this_k(), -1);
    
      bool wait = false;
    
      // refer to the JVM book page 47 for description of steps
      // Step 1
      {
        oop init_lock = this_k->init_lock();
        ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
    
        Thread *self = THREAD; // it's passed the current thread
    
        // Step 2
        // If we were to use wait() instead of waitInterruptibly() then
        // we might end up throwing IE from link/symbol resolution sites
        // that aren't expected to throw.  This would wreak havoc.  See 6320309.
        while(this_k->is_being_initialized() && !this_k->is_reentrant_initialization(self)) {
            wait = true;
          ol.waitUninterruptibly(CHECK);
        }
    
        // Step 3
        if (this_k->is_being_initialized() && this_k->is_reentrant_initialization(self)) {
          DTRACE_CLASSINIT_PROBE_WAIT(recursive, this_k(), -1,wait);
          return;
        }
    
        // Step 4
        if (this_k->is_initialized()) {
          DTRACE_CLASSINIT_PROBE_WAIT(concurrent, this_k(), -1,wait);
          return;
        }
    
        // Step 5
        if (this_k->is_in_error_state()) {
          DTRACE_CLASSINIT_PROBE_WAIT(erroneous, this_k(), -1,wait);
          ResourceMark rm(THREAD);
          const char* desc = "Could not initialize class ";
          const char* className = this_k->external_name();
          size_t msglen = strlen(desc) + strlen(className) + 1;
          char* message = NEW_RESOURCE_ARRAY(char, msglen);
          if (NULL == message) {
            // Out of memory: can't create detailed error message
            THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
          } else {
            jio_snprintf(message, msglen, "%s%s", desc, className);
            THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
          }
        }
    
        // Step 6
        this_k->set_init_state(being_initialized);
        this_k->set_init_thread(self);
      }
    
      // Step 7
      // Next, if C is a class rather than an interface, initialize it's super class and super
      // interfaces.
      if (!this_k->is_interface()) {
        Klass* super_klass = this_k->super();
        if (super_klass != NULL && super_klass->should_be_initialized()) {
          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 && this_k->has_nonstatic_concrete_methods()) {
          this_k->initialize_super_interfaces(this_k, THREAD);
        }
    
        // If any exceptions, complete abruptly, throwing the same exception as above.
        if (HAS_PENDING_EXCEPTION) {
          Handle e(THREAD, PENDING_EXCEPTION);
          CLEAR_PENDING_EXCEPTION;
          {
            EXCEPTION_MARK;
            // Locks object, set state, and notify all waiting threads
            this_k->set_initialization_state_and_notify(initialization_error, THREAD);
            CLEAR_PENDING_EXCEPTION;
          }
          DTRACE_CLASSINIT_PROBE_WAIT(super__failed, this_k(), -1,wait);
          THROW_OOP(e());
        }
      }
    
    
      // Look for aot compiled methods for this klass, including class initializer.
      AOTLoader::load_for_klass(this_k, THREAD);
    
      // Step 8
      {
        assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl");
        JavaThread* jt = (JavaThread*)THREAD;
        DTRACE_CLASSINIT_PROBE_WAIT(clinit, this_k(), -1,wait);
        // Timer includes any side effects of class initialization (resolution,
        // etc), but not recursive entry into call_class_initializer().
        PerfClassTraceTime timer(ClassLoader::perf_class_init_time(),
                                 ClassLoader::perf_class_init_selftime(),
                                 ClassLoader::perf_classes_inited(),
                                 jt->get_thread_stat()->perf_recursion_counts_addr(),
                                 jt->get_thread_stat()->perf_timers_addr(),
                                 PerfClassTraceTime::CLASS_CLINIT);
        this_k->call_class_initializer(THREAD);
      }
    
      // Step 9
      if (!HAS_PENDING_EXCEPTION) {
        this_k->set_initialization_state_and_notify(fully_initialized, CHECK);
        { ResourceMark rm(THREAD);
          debug_only(this_k->vtable()->verify(tty, true);)
        }
      }
      else {
        // Step 10 and 11
        Handle e(THREAD, PENDING_EXCEPTION);
        CLEAR_PENDING_EXCEPTION;
        // JVMTI has already reported the pending exception
        // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
        JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
        {
          EXCEPTION_MARK;
          this_k->set_initialization_state_and_notify(initialization_error, THREAD);
          CLEAR_PENDING_EXCEPTION;   // ignore any exception thrown, class initialization error is thrown below
          // JVMTI has already reported the pending exception
          // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
          JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
        }
        DTRACE_CLASSINIT_PROBE_WAIT(error, this_k(), -1,wait);
        if (e->is_a(SystemDictionary::Error_klass())) {
          THROW_OOP(e());
        } else {
          JavaCallArguments args(e);
          THROW_ARG(vmSymbols::java_lang_ExceptionInInitializerError(),
                    vmSymbols::throwable_void_signature(),
                    &args);
        }
      }
      DTRACE_CLASSINIT_PROBE_WAIT(end, this_k(), -1,wait);
    }
    

    整个初始化算法分为以下11个步骤:

    步骤1
         oop init_lock = this_k->init_lock();
        ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
    
        Thread *self = THREAD; // it's passed the current thread
    

    通过ObjectLocker在初始化之前进行加锁,防止多个线程并发初始化。

    步骤2
     // If we were to use wait() instead of waitInterruptibly() then
        // we might end up throwing IE from link/symbol resolution sites
        // that aren't expected to throw.  This would wreak havoc.  See 6320309.
        while(this_k->is_being_initialized() && !this_k->is_reentrant_initialization(self)) {
            wait = true;
          ol.waitUninterruptibly(CHECK);
        }
    

    如果当前instanceKlassHandle正在被初始化,且初始化线程不是当前线程,则等待,并执行ol.waitUninterruptibly(CHECK),等待其他线程初始化完成后通知。

    步骤3
        if (this_k->is_being_initialized() && this_k->is_reentrant_initialization(self)) {
          DTRACE_CLASSINIT_PROBE_WAIT(recursive, this_k(), -1,wait);
          return;
        }
    

    如果当前instanceKlassHandle正在被初始化,且初始化线程为当前线程,则直接return。

    这种场景R大有过举例子:

    如果X类有静态变量指向new Y类实例,Y类中又有静态变量指向new X类实例,这样外部在调用X时需要初始化X类,初始化过程中又要触发Y类的初始化,而Y类初始化又再次触发X类的初始化…..

    步骤4
        if (this_k->is_initialized()) {
          DTRACE_CLASSINIT_PROBE_WAIT(concurrent, this_k(), -1,wait);
          return;
        }
    

    如果已经初始化完成,直接返回。此时为fully_initialized状态

    这里补充类的状态:

    enum ClassState {
        allocated,                          // allocated (but not yet linked)
        loaded,                             // loaded and inserted in class hierarchy (but not linked yet)
        linked,                             // successfully linked/verified (but not initialized yet)
        being_initialized,                  // currently running class initializer
        fully_initialized,                  // initialized (successfull final state)
        initialization_error                // error happened during initialization
      };
    
    步骤5
     if (this_k->is_in_error_state()) {
          DTRACE_CLASSINIT_PROBE_WAIT(erroneous, this_k(), -1,wait);
          ResourceMark rm(THREAD);
          const char* desc = "Could not initialize class ";
          const char* className = this_k->external_name();
          size_t msglen = strlen(desc) + strlen(className) + 1;
          char* message = NEW_RESOURCE_ARRAY(char, msglen);
          if (NULL == message) {
            // Out of memory: can't create detailed error message
            THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
          } else {
            jio_snprintf(message, msglen, "%s%s", desc, className);
            THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
          }
        }
    

    如果初始化失败,组装异常信息并抛出异常。

    步骤6
        this_k->set_init_state(being_initialized);
        this_k->set_init_thread(self);
    

    设置instanceKlassHandle状态为being_initialized,初始化线程为当前线程

    步骤7
     // Next, if C is a class rather than an interface, initialize it's super class and super
      // interfaces.
      if (!this_k->is_interface()) {
        Klass* super_klass = this_k->super();
        if (super_klass != NULL && super_klass->should_be_initialized()) {
          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 && this_k->has_nonstatic_concrete_methods()) {
          this_k->initialize_super_interfaces(this_k, THREAD);
        }
    
        // If any exceptions, complete abruptly, throwing the same exception as above.
        if (HAS_PENDING_EXCEPTION) {
          Handle e(THREAD, PENDING_EXCEPTION);
          CLEAR_PENDING_EXCEPTION;
          {
            EXCEPTION_MARK;
            // Locks object, set state, and notify all waiting threads
            this_k->set_initialization_state_and_notify(initialization_error, THREAD);
            CLEAR_PENDING_EXCEPTION;
          }
          DTRACE_CLASSINIT_PROBE_WAIT(super__failed, this_k(), -1,wait);
          THROW_OOP(e());
        }
      }
    
    
      // Look for aot compiled methods for this klass, including class initializer.
      AOTLoader::load_for_klass(this_k, THREAD);
    

    如果当前InstanceKlassHandle不是接口,并且父类不为空+父类未初始化,初始化其父类

    如果当前instanceKlassHandle实现了接口,且此接口定义了non-static方法,那么初始化当前InstanceKlassHandle的动作会触发父接口的初始化。

    如果初始化失败,则抛出异常。

    步骤8
     {
        assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl");
        JavaThread* jt = (JavaThread*)THREAD;
        DTRACE_CLASSINIT_PROBE_WAIT(clinit, this_k(), -1,wait);
        // Timer includes any side effects of class initialization (resolution,
        // etc), but not recursive entry into call_class_initializer().
        PerfClassTraceTime timer(ClassLoader::perf_class_init_time(),
                                 ClassLoader::perf_class_init_selftime(),
                                 ClassLoader::perf_classes_inited(),
                                 jt->get_thread_stat()->perf_recursion_counts_addr(),
                                 jt->get_thread_stat()->perf_timers_addr(),
                                 PerfClassTraceTime::CLASS_CLINIT);
        this_k->call_class_initializer(THREAD);
      }
    

    这里主要调用了call_class_initializer方法,实现如下:

    void InstanceKlass::call_class_initializer_impl(instanceKlassHandle this_k, TRAPS) {
      if (ReplayCompiles &&
          (ReplaySuppressInitializers == 1 ||
           ReplaySuppressInitializers >= 2 && this_k->class_loader() != NULL)) {
        // Hide the existence of the initializer for the purpose of replaying the compile
        return;
      }
    
      methodHandle h_method(THREAD, this_k->class_initializer());
      assert(!this_k->is_initialized(), "we cannot initialize twice");
      if (log_is_enabled(Info, class, init)) {
        ResourceMark rm;
        outputStream* log = Log(class, init)::info_stream();
        log->print("%d Initializing ", call_class_initializer_impl_counter++);
        this_k->name()->print_value_on(log);
        log->print_cr("%s (" INTPTR_FORMAT ")", h_method() == NULL ? "(no method)" : "", p2i(this_k()));
      }
      if (h_method() != NULL) {
        JavaCallArguments args; // No arguments
        JavaValue result(T_VOID);
        JavaCalls::call(&result, h_method, &args, CHECK); // Static call (no args)
      }
    }
    

    这里最终会执行JavaCalls::call来执行代码块逻辑,再下一层就是具体操作系统的实现了,JavaCalls.cpp中实现如下:

    void JavaCalls::call(JavaValue* result, const methodHandle& method, JavaCallArguments* args, TRAPS) {
      // Check if we need to wrap a potential OS exception handler around thread
      // This is used for e.g. Win32 structured exception handlers
      assert(THREAD->is_Java_thread(), "only JavaThreads can make JavaCalls");
      // Need to wrap each and every time, since there might be native code down the
      // stack that has installed its own exception handlers
      os::os_exception_wrapper(call_helper, result, method, args, THREAD);
    }
    
    步骤9
      if (!HAS_PENDING_EXCEPTION) {
        this_k->set_initialization_state_and_notify(fully_initialized, CHECK);
        { ResourceMark rm(THREAD);
          debug_only(this_k->vtable()->verify(tty, true);)
        }
      }
    

    如果初始化过程没有异常,说明已经初始化完成,设置状态为full_initialized,,并通知其他线程初始化已经完成。

    否则执行步骤10和11

    步骤10&11
    else {
        // Step 10 and 11
        Handle e(THREAD, PENDING_EXCEPTION);
        CLEAR_PENDING_EXCEPTION;
        // JVMTI has already reported the pending exception
        // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
        JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
        {
          EXCEPTION_MARK;
          this_k->set_initialization_state_and_notify(initialization_error, THREAD);
          CLEAR_PENDING_EXCEPTION;   // ignore any exception thrown, class initialization error is thrown below
          // JVMTI has already reported the pending exception
          // JVMTI internal flag reset is needed in order to report ExceptionInInitializerError
          JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
        }
        DTRACE_CLASSINIT_PROBE_WAIT(error, this_k(), -1,wait);
        if (e->is_a(SystemDictionary::Error_klass())) {
          THROW_OOP(e());
        } else {
          JavaCallArguments args(e);
          THROW_ARG(vmSymbols::java_lang_ExceptionInInitializerError(),
                    vmSymbols::throwable_void_signature(),
                    &args);
        }
      }
      DTRACE_CLASSINIT_PROBE_WAIT(end, this_k(), -1,wait);
    }
    

    如果初始化过程发生异常,则通过set_initialization_state_and_notify设置状态为initialization_error且通知其他线程,抛出异常

  3. 如果初始化完成,oop obj = klass->allocate_instance(CHECK); 这行代码会在堆内存中创建一个instanceOopDesc对象,即类的实例化

    instanceOopDesc的类定义如下:

    class instanceOopDesc : public oopDesc {
     public:
      // aligned header size.
      static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; }
    
      // If compressed, the offset of the fields of the instance may not be aligned.
      static int base_offset_in_bytes() {
        // offset computation code breaks if UseCompressedClassPointers
        // only is true
        return (UseCompressedOops && UseCompressedClassPointers) ?
                 klass_gap_offset_in_bytes() :
                 sizeof(instanceOopDesc);
      }
    
      static bool contains_field_offset(int offset, int nonstatic_field_size) {
        int base_in_bytes = base_offset_in_bytes();
        return (offset >= base_in_bytes &&
                (offset-base_in_bytes) < nonstatic_field_size * heapOopSize);
      }
    };
    

    这里指定了头size、类实例占用的内存空间长度、offset内存空间起始值等。

    而instanceOopDesc继承自oopDesc,其定义在上面章节OOP。

    allocate_instance的实现如下:

    instanceOop InstanceKlass::allocate_instance(TRAPS) {
      bool has_finalizer_flag = has_finalizer(); // Query before possible GC
      int size = size_helper();  // Query before forming handle.
    
      KlassHandle h_k(THREAD, this);
    
      instanceOop i;
    
      i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
      if (has_finalizer_flag && !RegisterFinalizersAtInit) {
        i = register_finalizer(i, CHECK_NULL);
      }
      return i;
    }
    
    

    可以看到,这里做了如下操作:

    • has_finalizer 判断是否包含不为空的finalize方法
    • size_helper 确定需要为当前对象分配多大内存
    • CollectedHeap::obj_allocate 在堆中申请指定大小的内存空间,并创建instanceOop对象
    • 如果重新实现了finalize方法,那么需要将生成的instanceOop对象包装成Finalizer对象,并将其添加到Finalizer链表中。
    • 当对象被GC时,如果是Finalizer对象,会将对象赋值到pending对象,Reference Handler线程会将pending对象push到queue中,Finalizer线程poll到对象,会先删除掉Finalizer链表中的对象,然后再执行对象的finalize方法。

    obj_allocate的实现如下:

    oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
      debug_only(check_for_valid_allocation_state());
      assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
      assert(size >= 0, "int won't convert to size_t");
      HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
      post_allocation_setup_obj(klass, obj, size);
      NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
      return (oop)obj;
    }
    

    可以看到,这里创建的对象obj是在Heap中的,通过HeapWord指针记录其信息,然后在返回时转换成instanceOop类型

参考文档:来自战小狼简书

https://www.jianshu.com/p/252e27863822

https://www.jianshu.com/p/0009aaac16ed

上一篇下一篇

猜你喜欢

热点阅读