类加载时JVM在搞什么?JVM源码分析+OOP-KLASS模型分析


前言

想要彻底学好JVM需要一定C语言基础,要理解头文件、指针、结构体、结构体指针、函数指针、类型别名等概念,本文尽量通俗易懂的介绍JNI,语雀地址:https://www.yuque.com/yangxiaofei-vquku/wmp1zm/dmz2gd

OOP-Klass模型

在撸源码之前首先科普下JVM的OOP-Klass模型:
JVM内部基于oop-klass模型描述一个java类,将一个java类分为两个部分进行描述,其中第一个模型是oop,第二个模型是klass,其中oop用来表示堆中的java对象实例,储存着对象实例的非静态成员变量属性,不包含任何方法;Klass用来表示java类的元数据,包含了java类中声明的方法存在于方法区;oop有klass的引用,如此多个oop实例就不用都保存一份相同的方法信息了。
Klass分类
在这里插入图片描述

OOP分类
在这里插入图片描述

类加载在JVM源码分析

废话不多说先撸一发源码,java中的类加载到jvm中的过程依靠类加载器来完成,Classloader有三个核心方法loadClass()、findClass()、definclass(),简单介绍一下各种作用
loadClass()

  • 查看这个Class是否已经别加载
  • 如果没有被加载,继续往下走,查看父类加载器,递归调用loadClass()
  • 如果父类加载器是null,说明是启动类加载器,查找对应的Class
  • 如果都没有找到,就调用findClass(String)
    findClass()
  • 根据名称或位置加载.class字节码,然后使用defineClass
    definclass()
  • 把字节码转化为Class
    由此可知Classloader加载类是最终起作用的是definclass方法,而definclass最终调用了Native方法definclass1()实现类的加载。
    definclass1()的实现在jdk-jdk8-b120-源码/jdk/src/share/native/java/lang/ClassLoader.c中的Java_java_lang_ClassLoader_defineClass1()函数
/**
 * 类加载native方法入口将字节码转化为Class
 */
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass1(JNIEnv *env,
                                        jobject loader,
                                        jstring name,
                                        jbyteArray data,
                                        jint offset,
                                        jint length,
                                        jobject pd,
                                        jstring source)
{
    jbyte *body;
    char *utfName;
    jclass result = 0;
    char buf[128];
    char* utfSource;
    char sourceBuf[1024];

    if (data == NULL) {
        JNU_ThrowNullPointerException(env, 0);
        return 0;
    }

    /* Work around 4153825. malloc crashes on Solaris when passed a
     * negative size.
     */
    if (length < 0) {
        JNU_ThrowArrayIndexOutOfBoundsException(env, 0);
        return 0;
    }

    body = (jbyte *)malloc(length);

    if (body == 0) {
        JNU_ThrowOutOfMemoryError(env, 0);
        return 0;
    }

    (*env)->GetByteArrayRegion(env, data, offset, length, body);

    if ((*env)->ExceptionOccurred(env))
        goto free_body;

    if (name != NULL) {
        utfName = getUTF(env, name, buf, sizeof(buf));
        if (utfName == NULL) {
            JNU_ThrowOutOfMemoryError(env, NULL);
            goto free_body;
        }
        VerifyFixClassname(utfName);
    } else {
        utfName = NULL;
    }

    if (source != NULL) {
        utfSource = getUTF(env, source, sourceBuf, sizeof(sourceBuf));
        if (utfSource == NULL) {
            JNU_ThrowOutOfMemoryError(env, NULL);
            goto free_utfName;
        }
    } else {
        utfSource = NULL;
    }
    // 核心方法调用jvm.cpp中的静态函数JVM_DefineClassWithSource
    result = JVM_DefineClassWithSource(env, utfName, loader, body, length, pd, utfSource);

    if (utfSource && utfSource != sourceBuf)
        free(utfSource);

 free_utfName:
    if (utfName && utfName != buf)
        free(utfName);

 free_body:
    free(body);
    return result;
}

调用了jdk-jdk8-b120-源码/hotspot/src/share/vm/prims/jvm.cpp中的JVM_DefineClassWithSource()函数

