jvm(3) 类文件的加载

具体描述过程需要参考jvm规范,本文只是追了一下代码
https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-5.html#jvms-5.3

类加载,jvm启动后,对必须要的类进行了初始化,main方法运行加载时需要LauncherHelper
这里主要看了一下系统类加载器加载类过程,class文件读取成vm对象后进行了java class对象的转换,后续java才能使用类的class信息进行初始化

jclass
GetLauncherHelperClass(JNIEnv *env)
{
    if (helperClass == NULL) {
        NULL_CHECK0(helperClass = FindBootStrapClass(env,
                "sun/launcher/LauncherHelper"));
    }
    return helperClass;
}

//find的实现
static FindClassFromBootLoader_t *findBootClass = NULL;

jclass
FindBootStrapClass(JNIEnv *env, const char* classname)
{
   if (findBootClass == NULL) {
       findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,
          "JVM_FindClassFromBootLoader");
       if (findBootClass == NULL) {
           JLI_ReportErrorMessage(DLL_ERROR4,
               "JVM_FindClassFromBootLoader");
           return NULL;
       }
   }
   return findBootClass(env, classname);
}
//连接到jvm.cpp
JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
                                              const char* name))
  // Java libraries should ensure that name is never null or illegal.
  if (name == nullptr || (int)strlen(name) > Symbol::max_length()) {
    // It's impossible to create this class;  the name cannot fit
    // into the constant pool.
    return nullptr;
  }
  assert(UTF8::is_legal_utf8((const unsigned char*)name, (int)strlen(name), false), "illegal UTF name");

  TempNewSymbol h_name = SymbolTable::new_symbol(name);
  Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
  if (k == nullptr) {
    return nullptr;
  }

  if (log_is_enabled(Debug, class, resolve)) {
    trace_class_resolution(k);
  }
  return (jclass) JNIHandles::make_local(THREAD, k->java_mirror());
JVM_END

Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
获取到kclass,方法调用到systemDictionary.cpp

if (loaded_class == nullptr) {
  // Do actual loading 
  loaded_class = load_instance_class(name, class_loader, THREAD);
}
InstanceKlass* SystemDictionary::load_instance_class(Symbol* name,
                                                     Handle class_loader,
                                                     TRAPS) {

  InstanceKlass* loaded_class = load_instance_class_impl(name, class_loader, CHECK_NULL);
  //...
  return loaded_class;
}

load_instance_class_impl中判断是否有classloader,没有的时候用的一个nullptr名字创建一个新的,
有的就是java的classloader,这里应该就是系统的loader

 if (k == nullptr) {
  // Use VM class loader
  //没用cds直接到了ClassLoader.cpp方法里面 ,ClassLoader.c是java对应类的jni,cpp是vm系统启动类
  PerfTraceTime vmtimer(ClassLoader::perf_sys_classload_time());
  k = ClassLoader::load_class(class_name, search_only_bootloader_append, CHECK_NULL);
}

classLoader.cpp中

InstanceKlass* ClassLoader::load_class(Symbol* name, bool search_append_only, TRAPS) {
//...
//上一步搜索是从cds,本次从jimage(就是jar的升级),这里的_jrt_entry在vm初始化 Arguments::init_system_properties()进行的赋值
//jdk9以后的lib包下文件存储方式已经发生变化了,比如rt.jar不见了,jar被Modules优化替代了,用jimage方式加载jar
// Load Attempt #2: [jimage | exploded build]
  if (!search_append_only && (nullptr == stream)) {
    //java runtime 判断是不是实时的jar包加载方式,是的话从实时包里面搜
    if (has_jrt_entry()) {
      e = _jrt_entry;
      stream = _jrt_entry->open_stream(THREAD, file_name);
    } else {
      // Exploded build - attempt to locate class in its defining module's location.
      assert(_exploded_entries != nullptr, "No exploded build entries present");
      stream = search_module_entries(THREAD, _exploded_entries, class_name, file_name);
    }
  }

// Load Attempt #3: [-Xbootclasspath/a]; [jvmti appended entries]
//从classpath的lib找对应的class文件
/**
这里的Xbootclasspath是系统获取额外的jar地址,通过不同的方式比如jvmti事件监听通过sun.instrument.InstrumentationImpl将一个jar包添加进启动包
ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment, true);
里面调用到系统的路径方法get_canonical_path获取到系统的jar在文件系统的位置
*/
if (search_append_only && (nullptr == stream)) {
    // For the boot loader append path search, the starting classpath_index
    // for the appended piece is always 1 to account for either the
    // _jrt_entry or the _exploded_entries.
    assert(classpath_index == 0, "The classpath_index has been incremented incorrectly");
    classpath_index = 1;

    e = first_append_entry();
    while (e != nullptr) {
      stream = e->open_stream(THREAD, file_name);
      if (nullptr != stream) {
        break;
      }
      e = e->next();
      ++classpath_index;
    }
  }
//...
//将class从包里面搜索出来成为stream文件转换成kclass对象
InstanceKlass* result = KlassFactory::create_from_stream(stream,
                                                           name,
                                                           loader_data,
                                                           cl_info,
                                                           CHECK_NULL);
}

上面就是一个搜索解析class名称拿到文件流的过程,主要解析还在classFileParser.cpp里面

//构造方法在通过parse_stream方法解析常量池等,父类也会被关联初始化,主要就是初始化静态的常量表
ClassFileParser parser(stream,
                         name,
                         loader_data,
                         &cl_info,
                         ClassFileParser::BROADCAST, // publicity level
                         CHECK_NULL);
//隐藏类,后面jdk引入的
const ClassInstanceInfo* cl_inst_info = cl_info.class_hidden_info_ptr();
InstanceKlass* result = parser.create_instance_klass(old_stream != stream, *cl_inst_info, CHECK_NULL);

解析出来的InstanceKlass是虚拟机层面的,通过mirror映射出来java的class对象

InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook,
                                                      const ClassInstanceInfo& cl_inst_info,
                                                      TRAPS) {
  if (_klass != nullptr) {
    return _klass;
  }
  //根据解析出来的字段分配对应的metaspace大小
  InstanceKlass* const ik =
    InstanceKlass::allocate_instance_klass(*this, CHECK_NULL);

  if (is_hidden()) {
    mangle_hidden_class_name(ik);
  }
  //元空间分配,创建java mirror
  fill_instance_klass(ik, changed_by_loadhook, cl_inst_info, CHECK_NULL);

  assert(_klass == ik, "invariant");

  return ik;
}

初始化class对象和静态字段信息

void java_lang_Class::allocate_mirror(Klass* k, bool is_scratch, Handle protection_domain, Handle classData,
                                      Handle& mirror, Handle& comp_mirror, TRAPS) {
  // Allocate mirror (java.lang.Class instance)
  //Universe::heap中为java class分配空间地址
  oop mirror_oop = InstanceMirrorKlass::cast(vmClasses::Class_klass())->allocate_instance(k, CHECK);
  //当前也就是主线程的HandleArea保存class句柄,方便资源管理
  mirror = Handle(THREAD, mirror_oop);

  // Setup indirection from mirror->klass
  set_klass(mirror(), k);

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

  set_static_oop_field_count(mirror(), mk->compute_static_oop_field_count(mirror()));

  //...
    //初始化static字段,所以static代码块要比静态字段晚一点执行
    initialize_mirror_fields(k, mirror, protection_domain, classData, THREAD);
   
  //...
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容