// JVM_DefineClassWithSource函数的定义
JVM_ENTRY(jclass, JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source))
  JVMWrapper2("JVM_DefineClassWithSource %s", name);
    // 核心方法
  return jvm_define_class_common(env, name, loader, buf, len, pd, source, true, THREAD);
JVM_END

调用了jdk-jdk8-b120-源码/hotspot/src/share/vm/prims/jvm.cpp中的jvm_define_class_common()函数

static jclass jvm_define_class_common(JNIEnv *env, const char *name,
                                      jobject loader, const jbyte *buf,
                                      jsize len, jobject pd, const char *source,
                                      jboolean verify, TRAPS) {
  if (source == NULL)  source = "__JVM_DefineClass__";

  assert(THREAD->is_Java_thread(), "must be a JavaThread");
  JavaThread* jt = (JavaThread*) THREAD;

  PerfClassTraceTime vmtimer(ClassLoader::perf_define_appclass_time(),
                             ClassLoader::perf_define_appclass_selftime(),
                             ClassLoader::perf_define_appclasses(),
                             jt->get_thread_stat()->perf_recursion_counts_addr(),
                             jt->get_thread_stat()->perf_timers_addr(),
                             PerfClassTraceTime::DEFINE_CLASS);

  if (UsePerfData) {
    ClassLoader::perf_app_classfile_bytes_read()->inc(len);
  }

  // Since exceptions can be thrown, class initialization can take place
  // if name is NULL no check for class name in .class stream has to be made.
  TempNewSymbol class_name = NULL;
  if (name != NULL) {
    const int str_len = (int)strlen(name);
    if (str_len > Symbol::max_length()) {
      // It's impossible to create this class;  the name cannot fit
      // into the constant pool.
      THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name);
    }
    class_name = SymbolTable::new_symbol(name, str_len, CHECK_NULL);
  }

  ResourceMark rm(THREAD);
  ClassFileStream st((u1*) buf, len, (char *)source);
  Handle class_loader (THREAD, JNIHandles::resolve(loader));
  if (UsePerfData) {
    is_lock_held_by_thread(class_loader,
                           ClassLoader::sync_JVMDefineClassLockFreeCounter(),
                           THREAD);
  }
  Handle protection_domain (THREAD, JNIHandles::resolve(pd));
  // 核心方法调用 SystemDictionary::resolve_from_stream
  Klass* k = SystemDictionary::resolve_from_stream(class_name, class_loader,
                                                     protection_domain, &st,
                                                     verify != 0,
                                                     CHECK_NULL);

  if (TraceClassResolution && k != NULL) {
    trace_class_resolution(k);
  }
    // 为class实例创建本地引用,防止class对象无GCROOT引用被gc掉
  return (jclass) JNIHandles::make_local(env, k->java_mirror());
}

调用JVM/jdk-jdk8-b120-源码/hotspot/src/share/vm/classfile/systemDictionary.cpp的resolve_from_stream函数获取Klass的指针,最后从Klass中获取java_mirror也就是堆里的class实例,java_mirror是Class类在堆中的oop实例。


Klass* SystemDictionary::resolve_from_stream(Symbol* class_name,
                                             Handle class_loader,
                                             Handle protection_domain,
                                             ClassFileStream* st,
                                             bool verify,
                                             TRAPS) {
 //...省略不重要代码

  instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name,
                                                             loader_data,
                                                             protection_domain,
//...省略不重要代码

  return k();
}

调用jdk-jdk8-b120-源码/hotspot/src/share/vm/classfile/classFileParser.cpp中的parseClassFile
函数,重头戏来了,这个方法贼鸡儿长,总结下完成的事情是:加载父类Klass、加载当前Klass、为当前Klass设置属性以及初始化默认属性值。

instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
                                                    ClassLoaderData* loader_data,
                                                    Handle protection_domain,
                                                    KlassHandle host_klass,
                                                    GrowableArray<Handle>* cp_patches,
                                                    TempNewSymbol& parsed_name,
                                                    bool verify,
                                                    TRAPS) {

 	//...省略父类Klass逻辑

    // We can now create the basic Klass* for this klass
    // 获取加载.class文件保存在方法区的元数据instanceKlass
    _klass = InstanceKlass::allocate_instance_klass(loader_data,
                                                    vtable_size,
                                                    itable_size,
                                                    info.static_field_size,
                                                    total_oop_map_size2,
                                                    rt,
                                                    access_flags,
                                                    name,
                                                    super_klass(),
                                                    !host_klass.is_null(),
                                                    CHECK_(nullHandle));
    instanceKlassHandle this_klass (THREAD, _klass);
    //...省略instanceKlass设置静态方法个数、静态变量个数、java镜像(class对象)、版本号...等属性代码
    

    // Allocate mirror and initialize static fields
    // 为klass和class实例互相设置引用,并为静态变量设置默认值以及完成对带final static的属性赋恒定值
    java_lang_Class::create_mirror(this_klass, protection_domain, CHECK_(nullHandle));

	// ...省略不关键代码
    

  return this_klass;
}

下面先看一下JVM/jdk-jdk8-b120-源码/hotspot/src/share/vm/classfile/javaClasses.cpp中的create_mirror函数,为klass和class实例互相设置引用并初始化class的静态字段(在每个Klass中都会有个java_mirror字段用来保存Klass对应的Class实例是个oop模型,Klass不直接暴露给java代码,给到java代码的是java_mirror即Class实例)

oop java_lang_Class::create_mirror(KlassHandle k, Handle protection_domain, TRAPS) {
  assert(k->java_mirror() == NULL, "should only assign mirror once");
  // Use this moment of initialization to cache modifier_flags also,
  // to support Class.getModifiers().  Instance classes recalculate
  // the cached flags after the class file is parsed, but before the
  // class is put into the system dictionary.
  int computed_modifiers = k->compute_modifier_flags(CHECK_0);
  k->set_modifier_flags(computed_modifiers);
  // Class_klass has to be loaded because it is used to allocate
  // the mirror.
  if (SystemDictionary::Class_klass_loaded()) {
    // Allocate mirror (java.lang.Class instance)
    // 获取Class实例oop.
    Handle mirror = InstanceMirrorKlass::cast(SystemDictionary::Class_klass())->allocate_instance(k, CHECK_0);
    // 获取Class类对应的Klass InstanceMirrorKlass
    InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass());
    java_lang_Class::set_static_oop_field_count(mirror(), mk->compute_static_oop_field_count(mirror()));

    // It might also have a component mirror.  This mirror must already exist.
    if (k->oop_is_array()) {
      Handle comp_mirror;
      if (k->oop_is_typeArray()) {
        BasicType type = TypeArrayKlass::cast(k())->element_type();
        comp_mirror = Universe::java_mirror(type);
      } else {
        assert(k->oop_is_objArray(), "Must be");
        Klass* element_klass = ObjArrayKlass::cast(k())->element_klass();
        assert(element_klass != NULL, "Must have an element klass");
          comp_mirror = element_klass->java_mirror();
      }
      assert(comp_mirror.not_null(), "must have a mirror");

        // Two-way link between the array klass and its component mirror:
      ArrayKlass::cast(k())->set_component_mirror(comp_mirror());
      set_array_klass(comp_mirror(), k());
    } else {
      assert(k->oop_is_instance(), "Must be");

      // Allocate a simple java object for a lock.
      // This needs to be a java object because during class initialization
      // it can be held across a java call.
      typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK_NULL);
      set_init_lock(mirror(), r);

      // Set protection domain also
      set_protection_domain(mirror(), protection_domain());

      // Initialize static fields
      // 为final static 属性赋恒定值
      InstanceKlass::cast(k())->do_local_static_fields(&initialize_static_field, CHECK_NULL);
    }
    return mirror();
  } else {
    if (fixup_mirror_list() == NULL) {
      GrowableArray<Klass*>* list =
       new (ResourceObj::C_HEAP, mtClass) GrowableArray<Klass*>(40, true);
      set_fixup_mirror_list(list);
    }
    fixup_mirror_list()->push(k());
    return NULL;
  }
}

接下来看下mirror的函数allocate_instance究竟干了什么,它在jdk-jdk8-b120-源码/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp中(每个类都对应一个Klass,instanceMirrorKlass就是Class类对于的Klass,保存了Class类里的方法信息),由源码可见Class对象是存在于堆中的,是从堆中申请的内存地址,同时为静态属性赋默认值

instanceOop InstanceMirrorKlass::allocate_instance(KlassHandle k, TRAPS) {
  // Query before forming handle.
  int size = instance_size(k);
  KlassHandle h_k(THREAD, this);
  // 在堆中申请内存创建Class实例,这里也证明了class实例是在堆中的。同时为静态属性赋默认值
  instanceOop i = (instanceOop) CollectedHeap::Class_obj_allocate(h_k, size, k, CHECK_NULL);
  return i;
}

接下来看下jdk-jdk8-b120-源码/hotspot/src/share/vm/gc_interface/collectedHeap.cpp中的Class_obj_allocate函数,可以看到Klass和Class互相持有对方的引用,Klass持有Class的引用论证了Person.Class和person.getClass()方法原理;Class持有Klass的引用论证了反射获取方法的原理。

oop CollectedHeap::Class_obj_allocate(KlassHandle klass, int size, KlassHandle real_klass, 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;
    assert(ScavengeRootsInCode > 0, "must be");
    obj = common_mem_allocate_init(real_klass, size, CHECK_NULL);
  post_allocation_setup_common(klass, obj);
  assert(Universe::is_bootstrapping() ||
         !((oop)obj)->is_array(), "must not be an array");
  NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
  oop mirror = (oop)obj;

  java_lang_Class::set_oop_size(mirror, size);

  // Setup indirections
  if (!real_klass.is_null()) {
      //为Class实例设置Klass引用
    java_lang_Class::set_klass(mirror, real_klass());
    // 为Klass设置java_mirror属性指向堆里的Class实例
    real_klass->set_java_mirror(mirror);
  }

  InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass());
  assert(size == mk->instance_size(real_klass), "should have been set");

  // notify jvmti and dtrace
  post_allocation_notify(klass, (oop)obj);

  return mirror;
}

Class在Klass中的引用就是java_mirror在HSDB中可以清楚看到,但是Klass在Class中的引用在HSDB中是看不到的,也就是在Class中没有Klass相关的属性,那又是怎么储存的呢?jdk-jdk8-b120-源码/hotspot/src/share/vm/classfile/javaClasses.cpp中的set_klass()函数

void java_lang_Class::set_klass(oop java_class, Klass* klass) {
  assert(java_lang_Class::is_instance(java_class), "must be a Class object");
  // 在数据区基于偏移量设置klass引用,_klass_offset为相对于数据区的固定偏移量
  java_class->metadata_field_put(_klass_offset, klass);
}

C++并不像Java一样,保存信息时非要在类中定义出相关属性,C++只是在分配内存时为要存储的信息分配好特定的内存,然后直接通过内存偏移来操作即可,所以C++是允许无属性存储引用的,只要记录相对偏移量就可以进行存取。

下面再回到JVM/jdk-jdk8-b120-源码/hotspot/src/share/vm/classfile/javaClasses.cpp中的create_mirror函数中的第二个关键方法do_local_static_fields函数,在创建Class实例后需要为final属性赋恒定值。这个函数的第一个参数是initialize_static_field的函数引用,会被回调执行,核心代码也在initialize_static_field函数中。

static void initialize_static_field(fieldDescriptor* fd, TRAPS) {
  Handle mirror (THREAD, fd->field_holder()->java_mirror());
  assert(mirror.not_null() && fd->is_static(), "just checking");
  	// 是否带final static
    if (fd->has_initial_value()) {
    BasicType t = fd->field_type();
    switch (t) {
      case T_BYTE:
        mirror()->byte_field_put(fd->offset(), fd->int_initial_value());
              break;
      case T_BOOLEAN:
        mirror()->bool_field_put(fd->offset(), fd->int_initial_value());
              break;
      case T_CHAR:
        mirror()->char_field_put(fd->offset(), fd->int_initial_value());
              break;
      case T_SHORT:
        mirror()->short_field_put(fd->offset(), fd->int_initial_value());
              break;
      case T_INT:
        mirror()->int_field_put(fd->offset(), fd->int_initial_value());
        break;
      case T_FLOAT:
        mirror()->float_field_put(fd->offset(), fd->float_initial_value());
        break;
      case T_DOUBLE:
        mirror()->double_field_put(fd->offset(), fd->double_initial_value());
        break;
      case T_LONG:
        mirror()->long_field_put(fd->offset(), fd->long_initial_value());
        break;
      case T_OBJECT:
        {
          #ifdef ASSERT
          TempNewSymbol sym = SymbolTable::new_symbol("Ljava/lang/String;", CHECK);
          assert(fd->signature() == sym, "just checking");
          #endif
          oop string = fd->string_initial_value(CHECK);
          mirror()->obj_field_put(fd->offset(), string);
        }
        break;
      default:
        THROW_MSG(vmSymbols::java_lang_ClassFormatError(),
                  "Illegal ConstantValue attribute in class file");
    }
  }
}

至此类加载的源码分析完毕,总结下在源码分析中我们的收获:

  1. .class文件被jvm加载后会在方法区生产一个instanceKlass用来储存Java类的方法信息、静态方法个数、静态变量个数、版本号、常量池等信息;
  2. 会在堆区产生一个instanceOOP代表Java类对应的java.lang.Class实例,class实例里保存了Java类的静态变量,在创建class实例时会为静态变量设置默认值,之后调用initialize_static_field函数为带final修饰的静态常量赋恒定值(可以在常量池找到恒定值符号引用的常量);
  3. 在Klass和Class互相持有引用,在instanceKlass中有个java_mirror的属性用于储存class的oop引用,在class里基于固定的指针偏移量_klass_offset来储存klass引用;
  4. instanceMirrorKlass用来表时java.lang.Class类的Klass,同时java.lang.Class类在堆中也会有对应oop类型的class实例(Class.class),那这个oop中即保存了Class对象的非静态字段值,又包含了Class类自身也定义的静态字段,故比较特殊。

用HSDB分析JVM的运行时数据

用HSDB分析Person类的运行时数据

public class Person {
    // 静态常量(常量池中不存在恒定值引用)
    private final static String finalStr1= UUID.randomUUID().toString();

    // 静态常量(常量池中存在恒定值引用)
    private final static String finalStr2="final String";

    // String类型静态变量
    private static String staticStr="static String";

    // int类型静态变量
    private static int staticInt=1;

    // String成员变量
    private String a="String";

    // int类型成员变量
    private int b=2;

    /******************get/set方法****************************/
    public static String getFinalStr1() {
        return finalStr1;
    }

    public static String getFinalStr2() {
        return finalStr2;
    }

    public static String getStaticStr() {
        return staticStr;
    }

    public static void setStaticStr(String staticStr) {
        Person.staticStr = staticStr;
    }

    public static int getStaticInt() {
        return staticInt;
    }

    public static void setStaticInt(int staticInt) {
        Person.staticInt = staticInt;
    }

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }
}

/**
 * JVM内存与垃圾回收统篇之类加载子系统
 */
public class Demo1 {


    public static void main(String[] args) {
       Person person1=new Person();
       while (true);
    }

}

Person在堆里的oop实例person1

在这里插入图片描述

Person类在方法区的Klass

在这里插入图片描述

在堆中Person.class oop实例

在这里插入图片描述

由上图可以得到Person类中的静态变量都保存在Klass中的java_mirror属性中,也就是都保存在Person.class实例中,成员变量保存在person1对应的Person oop实例中。
最后总结一波Person在JVM中的堆、栈、方法区的Klass-OOP引用关系:
(上图~)
在这里插入图片描述

final static 初始化问题

结合上面的java代码再从字节码层面论证一下"类加载在JVM源码分析"中的第二个总结:
会在堆区产生一个instanceOOP代表Java类对应的java.lang.Class实例,class实例里保存了Java类的静态变量,在创建class实例时会为静态变量设置默认值,之后调用initialize_static_field函数为带final修饰的静态常量赋恒定值(可以在常量池找到恒定值符号引用的常量)
静态变量存放在堆中的class实例里,这个上面三张图可以证明这一点,这次证明的主要是后半句话,final的静态常量赋值问题。

Person的

在这里插入图片描述

Person的常量池

Constant pool:
   #1 = Methodref          #13.#47        // java/lang/Object."<init>":()V
   #2 = String             #48            // String
   #3 = Fieldref           #6.#49         // jvm/memory/loading/Person.a:Ljava/lang/String;
   #4 = Fieldref           #6.#50         // jvm/memory/loading/Person.b:I
   #5 = Fieldref           #6.#51         // jvm/memory/loading/Person.finalStr1:Ljava/lang/String;
   #6 = Class              #52            // jvm/memory/loading/Person
   #7 = String             #53            // final String
   #8 = Fieldref           #6.#54         // jvm/memory/loading/Person.staticStr:Ljava/lang/String;
   #9 = Fieldref           #6.#55         // jvm/memory/loading/Person.staticInt:I
  #10 = Methodref          #56.#57        // java/util/UUID.randomUUID:()Ljava/util/UUID;
  #11 = Methodref          #56.#58        // java/util/UUID.toString:()Ljava/lang/String;
  #12 = String             #59            // static String
  #13 = Class              #60            // java/lang/Object
  #14 = Utf8               finalStr1
  #15 = Utf8               Ljava/lang/String;
  #16 = Utf8               finalStr2
  #17 = Utf8               ConstantValue
  #18 = Utf8               staticStr
  #19 = Utf8               staticInt
  #20 = Utf8               I
  #21 = Utf8               a
  #22 = Utf8               b
  #23 = Utf8               <init>
  #24 = Utf8               ()V
  #25 = Utf8               Code
  #26 = Utf8               LineNumberTable
  #27 = Utf8               LocalVariableTable
  #28 = Utf8               this
  #29 = Utf8               Ljvm/memory/loading/Person;
  #30 = Utf8               getFinalStr1
  #31 = Utf8               ()Ljava/lang/String;
  #32 = Utf8               getFinalStr2
  #33 = Utf8               getStaticStr
  #34 = Utf8               setStaticStr
  #35 = Utf8               (Ljava/lang/String;)V
  #36 = Utf8               getStaticInt
  #37 = Utf8               ()I
  #38 = Utf8               setStaticInt
  #39 = Utf8               (I)V
  #40 = Utf8               getA
  #41 = Utf8               setA
  #42 = Utf8               getB
  #43 = Utf8               setB
  #44 = Utf8               <clinit>
  #45 = Utf8               SourceFile
  #46 = Utf8               Person.java
  #47 = NameAndType        #23:#24        // "<init>":()V
  #48 = Utf8               String
  #49 = NameAndType        #21:#15        // a:Ljava/lang/String;
  #50 = NameAndType        #22:#20        // b:I
  #51 = NameAndType        #14:#15        // finalStr1:Ljava/lang/String;
  #52 = Utf8               jvm/memory/loading/Person
  #53 = Utf8               final String
  #54 = NameAndType        #18:#15        // staticStr:Ljava/lang/String;
  #55 = NameAndType        #19:#20        // staticInt:I
  #56 = Class              #61            // java/util/UUID
  #57 = NameAndType        #62:#63        // randomUUID:()Ljava/util/UUID;
  #58 = NameAndType        #64:#31        // toString:()Ljava/lang/String;
  #59 = Utf8               static String
  #60 = Utf8               java/lang/Object
  #61 = Utf8               java/util/UUID
  #62 = Utf8               randomUUID
  #63 = Utf8               ()Ljava/util/UUID;
  #64 = Utf8               toString

在Person中一共有四个静态变量,finalStr1,finalStr2,staticStr,staticInt其中前两个被final修饰。
在Person的类初始化方法的字节码中发现没有finalStr2相关的初始化指令,原因是在执行之前finalStr2已经进行了赋值操作,因为finalStr2是显式赋值且赋值过程中不涉及方法调用,其显式赋的值"final String"在编译时期就已经确认,被存放在常量池#53的位置,然后在创建class实例后通过initialize_static_field函数进行赋值。

总结一波一个类的各种成员变量的初始化顺序:

  1. 被static修饰的全部变量赋默认值,在创建Class实例时;
  2. 被final static修饰的静态常量**(可以在常量池找到恒定值符号引用的常量)**在创建Class实例后,调用initialize_static_field进行赋值;
  3. 在Person类初始化时调用方法进行赋值
  4. 在创建类Person类实例时调用构造方法对非静态成员变量进行赋值。

参考:https://blog.51cto.com/u_10912795/2775854 类加载阶段之准备阶段

博主语雀地址:https://www.yuque.com/yangxiaofei-vquku/wmp1zm/mel3x8

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

躺平程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值